Search code examples
c++templatesc++14c++17static-assert

How to make the `static_assert` print the value it got on failure?


Running this:

#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__)

template< int PathLength >
constexpr const int startfindlastslash(const char (&path)[PathLength]) {
    return PathLength;
}

int main(int argc, char const *argv[])
{
    STATIC_ASSERT( startfindlastslash( "cppdebugger/test_debugger.cpp" ) == 11 );
}

You got:

  1. g++ -o main.exe --std=c++14 test_debugger.cpp

    test_debugger.cpp: In function ‘int main(int, const char**)’:
    test_debugger.cpp:1:28: error: static assertion failed: startfindlastslash( "cppdebugger/test_debugger.cpp" ) == 11
     #define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__)
                                ^
    test_debugger.cpp:10:5: note: in expansion of macro ‘STATIC_ASSERT’
         STATIC_ASSERT( startfindlastslash( "cppdebugger/test_debugger.cpp" ) == 11 );
         ^~~~~~~~~~~~~
    
  2. clang++ -Xclang -ast-print -fsyntax-only --std=c++14 test_debugger.cpp > main.exe

    test_debugger.cpp:10:5: error: static_assert failed due to requirement 'startfindlastslash("cppdebugger/test_debugger.cpp") == 11' "startfindlastslash(
          \"cppdebugger/test_debugger.cpp\" ) == 11"
        STATIC_ASSERT( startfindlastslash( "cppdebugger/test_debugger.cpp" ) == 11 );
        ^              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    test_debugger.cpp:1:28: note: expanded from macro 'STATIC_ASSERT'
    #define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__)
                               ^             ~~~~~~~~~~~
    1 error generated.
    

The compiler does not tell which one was the value it was supposed to get, it only says the value is not equal.


Solution

  • I often find it useful to wrap static_asserts in a function template. Then the compiler will print at least the function template parameters in the error message. If you combine this strategy with the value template approach of @Phil1970, you get pretty decent results.

    When compiled with GCC, the following snippet prints the error message containing your values: In instantiation of 'constexpr void assert_equal(const_val<T, A>, const_val<U, B>) [with T = int; T A = 30; U = int; U B = 11]'

    Clang is even better: <source>:13:5: error: static_assert failed due to requirement '30 == 11' "Values are not equal!".

    #define MAKE_CONST(x) const_val<decltype(x), x>{}
    #define STATIC_ASSERT_EQUAL(x, y) assert_equal(MAKE_CONST(x), MAKE_CONST(y));
    
    template<typename T, T val>
    struct const_val {
        constexpr const_val() = default;
        constexpr const_val(T v) {}
    };
    
    template<typename T, T A, typename U, U B>
    constexpr void assert_equal(const_val<T, A>, const_val<U, B>) {
        static_assert(A == B, "Values are not equal!");
    }
    
    template< int PathLength >
    constexpr const int startfindlastslash(const char (&path)[PathLength]) {
        return PathLength;
    }
    
    int main(int argc, char const *argv[])
    {
      STATIC_ASSERT_EQUAL(startfindlastslash( "cppdebugger/test_debugger.cpp" ), 11);
    }
    

    It's not exactly the syntax you were using and it involves an additional macro for convenience, but hopefully it's sufficient for your purpose...

    Live code here.