Смешивание переменных и целочисленных констант в препроцессоре Boost - PullRequest
1 голос
/ 07 октября 2010

Я использую BOOST_PP для выполнения прекомпиляции вычислений в препроцессоре.Я сосредоточен на приложении, в котором размер кода чрезвычайно важен для меня.(Поэтому, пожалуйста, не говорите, что компилятор должен или , обычно , делает это, мне нужно контролировать, что выполняется во время компиляции и какой код генерируется).Однако я хочу иметь возможность использовать одно и то же имя макроса / функции как для целочисленных констант, так и для переменных.В качестве тривиального примера, у меня может быть

#define TWICE(n) BOOST_PP_MUL(n,2)
//.....
// somewhere else in code
int a = TWICE(5);

Это делает то, что я хочу, вычисляя

int a = 10;

во время компиляции.

Однако я также хочуэто будет использоваться в

int b = 5;
int a = TWICE(b);

Это должно быть предварительно обработано до

int b = 5;
int a = 5 * 2;

Конечно, я могу сделать это, используя традиционные макросы, такие как

#define TWICE(n) n * 2

Но тогда он не делает то, что я хочу, чтобы он делал для целочисленных констант (оценивая их во время компиляции).

Итак, у меня вопрос, есть ли хитрость, чтобы проверить, является ли аргумент литералом или переменной, а затем использовать другие определения.то есть что-то вроде этого:

#define TWICE(n) BOOST_PP_IF( _IS_CONSTANT(n), \
                              BOOST_PP_MUL(n,2), \
                              n * 2 )

edit: Итак, что мне действительно нужно, так это какой-то способ проверить, является ли что-то постоянным доступным во время компиляции, и, следовательно, хороший аргумент для функций BOOST_PP_.Я понимаю, что это отличается от того, что большинство людей ожидают от препроцессора и общих рекомендаций по программированию.Но не существует неправильного способа программирования, поэтому, пожалуйста, не ненавидьте этот вопрос, если вы не согласны с его философией.Существует причина, по которой библиотека BOOST_PP существует, и этот вопрос в том же духе.Возможно, это просто невозможно.

Ответы [ 3 ]

2 голосов
/ 08 октября 2010

Вы пытаетесь сделать что-то, что лучше оставить для оптимизации компилятора.

int main (void) {
  int b = 5;
  int a = b * 2;

  return a; // return it so we use a and it's not optimized away
}

gcc -O3 -s t.c

 .file "t.c"
 .text
 .p2align 4,,15
.globl main
 .type main, @function
main:
.LFB0:
 .cfi_startproc
 movl $10, %eax
 ret
 .cfi_endproc
.LFE0:
 .size main, .-main
 .ident "GCC: (Debian 4.5.0-6) 4.5.1 20100617 (prerelease)"
 .section .note.GNU-stack,"",@progbits

Оптимизирующий компилятор будет оптимизировать.

РЕДАКТИРОВАТЬ: Я знаю, что вы не хотите слышать, что компилятор "должен" или "обычно" делает это. Однако то, что вы пытаетесь сделать, - это не то, что должно быть сделано в препроцессоре C; люди, которые разработали язык C и препроцессор C, разработали его для работы с токеном предварительной обработки в качестве основного атома. CPP во многих отношениях "тупой". Это не плохо (на самом деле, это во многих случаях делает его таким полезным), но в конце концов, это препроцессор. Он обрабатывает исходные файлы перед их анализом. Он предварительно обрабатывает исходные файлы перед семантическим анализом. Он предварительно обрабатывает исходные файлы перед проверкой правильности указанного исходного файла. Я понимаю, что вы не хотите слышать, что это то, что анализатор и семантический анализатор должны обрабатывать или обычно делает. Тем не менее, это реальность ситуации. Если вы хотите создать невероятно маленький код, вам следует полагаться на свой компилятор, чтобы выполнять свою работу, вместо того, чтобы пытаться создавать конструкции препроцессора. Подумайте об этом так: тысячи часов работы ушли на ваш компилятор, поэтому постарайтесь использовать как можно больше этой работы!

1 голос
/ 07 октября 2010

Нет реальной возможности смешать два уровня (препроцессор и оценка переменных ).Из того, что я понимаю из вашего вопроса, b должна быть символической константой?

Я думаю, вы должны использовать традиционную

#define TWICE(n) ((n) * 2)

, но тогда вместо инициализации переменных с помощью выражений выследует инициализировать их с помощью констант времени компиляции.Единственное, что я вижу, чтобы заставить вычисление во время компиляции и иметь символические константы в C, это целочисленные константы перечисления.Они определены как тип int и оцениваются во время компиляции.

enum { bInit = 5 };
int b = bInit;
enum { aInit = TWICE(bInit) };
int a = aInit; 

И, как правило, вы не должны быть слишком экономными с const (как для вашего b) и проверять произведенный ассемблерс -S.

1 голос
/ 07 октября 2010

не совсем прямой подход, однако:

struct operation {
    template<int N>
    struct compile {
        static const int value = N;
    };
    static int runtime(int N) { return N; }
};

operation::compile<5>::value;
operation::runtime(5);

альтернативно

operation<5>();
operation(5);
...