3

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?

Charles Ofria
  • 1,936
  • 12
  • 24

2 Answers2

11

You need to rewrite your sequence of if constexprs 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 {};
Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • Could you please explain why `dependent_false` is usefull? Why not just `static_assert(false, "invalid template type for As()");`? – Adam Oct 08 '21 at 22:42
  • 4
    @Adam Template definition is not allowed to include source that is ill-formed regardless of the template parameter - even if the branch may be discarded. With `dependent_false` the compiler cannot prove that the assert will fail since there may be a specialization of `dependent_false` that defines `value = true`. – Eugene Oct 08 '21 at 23:33
  • In place, and worked perfectly! There's definitely a good bit of intuition I still need to develop surrounding templates and constexpr, but this technique makes perfect sense now. – Charles Ofria Oct 09 '21 at 16:26
1

static assert a templated type that is always false

if constexpr (std::is_same_v<T,int>) {
    return AsInt();
} ...else if constexpr (std::is_same_v<T,double>) {
    return AsDouble(); ... 
} else {
    // this is still constexpr, so it will be discarded if any other branch was taken
    static_assert(!std::is_same<T,T>::value, "invalid template type for As()");
}

Similar to brian bi's answer but you don't have to define a new type.

Max Gómez
  • 39
  • 5