C ++ выдает ошибку компиляции при сравнении sizeof () в препроцессоре #if - PullRequest
4 голосов
/ 11 ноября 2009

У меня есть это, которое не компилируется с ошибкой «фатальная ошибка C1017: недопустимое целочисленное константное выражение» из Visual Studio. Как бы я это сделал?

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

Ответы [ 10 ]

16 голосов
/ 11 ноября 2009

Препроцессор не понимает sizeof () (или типы данных, или идентификаторы, или шаблоны, или определения классов, и он должен понимать все эти вещи для реализации sizeof).

То, что вы ищете, - это статическое утверждение (реализуемое компилятором, который понимает все эти вещи). Я использую Boost.StaticAssert для этого:

template <class B>
A *Create()
{
  BOOST_STATIC_ASSERT(sizeof(B) <= sizeof(A));
  ...
}
8 голосов
/ 11 ноября 2009

Выражения препроцессора вычисляются до того, как компилятор начинает компиляцию. sizeof () оценивается только компилятором.

7 голосов
/ 11 ноября 2009

Вы не можете сделать это с препроцессором. Директивы препроцессора не могут работать с такими элементами уровня языка, как sizeof. Более того, даже если бы они могли, это все равно не сработало бы, так как директивы препроцессора удаляются из кода очень рано, от них нельзя ожидать, что они будут работать как часть кода шаблона, созданного позже (как вы, похоже, пытаетесь достижения).

Правильный способ сделать это - использовать некоторую форму статического утверждения

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

Существует довольно много реализаций статических утверждений. Выполните поиск и выберите тот, который вам больше всего подходит.

6 голосов
/ 11 ноября 2009

Препроцессор запускается перед компилятором (по крайней мере, логически) и не знает пользовательских типов (и не обязательно много знает о внутренних типах - размер int препроцессора может отличаться от целевых значений компилятора.

В любом случае, чтобы делать то, что вы хотите, вы должны использовать STATIC_ASSERT(). Смотрите следующий ответ:

С помощью STATIC_ASSERT() вы сможете сделать это:

template <class B>
A *Create()
{
    STATIC_ASSERT( sizeof(A) >= sizeof( B));
    return 0;
}
6 голосов
/ 11 ноября 2009

sizeof () нельзя использовать в директиве препроцессора.

3 голосов
/ 11 ноября 2009

Этого нельзя достичь с помощью препроцессора. Препроцессор выполняется за проход до компилятора - поэтому размеры NodeB и Node еще не были вычислены во время оценки #if.

Вы могли бы сделать что-то подобное, используя методы программирования шаблонов. Отличная книга на эту тему - Современный дизайн на C ++: универсальное программирование и применение шаблонов проектирования, автор - Андрей Александреску .

Вот пример с веб-страницы , которая создает шаблон оператора IF.

Из этого примера вы можете использовать:
IF< sizeof(NodeB)<sizeof(Node), non_existing_type, int>::RET i;

, который либо объявляет переменную типа int или типа non_existing_type. Если предположить, что несуществующий тип соответствует своему имени, если условие шаблона IF оценивается как true, то возникнет ошибка компилятора. Вы можете переименовать i что-нибудь описательное.

Использование этого было бы "прокручиванием собственного" статического утверждения, многие из которых уже доступны. Я предлагаю вам использовать один из них после того, как вы поиграете с его созданием.

1 голос
/ 18 мая 2010

Если вы заинтересованы в утверждении времени компиляции, которое будет работать как для C, так и для C ++, вот что я разработал:

#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_)

То, как это работает, заключается в том, что размер массива отрицателен (что недопустимо), когда выражение ложно. Дальнейшее включение этого в определение структуры не создает ничего во время выполнения.

0 голосов
/ 09 июня 2011

Используя MSVC, этот код компилируется для меня:

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

#if (cPointerSize == cFourBytes)
    ...

однако это (которое должно работать одинаково):

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

0 голосов
/ 30 апреля 2011

я вижу, что многие говорят, что sizeof нельзя использовать в директиве препроцессора, однако это не может быть целой историей, потому что я регулярно использую следующий макрос:

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

например:

#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));
}

выходы:

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

однако

я тоже не могу заставить sizeof () скомпилироваться в операторе #if в gcc 4.2.1. например, это не компилируется:

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

Любое понимание будет оценено.

0 голосов
/ 18 мая 2010

Это уже объяснено, но позвольте мне подробнее остановиться на , почему препроцессор не может вычислить размер структуры. Помимо того, что об этом слишком сложно спросить у простого препроцессора, существуют также флаги компилятора, которые влияют на структуру структуры.

struct X { short a; long b; };

эта структура может иметь длину 6 или 8 байтов, в зависимости от того, было ли указано, что компилятору предписано 32-битное выравнивание поля "b" по соображениям производительности. Препроцессор не может получить такую ​​информацию.

...