Переменные смешивания и целочисленные константы в препроцессоре повышения

StackOverflow https://stackoverflow.com/questions/3884489

  •  28-09-2019
  •  | 
  •  

Вопрос

Я использую 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 )

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

Это было полезно?

Решение

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

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 TC

 .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 Preprocessor, разработали его для работы с предварительно обработкой токеном, как основной атом. CPP во многих отношениях «тупой». Это не плохая вещь (на самом деле, это во многих случаях то, что делает его таким полезным), но в конце дня это препроцессор. Предварительно предопределяет исходные файлы, прежде чем они будут проанализированы. Предварительно исходные исходные файлы до возникновения семантического анализа. Предпоручивает исходные файлы до проверки достоверности данного исходного файла. Я понимаю, что вы не хотите слышать, что это то, что парсер и семантический анализатор должны обращаться или обычно. Однако это реальность ситуации. Если вы хотите разработать код, который невероятно маленький, то вы должны полагаться на ваш компилятор, чтобы сделать свою работу вместо того, чтобы попытаться создать препроцессорные конструкции, чтобы выполнить работу. Подумайте об этом таким образом: тысячи часов работы вошли в ваш компилятор, поэтому постарайтесь повторно повторно повторно как можно больше этой работы!

Другие советы

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top