Условная оценка макроса C++

У меня есть символ, определенный глобально, который должен быть условно неопределенным для данного подмножества моих исходных файлов. Все файлы, требующие специальной обработки, уже упакованы в пре- и пост-включения:

pre.h:

#undefine mysymbol // [1]

post.h:

#define mysymbol MY_SYMBOL_DEFINITION // [2]

Моя проблема в том, что pre.h и post.h могут быть включены несколько раз для данного исходного файла из-за различных цепочек включения. Таким образом, мне нужно, чтобы 1 произошло в первый раз перед .h включен, и мне нужно, чтобы 2 произошло в последний раз что post.h включено. Концептуально:

pre         // undefine
   pre      // no-op
      pre   // no-op
      post  // no-op
   post     // no-op
post        // redefine

Поскольку я использую GCC 3.4.6, у меня нет доступа к прагмы макросов push и pop, которые в противном случае могли бы решить эту проблему для меня.

Как я могу эмулировать это поведение с оставшейся функциональностью препроцессора?

Я пытался сделать что-то вроде увеличения/уменьшения значения с помощью препроцессора, но я не уверен, что это возможно.

«Что я действительно пытаюсь сделать?»

У нас есть макросы для замены new на new(__FILE__, __LINE__) -- см. мой другой вопрос по этой теме -- и нам нужно отменить определение этих макросов в наборе исходных файлов, обернутых пре- и пост-включениями, описанными выше, потому что мы не смогли создать макрос, совместимый с новым синтаксисом размещения, используемым в нем.


person David Citron    schedule 26.03.2009    source источник
comment
Очевидный вопрос: что вы ДЕЙСТВИТЕЛЬНО пытаетесь сделать?   -  person    schedule 26.03.2009
comment
@Neil - добавлено, см. РЕДАКТИРОВАТЬ   -  person David Citron    schedule 26.03.2009


Ответы (5)


Вы можете добавить что-то вроде этого в свой файл pre.h:

... 

#ifdef COUNT
#if COUNT == 2
#undef COUNT
#define COUNT 3
#endif

#if COUNT == 1
#undef COUNT
#define COUNT 2
#endif

#else
#define COUNT 1

... here put your pre.h code

#endif

И в пост.ч:

#ifdef COUNT
#if COUNT == 1
#undef COUNT
#endif

#if COUNT == 2
#undef COUNT
#define COUNT 1
#endif

#if COUNT == 3
#undef COUNT
#define COUNT 2
#endif

...    

#end

#ifndef COUNT

... here put your pre.h code

#endif

Но вам нужно знать, насколько глубоко вы можете зайти.

person klew    schedule 26.03.2009
comment
У вас с Беном Мерфи были похожие идеи :-) - person David Citron; 26.03.2009

Если вы знаете максимальную глубину рекурсии, вы сможете смоделировать push/pop, определив новый макрос на каждом уровне. Для приведенного вами примера с 3 уровнями это будет выглядеть примерно так:

Пред.ч:

#ifdef RECURSION_COUNT_1
 #ifdef RECURSION_COUNT_2
  #ifdef RECURSION_COUNT_3
   #error Recursion level too deep
  #else
   #define RECURSION_COUNT_3
  #endif
 #else
  #define RECURSION_COUNT_2
 #endif
#else
 #define RECURSION_COUNT_1
 #undef YOUR_SYMBOL_HERE
#endif

Post.h

#ifdef RECURSION_COUNT_3
 #undef RECURSION_COUNT_3
#else
 #ifdef RECURSION_COUNT_2
  #undef RECURSION_COUNT_2
 #else
  #ifdef RECURSION_COUNT_1
   #undef RECURSION_COUNT_1
   #define YOUR_SYMBOL_HERE
  #endif
 #endif
#endif
person Community    schedule 26.03.2009
comment
Интересное решение, если немного многословно :-) - person David Citron; 26.03.2009
comment
Хм, было бы чище оставить только один из этих макросов определенным за раз, тогда порядок мог бы позаботиться об остальных ветвях, как в решении klew. - person ; 27.03.2009

пре.ч

#ifndef MYSYMBOLUNDEFFERSTACK
#define MYSYMBOLUNDEFFERSTACK 0
#else
#define MYSYMBOLUNDEFFERSTACKTMP (MYSYMBOLUNDEFFERSTACK+1)
#undef MYSYMBOLUNDEFFERSTACK
#define MYSYMBOLUNDEFFERSTACK MYSYMBOLUNDEFFERSTACKTMP
#undef MYSYMBOLUNDEFFERSTACKTMP
#endif

#if MYSYMBOLUNDEFFERSTACK == 0
#undef mysymbol
#endif

пост.ч

#ifndef MYSYMBOLUNDEFFERSTACK
#error "Async Post.h"
#else
#define MYSYMBOLUNDEFFERSTACKTMP (MYSYMBOLUNDEFFERSTACK-1)
#undef MYSYMBOLUNDEFFERSTACK
#define MYSYMBOLUNDEFFERSTACK MYSYMBOLUNDEFFERSTACKTMP
#undef MYSYMBOLUNDEFFERSTACKTMP
#endif

#if MYSYMBOLUNDEFFERSTACK == 0
#define mysymbol "MY_SYMBOL_DEFINITION"
#endif

Что отлично работает в этом случае:

#include "stdio.h"
#include "pre.h"
#include "pre.h"
#include "pre.h"
//const char *pCompileError = mysymbol;
#include "post.h"
//const char *pCompileError = mysymbol;
#include "post.h"
//const char *pCompileError = mysymbol;
#include "post.h"

int main(void)
{
    const char *p = mysymbol;
    printf("%s\n", p);
    return 0;
}

Изменить: отлично работает с gcc 4.0.

person nusi    schedule 26.03.2009
comment
На самом деле это не работает — символ определяется после первого включения post.h. Пожалуйста, попробуйте и посмотрите. - person David Citron; 26.03.2009
comment
Численная оценка в препроцессоре несколько устарела. В противном случае не было бы необходимости в #if только для #ifdef. Я довольно часто видел код типа #if DEFINEDVALUE ›= 2. Может быть, ошибка в вашей версии gcc? - person nusi; 26.03.2009
comment
@nusi - я имею в виду, что если у вас есть #define A 1 и #define B (A+1), это НЕ приведет к B == 2, как требуется в приведенном выше коде. - person David Citron; 26.03.2009
comment
#define A 1, #define B (A+1), #if B == 2, #error foo, #endif работает с gcc4. Но вы правы, код в моем ответе кажется ошибочным. - person nusi; 26.03.2009
comment
@nusi -- да, ты прав, простой случай работает. Но передача значения через temp не работает. Любые идеи? - person David Citron; 26.03.2009

Увеличение/уменьшение значения во время компиляции, вероятно, возможно с помощью волшебства метапрограммирования а-ля Локи. Но вы уверены, что это необходимо?

Почему ваш файл h включен так много раз? Почему бы не использовать охрану включения?

person Assaf Lavie    schedule 26.03.2009
comment
Смысл заголовков pre/post состоит в том, чтобы обернуть каждый файл, в который они включены. Include Guards нарушит это поведение, поскольку pre/post заголовки будут включены только один раз для каждой единицы компиляции. - person David Citron; 26.03.2009

Пытаться:

Файл pre.h

#ifndef MYMACROGUARD
    #undef MYMACRO
    #define MYMACROGUARD MYMACROGUARD+1
#endif

Файл post.h

#if MYMACROGUARD <= 0
    #undef MYMACROGUARD
#else
    #define MYMACROGUARD MYMACROGUARD-1
#endif

Протестировано с этим кодом

#define MYMACRO

#include<iostream>

using namespace std;
int main()
{
    #ifdef MYMACRO
        cout<<"1"<<endl;
    #endif

    #include <pre.h>

    #ifdef MYMACRO
        cout<<"2"<<endl;
    #endif

    #include <pre.h>

    #ifdef MYMACRO
        cout<<"3"<<endl;
    #endif

    #include <post.h>

    #ifdef MYMACRO
        cout<<"4"<<endl;
    #endif

    #include <post.h>

    #ifdef MYMACRO
        cout<<"5"<<endl;
    #endif
}

$> g++ -w -I. test.cpp && ./a.out

1
5
person KitsuneYMG    schedule 26.03.2009
comment
Я уже пробовал это, но числовая оценка, похоже, не происходит в препроцессоре. Также вы не можете переопределить защиту. Наконец, сам макрос (MYMACROGUARD) не заменяется в тексте замены. - person David Citron; 26.03.2009
comment
Это работает для вас по совпадению, потому что у вас есть только два уровня вложенности. Если вы попробуете три уровня вложенности, это потерпит неудачу. - person David Citron; 26.03.2009
comment
Я пробовал gcc и g++ и не могу заставить работать #define SOME SOME+1. Он переопределяет макрос без добавления. Когда я пытаюсь использовать SOME в своем коде, он расширяется до SOME+1. И тестирование с #if ‹= SOME не работает. - person klew; 26.03.2009
comment
Пробовал на codepad.org (там вроде g++) - жалуется на переопределение макросов. - person xtofl; 26.03.2009