I have a long series of if constexpr
statements and would like to trigger a compile-time error if none of them succeed.
Specifically, I have an abstract syntax tree whose result I would like to convert to a specific set of types that I might need. I have AsInt(), AsDouble(), etc. working, but I need to be able to do so more dynamically based on a supplied type.
As things stand, I've written a templated As() member function, but it's error-checking is clunky. Specifically, using static assert requires an unwieldy test condition. Here's a simplified version:
template <typename T>
T As() {
if constexpr (std::is_same_v<T,int>) return AsInt();
if constexpr (std::is_same_v<T,double>) return AsDouble();
...
if constexpr (std::is_same_v<T,std::string>) return AsString();
static_assert( std::is_same_v<T,int>
|| std::is_same_v<T,double>
|| ...
|| std::is_same_v<T,std::string>,
"Invalid template type for As()" );
}
Is there a simpler way to trigger the static assert (or equivalent) if all of the conditions fail?
You need to rewrite your sequence of if constexpr
s as a chain of if constexpr ... else if constexpr ...
and have the final else
clause trigger a compilation error if "reached" (i.e., not discarded). This can be done using the "dependent false
idiom":
if constexpr (std::is_same_v<T,int>) {
return AsInt();
} else if constexpr (std::is_same_v<T,double>) {
return AsDouble();
} ... else if constexpr (std::is_same_v<T,std::string>) {
return AsString();
} else {
// this is still constexpr, so it will be discarded if any other branch was taken
static_assert(dependent_false<T>::value, "invalid template type for As()");
}
where dependent_false
is defined as:
template <class T> struct dependent_false : std::false_type {};