The problem is that in
consteval decltype(auto) pattern_munch(auto t, std::string_view remaining) {
if (remaining.empty()) {
return t;
}
switch (remaining.front()) {
case 'f':
return pattern_munch(tuple_push(t, 1.f), remaining.substr(1));
case 'i':
return pattern_munch(tuple_push(t, 1), remaining.substr(1));
default:
throw "unhandled";
}
}
the compiler compile all three return case, also when the remaining string is empty.
You could solve the problem with if constexpr, but remaining isn't a template argument.
What about using a template argument instead of remaining, so you can use if constexpr and cut the compilation of unused functions?
If you define a Foo class as follows
template <std::size_t Dim>
struct Foo
{
std::array<char, Dim-1u> data;
template <std::size_t ... Is>
constexpr Foo (const char (&init)[Dim], std::index_sequence<Is...>)
: data{init[Is]...}
{ }
constexpr Foo (const char (&init)[Dim])
: Foo{init, std::make_index_sequence<Dim-1u>{}}
{ }
constexpr std::size_t size () const
{ return data.size(); }
constexpr char get_char (std::size_t ind) const
{ return data.at(ind); }
};
and a _foo literal that uses Foo
template <Foo str>
constexpr auto operator"" _foo()
{ return str; }
You can write your pattern_munch() and compile_pattern() as follows (EDIT: but see also the Jarod42's solution to see how avoid the recursion)
template <Foo Val, std::size_t Ind>
constexpr auto pattern_munch (auto t) {
if constexpr ( Ind == Val.size() )
return t;
else if constexpr ( Val.get_char(Ind) == 'f' )
return pattern_munch<Val, Ind+1u>(tuple_push(t, 1.f));
else if constexpr ( Val.get_char(Ind) == 'i' )
return pattern_munch<Val, Ind+1u>(tuple_push(t, 1));
else
throw "unhandled";
}
template <Foo val>
constexpr auto compile_pattern () {
return pattern_munch<val, 0u>(std::make_tuple());
}
and you can invoke compile_pattern() this way
constexpr auto result = compile_pattern<"fif"_foo>();
The following is a full C++20 compiling example
#include <array>
#include <tuple>
#include <iostream>
#include <algorithm>
template <std::size_t Dim>
struct Foo
{
std::array<char, Dim-1u> data;
template <std::size_t ... Is>
constexpr Foo (const char (&init)[Dim], std::index_sequence<Is...>)
: data{init[Is]...}
{ }
constexpr Foo (const char (&init)[Dim])
: Foo{init, std::make_index_sequence<Dim-1u>{}}
{ }
constexpr std::size_t size () const
{ return data.size(); }
constexpr char get_char (std::size_t ind) const
{ return data.at(ind); }
};
template <Foo str>
constexpr auto operator"" _foo()
{ return str; }
constexpr auto tuple_push(auto t, auto e) {
return std::tuple_cat(t, std::make_tuple(e));
}
template <Foo Val, std::size_t Ind>
constexpr auto pattern_munch (auto t) {
if constexpr ( Ind == Val.size() )
return t;
else if constexpr ( Val.get_char(Ind) == 'f' )
return pattern_munch<Val, Ind+1u>(tuple_push(t, 1.f));
else if constexpr ( Val.get_char(Ind) == 'i' )
return pattern_munch<Val, Ind+1u>(tuple_push(t, 1));
else
throw "unhandled";
}
template <Foo val>
constexpr auto compile_pattern () {
return pattern_munch<val, 0u>(std::make_tuple());
}
int main() {
constexpr auto result = compile_pattern<"fif"_foo>();
static_assert(std::is_same_v<decltype(result),
std::tuple<float, int, float> const>);
}
using fif =MyClass<float, int, float>;` could achieve the same.constevalfunction...tand the return type may not be the same". Withdecltype(auto)(/auto), the type is deduced from the first return with isreturn t;, so the same. you would wantif constexpr, but it is not possible as-is.overloadswould then even be a better mapping Demo