views:

417

answers:

3

I am trying to setup a static assert (outside the main function) with GCC v4.3.x:

#define STATIC_ASSERT(cond) extern void static_assert(int arg[(cond) ? 1 : -1])
STATIC_ASSERT( (double)1 == (double)1 ); // failed

but when I use float numbers, the assert always failed.

Is it possible to run this static assert properly ?

+2  A: 

EDIT:

Indeed, moving STATIC_ASSERT out of main() gives a compiler error because a cast to a type other than an integral or enumeration type cannot appear in a constant-expression. Removing the casts works with GCC still it's not a valid ICE (as pointed by @AndreyT).

#define STATIC_ASSERT(cond) extern void static_assert(int arg[(cond) ? 1 : -1])

STATIC_ASSERT( 1.0 == 1.0 );
STATIC_ASSERT( 1.0 != 1.0 ); // this is line 4

int main()
{
  return 0;
}

gives:

main.cpp:4: error: size of array ‘arg’ is negative

Reference: ISO/IEC 14882 - 5.19 Constant Expressions

An integral constant-expression can involve only literals (2.13), enumerators, const variables or static data members of integral or enumeration types initialized with constant expressions (8.5), non-type template parameters of integral or enumeration types, andsizeofexpressions. Floating literals (2.13.3) can appear only if they are cast to integral or enumeration types. Only type conversions to integral or enumeration types can be used. In particular, except in sizeof expressions, functions, class objects, pointers, or references shall not be used, and assignment, increment, decrement, function-call, or comma operators shall not be used.


EDIT2: for the record, here is my own implementation of static assertions extracted from my code base: 1951741.cpp

#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(1.0f == 1.0 , ok);
STATIC_ASSERT(1.0f != 1.0 , ko);

int main()
{
  return 0;
}

When using STATIC_ASSERT((float) 1 == (float) 1, must_be_true); it gives a proper error:

main.cpp:49: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression


What is your question exactly?

#define STATIC_ASSERT(cond) extern void static_assert(int arg[(cond) ? 1 : -1])

int main()
{
  STATIC_ASSERT( (float)1 == (float)1 );
  STATIC_ASSERT( (float)1 != (float)1 ); // this is line 6
  return 0;
}

Compiling it with gcc 4.4.2 gives me:

main.cpp: In function ‘int main()’:

main.cpp:6: error: size of array ‘arg’ is negative

So yes, (float)1 != (float)1 evaluates to false and makes your STATIC_ASSERT macro use an array of size -1 which stops compilation.

Gregory Pakosz
try to move the STATIC_ASSERT(...) outside the main()
Soubok
indeed, moving it outside of `main()` gives a compiler error
Gregory Pakosz
I edited the answer with a link to a `STATIC_ASSERT` implementation that gives the right compiler error message about casting in constant expressions
Gregory Pakosz
@Soubok did you try my implementation of `STATIC_ASSERT` linked in the answer, could you please tell the error message you get using it when you keep the `(double)` casts?
Gregory Pakosz
None of this is standard C++, regardless of whether you do it inside main or outside main. Floating-point literals cannot be used in integral constant expressions unless they are cast to integral type. `1.0 == 1.0` is not an integrals constant expression. It doesn't form a compile-time `bool` value. Whatever is "working" in your test is nothing that just GCC-specific extensions outside the limits of C++ language.
AndreyT
Note that removing the explicit cast to `double` from a floating-point literal does not make it a valid inegral constan expresison in C++. Again, `1.0 == 1.0` is not an ICE in C++.
AndreyT
well the point was not in is it standard or correct to write `1.0 == 1.0` but rather saying that my implementation of `STATIC_ASSERT` gave a "better" message than "error: size of array ‘arg’ is negative" :)
Gregory Pakosz
"An integer constant expression shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, and floating constants that are the immediate operands of casts." -- indeed `1.0` is not an immediate operand of cast
Gregory Pakosz
Your code is C++, but for some reason your are quoting C standard (the term *constant* has different meanings in C and C++). Yet, C++ definition is not different in principle.
AndreyT
Yeah I'm definitely tired, had both the pdfs opened, picked up the wrong one. thank you for letting me realize about gcc allowing `1.0` while it's not an ICE. I hope there isn't any mistake remaining.
Gregory Pakosz
+4  A: 

I think this has to do with the rule that a cast to anything but an integer or enumeration type can not appear in a constant expression.

// would all work for example
STATIC_ASSERT( 1.0 == 1.0 );
STATIC_ASSERT( (int)1.0 == (int)1.0 );

So it's not the assert itself that's invalid, and causes a compiler error, it's your cast...

Just for the record, boost, of course, has a static assert too.

Pieter
+7  A: 

C++ Standard 2003, 5.19 "Constant expressions", paragraph 1.

In several places, C++ requires expressions that evaluate to an integral or enumeration constant: as array bounds (8.3.4, 5.3.4), as case expressions (6.4.2), as bit-field lengths (9.6), as enumerator initializers (7.2), as static member initializers (9.4.2), and as integral or enumeration non-type template arguments (14.3).

constant-expression: conditional-expression

An integral constant-expression can involve only literals (2.13), enumerators, const variables or static data members of integral or enumeration types initialized with constant expressions (8.5), non-type tem- plate parameters of integral or enumeration types, and sizeof expressions. Floating literals (2.13.3) can appear only if they are cast to integral or enumeration types. Only type conversions to integral or enumeration types can be used. In particular, except in sizeof expressions, functions, class objects, pointers, or references shall not be used, and assignment, increment, decrement, function-call, or comma operators shall not be used.

Alexey Malistov
But why? why is that a limitation? what's the sense in that?
shoosh
I do not know. IMHO because floating operations is not accurate.
Alexey Malistov