Как сделать первый вызов макроса отличным от всех следующих? - PullRequest
4 голосов
/ 15 марта 2010

Это может быть очень просто, но я не могу найти хороший ответ. Как сделать макрос, представляющий сначала определенное значение, а затем другое?

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

Должен ли я использовать "предварительный просмотр аргумента"?

Вам нужно знать, что я генерирую код:

#define INC_X x++ //should be declared if needed to
#define PRINT_X printf("VALUE OF X: %d\n", x)

int func() {
[...]
INC_X;
[...]
INC_X;
[...]
PRINT_X;
[...]
}

Ответы [ 7 ]

3 голосов
/ 15 марта 2010

Насколько я знаю, это невозможно. Я не знаю, каким образом расширение макроса контролирует способ расширения другого макроса или самого себя. C99 ввел _Pragma, чтобы #pragma можно было выполнять в макросах, но нет эквивалента для #define или #undef.

2 голосов
/ 15 марта 2010
#include <stdio.h>
#define FOO &s[ (!c) ? (c++, 0) : (4) ]
static int c = 0;
const char s[] = { 'f', 'o', 'o', '\0', 'b', 'a', 'r', '\0' };

int main() {
 puts(FOO);
 puts(FOO);
 return 0;
}

Помогает ли вышеуказанное?

1 голос
/ 15 марта 2010

После редактирования я пойду на ответ. Требуется, чтобы ваш компилятор поддерживал __FUNCTION__, что и MSVC, и GCC.

Сначала напишите набор функций, которые отображают строки в целые числа в памяти, все они хранятся в каком-то глобальном экземпляре структуры. Это оставлено в качестве упражнения для читателя, функционально это хэш-карта, но я назову полученный экземпляр «global_x_map». Функция get_int_ptr определена так, чтобы возвращать указатель на int, соответствующий указанной строке, и, если она еще не существует, создать ее и инициализировать ее значением 0. reset_int_ptr просто присваивает 0 счетчику на данный момент позже вы поймете, почему я не просто написал *_inc_x_tmp = 0;.

#define INC_X do {\
    int *_inc_x_tmp = get_int_ptr(&global_x_map, __FILE__ "{}" __FUNCTION__); \
    /* maybe some error-checking here, but not sure what you'd do about it */ \
    ++*_inc_x_tmp; \
} while(0)

#define PRINT_X do {\
    int *_inc_x_tmp = get_int_ptr(&global_x_map, __FILE__ "{}" __FUNCTION__); \
    printf("%d\n", *_inc_x_tmp); \
    reset_int_ptr(&global_x_map, _inc_x_tmp); \
} while(0)

Я выбрал разделитель "{}" на том основании, что он не будет встречаться в искаженном имени функции C - если ваш компилятор по какой-то причине может поместить это в искаженное имя функции, то, конечно, вы должны иметь изменить это. Использование чего-либо, чего нет в имени файла на вашей платформе, также будет работать.

Обратите внимание, что функции, использующие макрос, не являются входящими, поэтому это не совсем то же самое, что определение автоматической переменной. Я думаю, что возможно сделать это вновь входящим, все же. Передайте __LINE__ в качестве дополнительного параметра get_int_ptr. Когда запись будет создана, сохраните значение __LINE__.

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

Конечно, это работает только в том случае, если «первый» вызов INC_X всегда одинаков, вызывается только один раз за выполнение функции, и этот вызов не появляется в той же строке, что и другой вызов. Если он находится в цикле, блоке if() и т. Д., Он идет не так. Но если он находится внутри блока, то объявление автоматической переменной тоже может пойти не так. Это также работает только в том случае, если всегда вызывается PRINT_X (проверьте выходы из-за ранней ошибки), иначе вы не восстановите стек.

Все это может показаться сумасшедшим количеством инженерных разработок, но, по сути, это то, как Perl реализует динамически изменяемые переменные: у него есть стек для каждого имени символа. Разница в том, что, как и в C ++ с RAII, Perl автоматически извлекает этот стек при выходе из области видимости.

Если вам нужно, чтобы он был как потокобезопасным, так и повторно входящим, то сделайте global_x_map thread-local вместо global.

Edit: этот идентификатор __FILE__ "{}" __FUNCTION__ по-прежнему не является уникальным, если в заголовочных файлах определены статические функции - разные версии в разных TU будут использовать один и тот же счетчик в версии без повторного входа. В реентрирующей версии все нормально, хотя, я думаю. У вас также будут проблемы, если __FILE__ - это базовое имя, а не полный путь, поскольку вы можете получить коллизии для static функций с одинаковыми именами, определенных в файлах с одинаковыми именами. Это scuppers даже вернувшаяся версия. Наконец, ничего из этого не проверено.

1 голос
/ 15 марта 2010

Судя по всему, вы можете попробовать, если Boost.Preprocessor содержит то, что вы ищете. Посмотрите на этот урок

http://www.boostpro.com/tmpbook/preprocessor.html

из превосходной книги по метапрограммированию шаблонов C ++.

0 голосов
/ 15 марта 2010

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

void foo (void);
void bar (void);

void (*_func_foo)(void) = foo;

void foo (void) {
    puts ("foo\n");
}

void bar (void) {
    puts ("bar"\n");
}

#define FOO()  _func_foo(); \
               _func_foo = bar;

int main (void) {
    FOO();
    FOO();
    FOO();
    return 0;
}
0 голосов
/ 15 марта 2010
#define FOO __COUNTER__ ? bar : foo

Редактировать: удален весь ненужный код

0 голосов
/ 15 марта 2010

А как насчет макроса #define some flag в конце его выполнения и сначала проверять этот флаг?

#def printFoo
   #ifdef backagain
       bar
   #else
       foo
       #def backagain

Необходимо добавить несколько символов \, чтобы это работало - и вы, вероятно, не хотите этого делать по сравнению с встроенной функцией func ()

...