Как я могу генерировать уникальные значения в препроцессоре C? - PullRequest
17 голосов
/ 15 июля 2009

Я пишу несколько связанных макросов препроцессора, один из которых генерирует метки, к которым другой переходит. Я использую их таким образом:

MAKE_FUNNY_JUMPING_LOOP(
  MAKE_LABEL();
  MAKE_LABEL();
)

Мне нужен какой-то способ создания уникальных меток, по одной для каждого внутреннего вызова MAKE_LABEL, с препроцессором. Я пытался использовать __LINE__, но так как я вызываю MAKE_LABEL внутри другого макроса, все они имеют одинаковую строку и метки сталкиваются.

Я бы хотел, чтобы это расширилось:

MAKE_FUNNY_JUMPING_LOOP(
  my_cool_label_1:  // from first inner macro
  ...
  my_cool_label_2:  // from second inner macro
  ...
)

Можно ли с помощью препроцессора генерировать хэши или автоматически увеличивать целые числа?

Ответы [ 6 ]

18 голосов
/ 15 июля 2009

Если вы используете GCC или MSVC, есть __COUNTER__.

Кроме этого, вы могли бы сделать что-то достойное рвоты, например:

#ifndef USED_1
#define USED_1
1
#else
#ifndef USED_2
#define USED_2
2
/* many many more */
#endif
#endif
17 голосов
/ 03 ноября 2009

Я использую это:

#define MERGE_(a,b)  a##b
#define LABEL_(a) MERGE_(unique_name_, a)
#define UNIQUE_NAME LABEL_(__LINE__)

int main()
{
    int UNIQUE_NAME = 1;
    return 0;
}

... и получите следующее:

int main()
{
    int unique_name_8 = 1;
    return 0;
}
15 голосов
/ 15 июля 2009

Как уже отмечалось, __COUNTER__ - это простой, но нестандартный способ сделать это.

Если вам нужна дополнительная переносимость или для других интересных приемов препроцессора, будет работать библиотека Boost Preprocessor *1005* (которая работает как на C, так и на C ++). Например, следующий заголовочный файл будет выводить уникальную метку, где бы она ни была включена.

#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/slot/slot.hpp>

#if !defined(UNIQUE_LABEL)
#define UNIQUE_LABEL
#define BOOST_PP_VALUE 1
#include BOOST_PP_ASSIGN_SLOT(1)
#undef BOOST_PP_VALUE
#else
#define BOOST_PP_VALUE BOOST_PP_INC(BOOST_PP_SLOT(1))
#include BOOST_PP_ASSIGN_SLOT(1)
#undef BOOST_PP_VALUE
#endif


BOOST_PP_CAT(my_cool_label_, BOOST_PP_SLOT(1)):

Пример:

int main(int argc, char *argv[]) {
    #include "unique_label.h"
    printf("%x\n", 1234);
    #include "unique_label.h"
    printf("%x\n", 1234);
    #include "unique_label.h"
    return 0;
}

препроцессирует к

int main(int argc, char *argv[]) {
    my_cool_label_1:
    printf("%x\n", 1234);
    my_cool_label_2:
    printf("%x\n", 1234);
    my_cool_label_3:
    return 0;
}
7 голосов
/ 15 июля 2009

Я не могу придумать способ их автоматической генерации, но вы можете передать параметр в MAKE_LABEL:

#define MAKE_LABEL(n) my_cool_label_##n:

Тогда ...

MAKE_FUNNY_JUMPING_LOOP(
  MAKE_LABEL(0);
  MAKE_LABEL(1);
)
0 голосов
/ 16 июля 2009

Вы могли бы сделать это:

#define MAKE_LABEL() \
do {                 \   
my_cool_label:       \
/* some stuff */;    \
goto my_cool_label;  \
/* other stuff */;   \
} while (0) 

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

Если вы хотите, чтобы метки были доступныболее глобально, неясно, как ваш макрос "MAKE_FUNNY_JUMPING_LOOP" ссылается на эти метки.Вы можете объяснить?

0 голосов
/ 15 июля 2009

Это кажется невозможным со стандартным препроцессором, хотя вы могли бы подделать его, поместив параметры в MAKE_LABEL или MAKE_FUNNY_JUMPING_LOOP, и использовать вставку токена для создания метки.

Ничто не мешает вам создать собственный сценарий предварительной обработки, который выполняет автоматическое увеличение за вас. Однако в этом случае это не будет стандартный файл C / C ++.

Список доступных команд: http://www.cppreference.com/wiki/preprocessor/start

...