views:

412

answers:

6

Since boost is forbidden in a company I work for I need to implement its functionality in pure C++. I've looked into boost sources but they seem to be too complex to understand, at least for me. I know there is something called static_assert() in the C++0x standart, but I'd like not to use any C++0x features.

+11  A: 
template<bool> struct StaticAssert;
template<> struct StaticAssert<true> {};

int main() {
   StaticAssert< (4>3) >(); //OK
   StaticAssert< (2+2==5) >(); //ERROR
}
Alexey Malistov
+1 simple enough, but I like to have a message associated with the assertion
Gregory Pakosz
@Gregory: `StaticAssert< (2+2==5) > associatedMessage();`
Alexey Malistov
there are places where you don't want / can't use variables though
Gregory Pakosz
@Gregory: `typedef StaticAssert< (2+2==5) > AssociatedMessage_t;`
Alexey Malistov
Aren't you missing the `struct` keyword in the first two lines? And the `typedef` trick doesn't appear to work - incomplete types appear to be acceptable (in fact it seems the struct might even declare, e.g a negative-sized array but typedeffing would still compile, as it doesn't instantiate the template?)
UncleBens
@UncleBens: Yes. Right. Thank you.
Alexey Malistov
+2  A: 

You could simply copy the macro from the Boost source file to your own code. If you don't need to support all the compilers Boost supports you can just pick the right definition for your compiler and omit the rest of the #ifdefs in that file.

sth
+8  A: 

One other trick (which can be used in C) is to try to build an array with a negative size if the assert fail:

#define ASSERT(cond) int foo[(cond) ? 1 : -1]

as a bonus, you may use a typedef instead of an object, so that it is usable in more contexts and doesn't takes place when it succeed:

#define ASSERT(cond) typedef int foo[(cond) ? 1 : -1]

finally, build a name with less chance of name clash (and reusable at least in different lines):

#define CAT_(a, b) a ## b
#define CAT(a, b) CAT_(a, b)
#define ASSERT(cond) typedef int CAT(AsSeRt, __LINE__)[(cond) ? 1 : -1]
AProgrammer
+2  A: 

I am using the following header file, with code ripped from someone else...

#ifndef STATIC_ASSERT__H
#define STATIC_ASSERT__H

/* ripped from http://www.pixelbeat.org/programming/gcc/static_assert.html */

#define ASSERT_CONCAT_(a, b) a##b
#define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b)
/* These can't be used after statements in c89. */
#ifdef __COUNTER__
  /* microsoft */
  #define STATIC_ASSERT(e) enum { ASSERT_CONCAT(static_assert_, __COUNTER__) = 1/(!!(e)) }
#else
  /* This can't be used twice on the same line so ensure if using in headers
   * that the headers are not included twice (by wrapping in #ifndef...#endif)
   * Note it doesn't cause an issue when used on same line of separate modules
   * compiled with gcc -combine -fwhole-program.  */
  #define STATIC_ASSERT(e) enum { ASSERT_CONCAT(assert_line_, __LINE__) = 1/(!!(e)) }
#endif

/* http://msdn.microsoft.com/en-us/library/ms679289(VS.85).aspx */
#ifndef C_ASSERT
#define C_ASSERT(e) STATIC_ASSERT(e)
#endif

#endif
doj
+6  A: 

Here is my own implementation of static assertions extracted from my code base: 1951741.cpp

Usage:

STATIC_ASSERT(expression, message);

When the static assertion test fails, a compiler error message that somehow contains the STATIC_ASSERTION_FAILED_AT_LINE_xxx_message is generated.

message has to be a valid C++ identifier, like no_you_cant_have_a_pony which will produce a compiler error containing:

STATIC_ASSERTION_FAILED_AT_LINE_1337_no_you_cant_have_a_pony :)

#define CONCATENATE(arg1, arg2)   CONCATENATE1(arg1, arg2)
#define CONCATENATE1(arg1, arg2)  CONCATENATE2(arg1, arg2)
#define CONCATENATE2(arg1, arg2)  arg1##arg2

/**
 * Usage:
 *
 * <code>STATIC_ASSERT(expression, message)</code>
 *
 * When the static assertion test fails, a compiler error message that somehow
 * contains the "STATIC_ASSERTION_FAILED_AT_LINE_xxx_message" is generated.
 *
 * /!\ message has to be a valid C++ identifier, that is to say it must not
 * contain space characters, cannot start with a digit, etc.
 *
 * STATIC_ASSERT(true, this_message_will_never_be_displayed);
 */

#define STATIC_ASSERT(expression, message)\
  struct CONCATENATE(__static_assertion_at_line_, __LINE__)\
  {\
    implementation::StaticAssertion<static_cast<bool>((expression))> CONCATENATE(CONCATENATE(CONCATENATE(STATIC_ASSERTION_FAILED_AT_LINE_, __LINE__), _), message);\
  };\
  typedef implementation::StaticAssertionTest<sizeof(CONCATENATE(__static_assertion_at_line_, __LINE__))> CONCATENATE(__static_assertion_test_at_line_, __LINE__)

  // note that we wrap the non existing type inside a struct to avoid warning
  // messages about unused variables when static assertions are used at function
  // scope
  // the use of sizeof makes sure the assertion error is not ignored by SFINAE

namespace implementation {

  template <bool>
  struct StaticAssertion;

  template <>
  struct StaticAssertion<true>
  {
  }; // StaticAssertion<true>

  template<int i>
  struct StaticAssertionTest
  {
  }; // StaticAssertionTest<int>

} // namespace implementation


STATIC_ASSERT(true , ok);
STATIC_ASSERT(false , ko);

int main()
{
  return 0;
}
Gregory Pakosz
+1 for "no_you_cant_have_a_pony"
Billy ONeal
A: 

I believe this should work:

template<bool> struct CompileTimeAssert;   
template<> struct CompileTimeAssert<true>{};
#define STATIC_ASSERT(e) (CompileTimeAssert <(e) != 0>())
Sigmel