Compile-time assertion?
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_EXPRESSIO开发者_开发问答N(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)
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.
See static_assert
(C++0x only); if on an older version, see Boost's StaticAssert
.
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
Another possibility for Windows is C_ASSERT, which is defined if Windows.h is included.
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;
}
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;
}
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.
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.
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
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
I suggest to take a look at the Eigen library static assert mechanism:
http://eigen.tuxfamily.org/dox/StaticAssert_8h_source.html
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
精彩评论