admin管理员组文章数量:1356957
I am trying to write a parser. I have the parser class below, but I am struggling to write a generalized ParseMany
function with templates. Currently, to parse, I use parser.Parse<TypeToParse>()
, which returns an optional with {}
for a parsing error or TypeToParse
if successful. I was wondering if it was possible to generalize the ParseMany
function for a variable number of types. I am using visual studio 2022, C++ 20, and my project compiles and runs without errors.
class Parser {
Lexer& lexer;
public:
template<typename ReturnType>
std::optional<ReturnType> Parse();
Parser(Lexer& lexer);
template<typename ReturnType, typename T1>
inline std::optional<ReturnType> ParseMany() {
std::optional<T1> maybe_t1 = Parse<T1>();
if (!maybe_t1.has_value()) return {};
return ReturnType{ std::forward<T1>(maybe_t1.value()) };
}
template<typename ReturnType, typename T1, typename T2>
inline std::optional<ReturnType> ParseMany() {
std::optional<T1> maybe_t1 = Parse<T1>();
if (!maybe_t1.has_value()) return {};
std::optional<T2> maybe_t2 = Parse<T2>();
if (!maybe_t2.has_value()) return {};
return ReturnType{ std::forward<T1>(maybe_t1.value()), std::forward<T2>(maybe_t2.value()) };
}
template<typename ReturnType, typename T1, typename T2, typename T3>
inline std::optional<ReturnType> ParseMany() {
std::optional<T1> maybe_t1 = Parse<T1>();
if (!maybe_t1.has_value()) return {};
std::optional<T2> maybe_t2 = Parse<T2>();
if (!maybe_t2.has_value()) return {};
std::optional<T3> maybe_t3 = Parse<T3>();
if (!maybe_t3.has_value()) return {};
return ReturnType{ std::forward<T1>(maybe_t1.value()), std::forward<T2>(maybe_t2.value()), std::forward<T3>(maybe_t3.value()) };
}
};
I am trying to write a parser. I have the parser class below, but I am struggling to write a generalized ParseMany
function with templates. Currently, to parse, I use parser.Parse<TypeToParse>()
, which returns an optional with {}
for a parsing error or TypeToParse
if successful. I was wondering if it was possible to generalize the ParseMany
function for a variable number of types. I am using visual studio 2022, C++ 20, and my project compiles and runs without errors.
class Parser {
Lexer& lexer;
public:
template<typename ReturnType>
std::optional<ReturnType> Parse();
Parser(Lexer& lexer);
template<typename ReturnType, typename T1>
inline std::optional<ReturnType> ParseMany() {
std::optional<T1> maybe_t1 = Parse<T1>();
if (!maybe_t1.has_value()) return {};
return ReturnType{ std::forward<T1>(maybe_t1.value()) };
}
template<typename ReturnType, typename T1, typename T2>
inline std::optional<ReturnType> ParseMany() {
std::optional<T1> maybe_t1 = Parse<T1>();
if (!maybe_t1.has_value()) return {};
std::optional<T2> maybe_t2 = Parse<T2>();
if (!maybe_t2.has_value()) return {};
return ReturnType{ std::forward<T1>(maybe_t1.value()), std::forward<T2>(maybe_t2.value()) };
}
template<typename ReturnType, typename T1, typename T2, typename T3>
inline std::optional<ReturnType> ParseMany() {
std::optional<T1> maybe_t1 = Parse<T1>();
if (!maybe_t1.has_value()) return {};
std::optional<T2> maybe_t2 = Parse<T2>();
if (!maybe_t2.has_value()) return {};
std::optional<T3> maybe_t3 = Parse<T3>();
if (!maybe_t3.has_value()) return {};
return ReturnType{ std::forward<T1>(maybe_t1.value()), std::forward<T2>(maybe_t2.value()), std::forward<T3>(maybe_t3.value()) };
}
};
Share
Improve this question
asked Mar 27 at 22:55
girobuzgirobuz
1757 bronze badges
1
- I assume the T1, T2 etc are not guaranteed to be distinct types, correct? – Chronial Commented Mar 27 at 23:52
2 Answers
Reset to default 3Here's a basic version that works with arbitrary types in Ts
:
template<typename ReturnType, typename... Ts>
std::optional<ReturnType> ParseMany() {
auto maybe_tuple = ParseManyHelper<Ts...>();
if (!maybe_tuple.has_value()) return std::nullopt;
return std::make_from_tuple<ReturnType>(std::move(maybe_tuple.value()));
}
template<typename T, typename... Rest>
std::optional<std::tuple<T, Rest...>> ParseManyHelper() {
std::optional<T> maybe_t = Parse<T>();
if (!maybe_t.has_value()) return std::nullopt;
if constexpr (sizeof...(Rest) == 0) {
return std::tuple<T>(std::move(maybe_t.value()));
} else {
auto maybe_rest = ParseManyHelper<Rest...>();
if (!maybe_rest.has_value()) return std::nullopt;
return std::tuple_cat(
std::tuple<T>(std::move(maybe_t.value())),
std::move(maybe_rest.value()));
}
}
You can alternatively avoid the recursion by using fold expressions and std::index_sequence
:
template<typename ReturnType, typename... Ts>
std::optional<ReturnType> ParseMany3() {
return ParseManyHelper<ReturnType, Ts...>(
std::index_sequence_for<Ts...>());
}
template<typename ReturnType, typename... Ts, std::size_t... Is>
std::optional<ReturnType> ParseManyHelper(std::index_sequence<Is...>) {
std::tuple<std::optional<Ts>...> maybe_values;
bool all_present = ([&] {
std::get<Is>(maybe_values) =
Parse<typename std::tuple_element_t<Is, std::tuple<Ts...>>>();
return std::get<Is>(maybe_values).has_value();
}() && ...);
if (!all_present) return std::nullopt;
return ReturnType(std::move(std::get<Is>(maybe_values).value()) ...);
}
Here is a version using std::apply
to deal with the types in the pack.
class Parser {
Lexer& lexer;
public:
template<typename ReturnType>
std::optional<ReturnType> Parse();
Parser(Lexer& lexer);
template<typename ReturnType, typename... T>
inline std::optional<ReturnType> ParseMany() {
auto tup = std::make_tuple(Parse<T>()...);
return std::apply([](auto&... t) -> std::optional<ReturnType> {
if ((t.has_value() && ...)) {
return ReturnType{std::forward<T>(t.value())...};
}
return {};
}, tup);
}
};
本文标签: cHow to generalize this function with a variadic templateStack Overflow
版权声明:本文标题:c++ - How to generalize this function with a variadic template - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744064442a2584725.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论