35

Is there a way I can assert that two constant expressions are equal at compile time?

e.g. I want this to cause a compile-time error

enum { foo=263, bar=264 };
SOME_EXPRESSION(foo,bar)

but I want this to not cause an error

enum { foo=263, bar=263 };
SOME_EXPRESSION(foo,bar)

edit: the above was simplified. My situation is more like

some_other_file_I_dont_control.h:

class X
{
public:
   enum { foo=263 };
}

my_file.h:

enum { bar=something+somethingelse }; // bar should equal X::foo
SOME_EXPRESSION(X::foo, bar)
Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
Jason S
  • 184,598
  • 164
  • 608
  • 970

12 Answers12

38

Yes. You can do this with template specializations on type bool, like this:

// empty default template
template <bool b>
struct StaticAssert {};

// template specialized on true
template <>
struct StaticAssert<true>
{
    static void assert() {}
};

int f()
{
    StaticAssert<1==1>::assert();   // compiles fine, assert() member found
    StaticAssert<1==2>::assert();   // compile failure, no assert() member for StaticAssert<false>
}

Code is basically from memory, may need some tweaks.

Chad
  • 18,706
  • 4
  • 46
  • 63
  • 3
    You can also spare the `assert()` member and just use the default constructor: `StaticAssert<1==1>();` – Julien-L Mar 03 '13 at 20:41
  • since the template takes a `bool` why can't you pass any bool that is programmatically created at runtime? I am trying this novel code of yours and passing in a bool I update at runtime, but compiler fails saying `non-type template argument is not a constant expression` – johnbakers May 12 '13 at 05:58
  • 2
    Templates evaluated (and generated) at compile time, and therefore must use values that are known by the compiler at compile time (a `constexpr`) – Chad May 12 '13 at 14:16
  • 2
    This is the implementation and the one I was looking for, thanks. Very clean. Here is another reference: http://www.drdobbs.com/compile-time-assertions/184401873 and Alexandrescu's Modern C++ Design book chapter 2.1. – KlingonJoe Oct 07 '13 at 12:32
36

See static_assert (C++0x only); if on an older version, see Boost's StaticAssert.

user541686
  • 205,094
  • 128
  • 528
  • 886
  • 2
    And I don't have Boost either. This is an embedded system that doesn't have such luxuries. I'm lucky I have C++ and not just plain C. – Jason S Jul 20 '11 at 17:19
  • 8
    @Jason: It doesn't matter that it's an embedded system (unless your *compiler* is running on an embedded system... I hope not) -- the assertion is static, so it's obviously not compiled into the final code. :-) If you'd like, extract this part from the Boost library. – user541686 Jul 20 '11 at 17:20
  • Depending on the OP's use, `static_assert` may not work if the comparisons is between the contents of variables. – johnbakers May 12 '13 at 05:48
24

For another version of a static assert, that you can glorify by adding a better name, you can use:

// name must be a valid identifier
#define STATIC_ASSERT( condition, name )\
    typedef char assert_failed_ ## name [ (condition) ? 1 : -1 ];

And use as:

STATIC_ASSERT( x == y, constants_must_be_same );

The compiler will trigger an error similar to:

size of array 'assert_failed_constants_must_be_same' is negative

Which does not seem that helpful, but it will point to the exact line of the assert, and after a while you will start processing that error message as static assert failed

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • The fact that negative sized arrays are forbidden is much simpler to keep in mind than the template version. If you must such tricks at all on a pre C++11 compiler. – Wolf Sep 14 '17 at 10:20
  • 2
    If you use -Werror=unused-local-typedefs then using this STATIC_ASSERT will be a problem. The template solution seems like a better option. – blue_whale Mar 14 '18 at 08:29
6

Another possibility for Windows is C_ASSERT, which is defined if Windows.h is included.

  • 2
    I don't want to be tied to Windows -- but the link you posted shows how it's implemented, and it's really simple, so thanks! – Jason S Jan 04 '13 at 17:09
6

There is also the trick to use a switch (..) statement. Kind of old style though. The case entry foo == bar has to be compile time evaluated and if it happens to be false the switch statement will cause an error. The compiler will also reduce it to "nothing".

{ 
  bool x=false; 
  switch (x) {
  case foo == bar:
    break;
  case false:
    // Compile time test that foo == bar
    break;
}
epatel
  • 45,805
  • 17
  • 110
  • 144
2

you can define your own static assertion , this way :

#include <iostream>
template <bool b> class ClassStaticAssert;
template <>
class ClassStaticAssert<true>{static const bool value = true;};
#define STATIC_ASSERT(e) (ClassStaticAssert<e>())
int main()
{
    STATIC_ASSERT(0);
    return 0;
}
Dhia Hassen
  • 508
  • 4
  • 20
1

Similar to iammillind's solution, which was unfortunately one only useful at runtime:

template <int A, int B>
class VALUES { 
};

// specialization to provide safe passage for equal values        
template <int X>
class VALUES<X, X> {
public:
   static void MY_VALUES_ARE_EQUAL() {}
};


#define ASSERT_EQUALITY(a, b)    \
{    \
   typedef VALUES<a, b> COMPILE_TIME_ASSERTION;       \
   COMPILE_TIME_ASSERTION::VALUES_ARE_EQUAL();     \
}

int main() {
   ASSERT_EQUALITY(1, 1);    // compiles just fine
   ASSERT_EQUALITY(1, 2);    // ERROR!
   // . . . 
 }

The nice thing about this is that it provides a nice compiler message. My compiler tells me the following:

‘VALUES_ARE_EQUAL’ is not a member of ‘COMPILE_TIME_ASSERTION {aka VALUES<1, 2>}’

You don't need the typedef. Without:

'VALUES_ARE_EQUAL' is not a member of 'VALUES<1, 2>'

Of course, there are a bunch of other ways to generate helpful messages. For giggles:

// these give use some tips in the compiler warnings
class COMPILE_TIME_EQUALITY_ASSERTION {} compiler_message; 
class EQUAL_VALUES_ONLY_PLEASE {};


template <int A, int B>
class VALUES {
public:
   static void AreEqual(EQUAL_VALUES_ONLY_PLEASE) {}
};

template <int X>
class VALUES<X, X>
{
public:
   static void AreEqual(COMPILE_TIME_EQUALITY_ASSERTION) {}
};


#define ASSERT_EQUALITY(a, b)                                   \
{                                                               \
   VALUES<a, b>::AreEqual(compiler_message);                             \
}

int main() {
    ASSERT_EQUALITY(1, 1) // a-okay
    ASSERT_EQUALITY(1, 2) // ERROR!
}

I get the following compiler errors:

no matching function for call to:
‘VALUES<1,2>::AreEqual(COMPILE_TIME_EQUALITY_ASSERTION&)' 
candidate is:
static void VALUES<\A, B>::AreEqual(EQUAL_VALUES_ONLY_PLEASE) [with int A = 1, int B = 2]

combinations of static member functions/constructors/field assignment/privacy and template specifications can yield different results perhaps more appropriate for your situation.

Dodgie
  • 643
  • 1
  • 10
  • 17
1
template <int a, int b>
inline void static_assert_equal()
{
    typedef char enum_values_must_be_equal[a == b ? 1 : -1];
    (void) sizeof(enum_values_must_be_equal);
}

int main()
{
    enum { foo = 1, bar = 2, fum = foo };
    static_assert_equal<foo, fum>(); // compiles ok
    static_assert_equal<foo, bar>(); // fails at compile time
    return 0;
}

This derives from the checked_delete idiom.

Andreas Spindler
  • 7,568
  • 4
  • 43
  • 34
  • ummmm... and how does this answer my question? ("Is there a way I can assert that two constant expressions are equal at compile time?") – Jason S Jul 18 '15 at 14:50
  • You are right. I edited the answer by replacing `checked_delete` with a more specific function. The idea, however, remains the same. And... you can easily change the test expression `a == b` to something else, e.g. to test if three enums are all equal (`a == b && b == c`) or test if enums are in a range (`(a - b) < N`) etc. – Andreas Spindler Jul 18 '15 at 18:42
0

I suggest to take a look at the Eigen library static assert mechanism:

http://eigen.tuxfamily.org/dox/StaticAssert_8h_source.html

linello
  • 8,451
  • 18
  • 63
  • 109
0

You can do some preprocessor magic like

#define FOO_VALUE 263
#define BAR_VALUE 264

enum {foo=FOO_VALUE, bar=BAR_VALUE}

#if !(FOO_VALUE == BAR_VALUE)
#error "Not equal"
#endif
marc
  • 6,103
  • 1
  • 28
  • 33
  • In my particular case I can't rely on that -- one of the enums is coming from code beyond my control. – Jason S Jul 20 '11 at 17:16
  • 1
    So depend on just the `#if`, `#error`, and `#endif`. This gives you exactly what you asked for, which is a compile time test of whether or not `X::foo` and `bar` are equal to one another. The nice the about `#error` is that **you** control the error message. The not so nice thing is that some C preprocessors are not ANSI-compliant, particular so with preprocessors for ancient, embedded systems. – David Hammen Jul 20 '11 at 17:31
  • 2
    I'd love to, but preprocessors only have access to #define-d constants, not enums. – Jason S Jul 20 '11 at 17:40
0

I would go for one of the available static_asserts.

  • boost::static_assert
  • C++0x static_assert

But just because I have never tried before I wrote this:

enum { foo=263, bar=264 };

template<bool test>
struct CompileAssert
{
    bool assert() {}
};

template<>
struct CompileAssert<false>  {}; // fail on false.

int main()
{
    CompileAssert<foo != bar>().assert();  // Now I have seen Chad above I like his static 
    CompileAssert<foo == bar>().assert();  // method better than using a normal method.
}                                          // But I tried zero length arrays first did 
                                           // not seem to work
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • If `#error` is available it is much more compact and the error message can be made to be very obvious. `boost::static_assert` is a bit bloated and the error messages are bloated++. – David Hammen Jul 20 '11 at 17:34
  • @David Hammen #error is a preprocessor directive and not available during the compile phase. – Captain Obvlious Jul 20 '11 at 17:43
-1
template<int X, int Y>
struct Check
{
  enum { value = false };
};

template<int X>
struct Check<X,X>
{
  enum { value = true };
};

I have taken the example of int. You can change it according to your need. Here is the demo. Usage:

Check<foo, bar>::value
iammilind
  • 68,093
  • 33
  • 169
  • 336
  • Unfortunately this does not work. It is a runtime test to see if the values are not equal, what you want is a compile time test. – Martin York Jul 20 '11 at 17:29
  • @Martin, yes correct. OP is asking for assertion, and I have pointed the way to find if the values are equal or not. The better way in current standard is `boost::static_assert`. – iammilind Jul 20 '11 at 17:36
  • Then you should mention boost::static_assert in your answer. I think he already knows how to test if two things are equal at runtime (that is a relatively trivial task). I was pointing out that you have mis-read the question. – Martin York Jul 20 '11 at 17:39