views:

493

answers:

8

I have this which does not compile with the error "fatal error C1017: invalid integer constant expression" from visual studio. How would I do this?

template <class B>
A *Create()
{
  #if sizeof(B) > sizeof(A)
  #error sizeof(B) > sizeof(A)!
  #endif
  ...
}
+6  A: 

sizeof() cannot be used in a preprocessor directive.

MArag
Can a preprocessor ever run a function like that?
David Oneill
So how do I do what I want?
Ramónster
sizeof() is an operator actually and it is determined at compile time, so I figured it should work in a preprocessor directive
Ramónster
Back in the old days, some C/C++ compilers actually did support sizeof in preprocessor expressions. Turbo C++ comes to mind.
Jeff Paquette
@Ramónster: Even if it was so, your code still wouldn't work the way you want it to work. You can't have preprocessing directives operating on template arguments.
AndreyT
+8  A: 

Preprocessor expressions are evaluated before the compiler starts compilation. sizeof() is only evaluated by the compiler.

David Joyner
+1 good explantion
Patrick
Misses the main issue in the original question though...
AndreyT
+6  A: 

You can't do this with preprocessor. Preprocessor directives cannot operate with such language-level elements as sizeof. Moreover, even if they could, it still wouldn't work, since preprocessor directives are eliminated from the code very early, they can't be expected to work as part of template code instantiated later (which is what you seem to be trying to achieve).

The proper way to go about it is to use some form of static assertion

template <class B>
A *Create()
{
  STATIC_ASSERT(sizeof(B) <= sizeof(A));
  ...
}

There are quite a few implementations of static assertions out there. Do a search and choose one that looks best to you.

AndreyT
+12  A: 

The preprocessor does not understand sizeof() (or data types, or identifiers, or templates, or class definitions, and it would need to understand all of those things to implement sizeof).

What you're looking for is a static assertion (enforced by the compiler, which does understand all of these things). I use Boost.StaticAssert for this:

template <class B>
A *Create()
{
  BOOST_STATIC_ASSERT(sizeof(B) <= sizeof(A));
  ...
}
Josh Kelley
+1, and know that C++0x will provide a language feature static_assert() too. Some compilers already provide it.
Klaim
Better yet: `BOOST_MPL_ASSERT_MSG`, providing a healthy message in the compiler error output is much nicer to your users!
Matthieu M.
+3  A: 

This cannot be accomplished with pre-processor . The pre-processor executes in a pass prior to the compiler -- therefore the sizes of NodeB and Node have not yet been computed at the time #if is evaluated.

You could accomplish something similar using template-programming techniques. An excellent book on the subject is Modern C++ Design: Generic Programming and Design Patterns Applied, by Andrei Alexandrescu.

Here is an example from a web page which creates a template IF statement.

From that example, you could use:
IF< sizeof(NodeB)<sizeof(Node), non_existing_type, int>::RET i;

which either declares a variable of type int or of type non_existing_type. Assuming the non-existing type lives up to its name should the template IF condition evaluate as true, a compiler error will result. You can rename i something descriptive.

Using this would be "rolling your own" static assert, of which many are already available. I suggest you use one of those after playing around with building one yourself.

Heath Hunnicutt
+6  A: 

The preprocessor runs before the compiler (at least logically it does) and has no knowledge of user defined types (and not necessarily much knowledge about intrinsic types - the preprocessor's int size could be different than the compiler targets.

Anyway, to do what you want, you should use a STATIC_ASSERT(). See the following answer:

With a STATIC_ASSERT() you'll be able to do this:

template <class B>
A *Create()
{
    STATIC_ASSERT( sizeof(A) >= sizeof( B));
    return 0;
}
Michael Burr
+1 for STATIC_ASSERT
David Joyner
I removed the `typename` keyword from the `sizeof(B)` expression - MSVC doesn't care either way and GCC doesn't like it there. Like I said initially, the rules for when `typename` is required and forbidden make my head hurt. I wouldn't mind if someone commented on why it's not allowed here (at least according to GCC).
Michael Burr
A: 

This has already been explained, but allow me to elaborate on why the preprocessor can not compute the size of a structure. Aside from the fact that this is too much to ask of a simple preprocessor, there are also compiler flags that affect the way the structure is laid out.

struct X { short a; long b; };

this structure might be 6 bytes or 8 bytes long, depending on whether the compiler was told to 32-bit align the "b" field, for performance reasons. There's no way the preprocessor could have that information.

Vagrant
A: 

If you are interested in a compile time assert that will work for both C and C++, here is one I developed:

#define CONCAT2(x, y)              x ## y
#define CONCAT(x, y)               CONCAT2(x, y)

#define COMPILE_ASSERT(expr, name)     \
    struct CONCAT(name, __LINE__) { char CONCAT(name, __LINE__) [ (expr) ? 1 : -1 ]; }

#define CT_ASSERT(expr)  COMPILE_ASSERT(expr, ct_assert_)

The to how this works is that the size of the array is negative (which is illegal) when the expression is false. By further wrapping that in a structure definition, this does not create anything at runtime.

R Samuel Klatchko