Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Verify static_assert in a unit test

I would like to make sure that a static_assert works as it should in a unit test. That is, if I have a

class MyClass {static_assert(my_type_trait<T>::value, "error"); };

then in the unit test MyClass<TypeWithTrait> myClass; should 'pass' and MyClass<TypeWithoutTrait> myClass; should 'fail'.

Is it possible to do something like this?

like image 325
David Doria Avatar asked Feb 08 '16 20:02

David Doria


2 Answers

If you want to check that something fails to compile, you'll have to test that external to the code. Just write a simple file like:

#include "MyClass.h"

int main() { 
    MyClass<%T%> m;
}

And write a unit test that compiles that file with different values of %T%. Verify that the compilation either succeeds as expected, or fails with something about static_assert in the failure text as expected.

like image 150
Barry Avatar answered Oct 21 '22 04:10

Barry


Barry's suggestion is one possibility, but if you have many things you want to test, you need to create many small files. What's more, those files could fail to compile for other reasons than what you expect, giving you a false sense your test passed.

An alternative is that instead of using static_assert, you use some kind of SFINAE to detect whether or not something works. For traits classes this is a bit tricky, but you can do this:

template <class T>
using void_t = void;

template <class T>
struct foo;

template <>
struct foo <double> {};

template <class T, class = void>
struct has_foo_trait : std::false_type {};

template <class T>
struct has_foo_trait<T, void_t<decltype(foo<T>{})>> : std::true_type {};

int main(int, char**) {
  std::cerr << has_foo_trait<int>::value;
  std::cerr << has_foo_trait<double>::value;
  return 0;
}

This prints out 01. So now, instead of getting a hard failure from static_asserting directly, you can compute the value of the trait presence at compile time, and then static_assert that you get the value you expect.

Note that the reason that traits classes are tricky is because the trait is declared as a general template, just not defined. So doing the "usual" metaprogramming thing of using the type directly inside the void_t does not work. In order to trigger a soft SFINAE error in the true branch of has_foo_trait, I actually had to default construct an instance of the traits class. If you write your traits classes so they're not default constructible, this won't work. But in general you wouldn't write them that way. Would be curious to see if there's a better way to do it

like image 38
Nir Friedman Avatar answered Oct 21 '22 04:10

Nir Friedman