5

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
  ...
}
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Ramónster
  • 2,318
  • 3
  • 17
  • 16
  • 1
    What is NodeB and Node? Please provide more details. – Kugel Nov 11 '09 at 19:59
  • 2
    Duplicate: http://stackoverflow.com/questions/1612322/why-cant-i-use-sizeof-in-a-preprocessor-condition – Daniel Pryden Nov 11 '09 at 20:01
  • I agree with Kugel, I'm not sure I understand what you're trying to do... – David Oneill Nov 11 '09 at 20:01
  • 2
    Not a duplicate. The main issue with the above is not the use of `sizeof` in `#if`, The main issue is the assumption that you can use preprocessor directives in a template code and expect it do depend on template parameters. – AnT stands with Russia Nov 11 '09 at 20:04
  • @AndreyT: The preprocessor runs before the C++ parser. AT the moment the preprocessor encounters the sizeof, you can't talk about template code yet. At that point, the program is just a sequence of tokens without meaning. – MSalters Nov 12 '09 at 09:38
  • @MSalters: To be a little more formal, the C++ standard prescribes steps of the compilation process. Preprocessor directives are evaluated fairly early, and operate on names in general. No meaning is attached to the names until later in compilation. This applies both to `sizeof` and `template` - by the time those are even identified as keywords, preprocessor directives are finished, and nothing remains except the transformed text they left behind. – David Thornley May 18 '10 at 19:22

10 Answers10

16

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
  • 56,064
  • 19
  • 146
  • 246
  • 1
    +1, and know that C++0x will provide a language feature static_assert() too. Some compilers already provide it. – Klaim Nov 11 '09 at 21:01
  • Better yet: `BOOST_MPL_ASSERT_MSG`, providing a healthy message in the compiler error output is much nicer to your users! – Matthieu M. Nov 12 '09 at 13:48
8

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

David Joyner
  • 22,449
  • 4
  • 28
  • 33
8

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.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
6

sizeof() cannot be used in a preprocessor directive.

MArag
  • 136
  • 3
6

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;
}
Community
  • 1
  • 1
Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • 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 Nov 11 '09 at 20:35
3

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
  • 18,667
  • 3
  • 39
  • 62
1

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
  • 74,869
  • 16
  • 134
  • 187
0

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
  • 1,696
  • 12
  • 19
0

Using MSVC, this code compiles for me:

const int cPointerSize = sizeof(void*);
const int cFourBytes = 4;`

#if (cPointerSize == cFourBytes)
    ...

however this (which should work identically) does not:

#if ( sizeof(void*) == 4 ) ...

Orlymee
  • 2,349
  • 1
  • 22
  • 24
Dave
  • 1
  • Allow me to point out that the size of a void pointer is 8 on 64-bit systems, not 4. – yyny Mar 03 '16 at 22:21
  • The reason this code "works" is because unknown macro names are silently replaced by the value `0` in `#if` directives. The preprocessor sees this condition as `#if 0 == 0`. Run it on a 64-bit system and it'll still evaluate true: the constants above the `#if` line have nothing to do with how it's evaluated. – Alex Celeste Sep 13 '18 at 13:52
-1

i see many people say that sizeof cannot be used in a pre-processor directive, however that can't be the whole story because i regularly use the following macro:

#define STATICARRAYSIZE(a) (sizeof(a)/sizeof(*a))

for example:

#include <stdio.h>

#define STATICARRAYSIZE(a) (sizeof(a)/sizeof(*a))

int main(int argc, char*argv[])
{
        unsigned char chars[] = "hello world!";
        double        dubls[] = {1, 2, 3, 4, 5};

        printf("chars num bytes: %ld, num elements: %ld.\n" , sizeof(chars), STATICARRAYSIZE(chars));
        printf("dubls num bytes: %ld, num elements: %ld.\n" , sizeof(dubls), STATICARRAYSIZE(dubls));
}

yields:

orion$ ./a.out 
chars num bytes: 13, num elements: 13.
dubls num bytes: 40, num elements: 5.

however

i, too, cannot get sizeof() to compile in a #if statement under gcc 4.2.1. eg, this doesn't compile:

#if (sizeof(int) == 2)
#error uh oh
#endif

any insight would be appreciated.

orion elenzil
  • 4,484
  • 3
  • 37
  • 49
  • 1
    #define does a simple text substitution of "STATICARRAYSIZE" for "(sizeof(a)/sizeof(*a))". For example in the first printf statement when the preprocessor finishes its work, it outputs the string "sizeof(chars)/sizeof(*chars)", not "13" – DGentry Oct 29 '12 at 22:39