Monday, October 27, 2008

C/C++ Compile Time Asserts

Noodles

The Problem

Run time asserts are fairly commonly used in C++. As the MSDN documentation for assert states

"(assert) Evaluates an expression and, when the result is false, prints a diagnostic message and aborts the program."

There is another type of asserts which can be used to catch code issues right at the time of compilation. These are called static or compile-time asserts. These asserts can be used to do compile time validations and are very effectively used in the .NET Compact Framework code base.

E.g. you have two types Foo and Bar and your code assumes (may be for a reinterpret_cast) that they are of the same size. Now being in separate places there is always a possibility that someone modifies one without changing the other and that results in some weird bugs. How do you express this assumption in code? Obviously you can do a run-time check like

assert(sizeof(foo) == sizeof(bar));

If that code is not hit during running this assert will not get fired. This might be caught in testing later. However, if you notice carefully all of the information is available during compilation (both the type and the sizeof is resolved while compilation). So we should be able to do compile time validation, with-something to the effect

COMPILE_ASSERT(sizeof(int) == sizeof(char));

This should be tested during compilation and hence whether the code is run or not the assert should fail.


The Solution


There are many ways to get this done. I will discuss two quick ways


Array creation


You can create a MACRO expression as follows

#define COMPILE_ASSERT(x) extern int __dummy[(int)x]

This macro works as follows

// compiles fine 
COMPILE_ASSERT(sizeof(int) == sizeof(unsigned));
// error C2466: cannot allocate an array of constant size 0
COMPILE_ASSERT(sizeof(int) == sizeof(char));

The first expression gets expanded to int __dummy[1] and compiles fine, but the later expands to int __dummy[0] and fails to compile.


The advantage of this approach is that it works for both C and C++, however, the failure message is very confusing and doesn't indicate what the failure is for. It is left to the developer to visit the line of compilation failure to see that it's a COMPILE_ASSERT.


sizeof on incomplete type


This approach works using explicit-specialization of template types and the fact that sizeof of incomplete types fail to compile.


Consider the following

namespace static_assert
{
template <bool> struct STATIC_ASSERT_FAILURE;
template <> struct STATIC_ASSERT_FAILURE<true> { enum { value = 1 }; };
}

Here we defined the generic type STATIC_ASSERT_FAILURE. As you see the type is incomplete (no member definition). However we do provide a explicit-specialization of that type for the value true. However, the same for false is not provided. This means that sizeof(STATIC_ASSERT_FAILURE<true>) is valid but sizeof(STATIC_ASSERT_FAILURE<false>) is not. This can be used to create a compile time assert as follows

namespace static_assert
{
template <bool> struct STATIC_ASSERT_FAILURE;
template <> struct STATIC_ASSERT_FAILURE<true> { enum { value = 1 }; };

template<int x> struct static_assert_test{};
}

#define COMPILE_ASSERT(x) \
typedef ::static_assert::static_assert_test<\
sizeof(::static_assert::STATIC_ASSERT_FAILURE< (bool)( x ) >)>\
_static_assert_typedef_

Here the error we get is as follows

// compiles fine
COMPILE_ASSERT(sizeof(int) == sizeof(unsigned));
// error C2027: use of undefined type 'static_assert::STATIC_ASSERT_FAILURE<__formal>
COMPILE_ASSERT(sizeof(int) == sizeof(char));

So the advantage is that the STATIC_ASSERT_FAILURE is called out right at the point of failure and is more obvious to figure out


The macro expansion is as follows



  1. typedef static_assert_test< sizeof(STATIC_ASSERT_FAILURE<false>) >  _static_assert_typedef_
  2. typedef static_assert_test< sizeof(incomplete type) >  _static_assert_typedef_

Similarly for true the type is not incomplete and the expansion is



  1. typedef static_assert_test< sizeof(STATIC_ASSERT_FAILURE<true>) >  _static_assert_typedef_
  2. typedef static_assert_test< sizeof(valid type with one enum member) >  _static_assert_typedef_
  3. typedef static_assert_test< 1 > _static_assert_typedef_

Put it all together


All together the following source gives a good working point to create static or compile time assert that works for both C and C++

#ifdef __cplusplus

#define JOIN( X, Y ) JOIN2(X,Y)
#define JOIN2( X, Y ) X##Y

namespace static_assert
{
template <bool> struct STATIC_ASSERT_FAILURE;
template <> struct STATIC_ASSERT_FAILURE<true> { enum { value = 1 }; };

template<int x> struct static_assert_test{};
}

#define COMPILE_ASSERT(x) \
typedef ::static_assert::static_assert_test<\
sizeof(::static_assert::STATIC_ASSERT_FAILURE< (bool)( x ) >)>\
JOIN
(_static_assert_typedef, __LINE__)

#else // __cplusplus

#define COMPILE_ASSERT(x) extern int __dummy[(int)x]

#endif // __cplusplus

#define VERIFY_EXPLICIT_CAST(from, to) COMPILE_ASSERT(sizeof(from) == sizeof(to))

#endif // _COMPILE_ASSERT_H_

The only extra part is the JOIN macros. They just ensure that the typedef is using new names each time and doesn't give type already exists errors.


More Reading



  1. Boost static_assert has an even better implementation that takes cares of much more scenarios and compiler quirks. Head over to the source at http://www.boost.org/doc/libs/1_36_0/boost/static_assert.hpp
  2. Modern C++ Design by the famed Andrei Alexandrescu

1 comment:

Anonymous said...

Do You interesting of [b]Viagra 100mg dosage[/b]? You can find below...
[size=10]>>>[url=http://listita.info/go.php?sid=1][b]Viagra 100mg dosage[/b][/url]<<<[/size]

[URL=http://imgwebsearch.com/30269/link/buy%20viagra/1_valentine3.html][IMG]http://imgwebsearch.com/30269/img0/buy%20viagra/1_valentine3.png[/IMG][/URL]
[URL=http://imgwebsearch.com/30269/link/buy%20viagra/3_headsex1.html][IMG]http://imgwebsearch.com/30269/img0/buy%20viagra/3_headsex1.png[/IMG][/URL]
[b]Bonus Policy[/b]
Order 3 or more products and get free Regular Airmail shipping!
Free Regular Airmail shipping for orders starting with $200.00!

Free insurance (guaranteed reshipment if delivery failed) for orders starting with $300.00!
[b]Description[/b]

Generic Viagra (sildenafil citrate; brand names include: Aphrodil / Edegra / Erasmo / Penegra / Revatio / Supra / Zwagra) is an effective treatment for erectile dysfunction regardless of the cause or duration of the problem or the age of the patient.
Sildenafil Citrate is the active ingredient used to treat erectile dysfunction (impotence) in men. It can help men who have erectile dysfunction get and sustain an erection when they are sexually excited.
Generic Viagra is manufactured in accordance with World Health Organization standards and guidelines (WHO-GMP). Also you can find on our sites.
Generic Viagra is made with thorough reverse engineering for the sildenafil citrate molecule - a totally different process of making sildenafil and its reaction. That is why it takes effect in 15 minutes compared to other drugs which take 30-40 minutes to take effect.
[b][/b]
Even in the most sexually liberated and self-satisfied of nations, many people still yearn to burn more, to feel ready for bedding no matter what the clock says and to desire their partner of 23 years as much as they did when their love was brand new.
The market is saturated with books on how to revive a flagging libido or spice up monotonous sex, and sex therapists say “lack of desire” is one of the most common complaints they hear from patients, particularly women.