Charles Ofria
Charles Ofria

Reputation: 2006

Best way to trigger a compile-time error if no if-constexpr's succeed?

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?

Upvotes: 5

Views: 1403

Answers (2)

Max G&#243;mez
Max G&#243;mez

Reputation: 69

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.

Upvotes: 3

Brian Bi
Brian Bi

Reputation: 119382

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 {};

Upvotes: 12

Related Questions