Средство препроцессора __COUNTER__ в Visual C ++ - PullRequest
7 голосов
/ 04 августа 2011

Мне нужно сгенерировать последовательность последовательных чисел во всем коде во время компиляции.Я попробовал «__COUNTER__» таким образом:

void test1()
{
  printf("test1(): Counter = %d\n", __COUNTER__);
}
void test2()
{
  printf("test2(): Counter = %d\n", __COUNTER__);
}
int main()
{
  test1();
  test2();
}

И результат был идеальным, как я и ожидал:

test1(): Counter = 0
test2(): Counter = 1

Затем я разложил «__COUNTER__» в разные файлы .cpp:

In Foo.cpp:
Foo::Foo()
{
  printf("Foo::Foo() with counter = %d\n", __COUNTER__);
}
In Bar.cpp:
Bar::Bar()
{
  printf("Bar::Bar() with counter = %d\n", __COUNTER__);
}

In Main.cpp:
int main()
{
  Foo foo;
  Bar bar;
}

Результат был:

Foo::Foo() with counter = 0
Bar::Bar() with counter = 0

Мне кажется, что "__COUNTER__" предоставляется как на единицу компиляции переменная.

Мне бы хотелось иметь глобальный счетчик, работающий во всем коде.

Он используется для тестирования в отладочной сборке, где я хочу достичь этой цели:

Представьте себе, чтоУ меня есть блоки try / catch по всему коду (подсистема или модуль в нескольких файлах .cpp).Во время выполнения программа работает в цикле, в каждом цикле все блоки try будут выполняться в порядке (в каком порядке это не имеет значения), и я хочу проверить, как код реагирует на исключение для каждого try / catch,по одному.Например, первый раз в цикле блок # 1 try / catch генерирует исключение;второй раз в цикле, блок try / catch # 2 генерирует исключение и т. д. и т. д.

Я планирую использовать глобальный счетчик, например:

int g_testThrowExceptionIndex = 0;

В каждом try / catch

try
{
  TEST_THROW_EXCEPTION(__COUNTER__)
  //My logic is here...
}
catch(...)
{
  //Log or notify...
}

И макрос мог бы выглядеть примерно так:

#define TEST_THROW_EXCEPTION(n) \
        if(g_testThrowExceptionIndex == n)\
        {\
          g_testThrowExceptionIndex++;\
          throw g_testThrowExceptionIndex;\
        }\

Без возможности генерировать порядковый номер во время компиляции, я должен написать макрос так:

TEST_THROW_EXCEPTION(THROW_INDEX_1)
......
TEST_THROW_EXCEPTION(THROW_INDEX_N)

И в заголовке определяет:

#define THROW_INDEX_1 0
#define THROW_INDEX_2 1
......

Проблема в том, что каждый раз, когда вы добавляете блок try / catch и хотите протестировать, вы должны создать новую константу через# определить и поместить этот номер в макрос.Хуже того, что если вы удалите некоторые блоки try / catch из кода?Вы также должны обновить свой список #define ...

==============

Решение: Спасибо за идею Сумы, я кое-что закончилкак это:

#if defined(_DEBUG)  && defined(_EXCEPTION_TEST)
  extern int g_testThrowExceptionIndex;
  struct GCounter
  {
    static int counter; // used static to guarantee compile time initialization
    static int NewValue() {return counter++;}
  };
  #define TEST_THROW_EXCEPTION \
      static int myConst = GCounter::NewValue();\
      if(g_testThrowExceptionIndex == myConst)\
      {\
        g_testThrowExceptionIndex++;\
        throw 0;\
      }
#else
  #define TEST_THROW_EXCEPTION 
#endif

В main.cpp:

#if defined(_DEBUG) && defined(_EXCEPTION_TEST)
  int g_testThrowExceptionIndex= 0;
  int GCounter::counter= 0;
#endif

Затем вы можете поместить "TEST_THROW_EXCEPTION" в любой из ваших блоков try / catch, которые вы хотите протестировать.

Ответы [ 2 ]

5 голосов
/ 04 августа 2011

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

struct GCounter
{
  static int counter; // used static to guarantee compile time initialization
  static int NewValue() {return counter++;}
};

int GCounter::counter = 0;

void Foo1()
{
  static int ID1 = GCounter::NewValue();
}

void Foo2()
{
  static int ID2 = GCounter::NewValue();
}

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

1 голос
/ 05 августа 2011

Поскольку вы используете MSVC, вы всегда можете добавить шаг предварительной сборки, который анализирует файлы и расширяет __COUNTER__ до суперглобального значения вместо глобального значения единицы. Конечно, сложная часть заключается в управлении файлами, чтобы не вызывать проблем ...

...