Динамически префикс имени макроса с помощью макроса variadic

Фон

Я использовал набор макросов препроцессора из другого вопроса, который позволяет мне ставить префиксы имен символов (перечисления, имена функций, имена структур и т. Д.) В моем источнике, то есть:

#include <stdio.h>
#define VARIABLE 3
#define PASTER(x,y) x ## _ ## y
#define EVALUATOR(x,y)  PASTER(x,y)
#define NAME(fun) EVALUATOR(fun, VARIABLE)

void NAME(func)(int i);

int main(void)
{
    NAME(func)(123);

    return 0;
}

void NAME(func)(int i)
{
    printf("i is %d in %s.\n", i, __func__);
}

проблема

Это работает, как и ожидалось, со следующим выводом: i is 123 in func_3.

редактировать

Я хотел бы этот код:

#define NAME(SOME_MACRO_CONST)  (123)
#define NAME(SOME_MACRO_CONST2) (123)

Расширить до:

#define 3SOME_MACRO_CONST  (123)
#define 3SOME_MACRO_CONST2 (123)

Я понимаю, что макрос не должен начинаться с цифры. В конечном коде я буду использовать такие имена, как LIB_A_ а также LIB_B_ в качестве префиксов.

/Редактировать

Однако, если я попытаюсь сделать то же самое с макросами в качестве аргументов моего NAME макрос variadic не работает так:

Повторное использование NAME макрос:

Код

#define NAME(MY_CONST)  (3)

Выход

test.c:7:0: warning: "NAME" redefined
 #define NAME(MY_CONST) 3

Вставка префикса вручную:

Код:

#define VARIABLE ## MY_CONST    (3)

Выход:

test.c:8:18: error: '##' cannot appear at either end of a macro expansion
 #define VARIABLE ## MY_CONST (3)

Вопрос

Как я могу создать простые определения макросов (имя + значение), которые имеют общий префикс для всех макросов? Цель состоит в том, чтобы иметь возможность сделать несколько копий исходного файла и скомпилировать их с разными флагами, чтобы все версии можно было связать вместе в один и тот же конечный двоичный файл без коллизий имен символов / макросов (макросы позже будут перемещены в заголовочные файлы). Конечный файл будет слишком большим, чтобы писать что-то вроде M4 или языка шаблонов. В идеале решение должно включать возможность использования одной макрофункции /variadic-макроса для всех случаев использования, но я в порядке с одним макросом для префикса символа, а другим - для префикса имени макроса.

1 ответ

Решение

Я хотел бы этот код:

 #define NAME(SOME_MACRO_CONST)  (123)
 #define NAME(SOME_MACRO_CONST2) (124)

Расширить до:

 #define 3SOME_MACRO_CONST  (123)
 #define 3SOME_MACRO_CONST2 (124)

(Я исправил второе число до 124, чтобы оно отличалось от первого для удобства чтения)

Это невозможно с препроцессором C

по нескольким причинам:

  • 3SOME_MACRO_CONST не является допустимым идентификатором (как для препроцессора, так и для самого компилятора C), поскольку он не начинается с буквы или подчеркивания. Итак, давайте предположим, что вы хотите, чтобы ваш код был расширен до:

      /// new desired expansion
      #define THREE_SOME_MACRO_CONST  (123)
      #define THREE_SOME_MACRO_CONST2 (124)
    
  • это все еще невозможно, потому что препроцессор работает раньше всего и не может генерировать никаких директив препроцессора (например, #define).

Обходной путь, если вы хотите только #define некоторые числа (вычисляемые во время компиляции!!!) могут быть расширены до некоторого анонимного enum лайк

 enum {
   THREE_SOME_MACRO_CONST= 123,
   THREE_SOME_MACRO_CONST2= 124,
 };

и вы знаете, как сделать это в деталях. Читайте также о X-макросах.


Тем не менее, даже если вы можете изменить свое требование на что-то, что возможно, это может быть не рекомендуется, потому что ваш код становится очень нечитаемым (IMHO). Иногда вы можете написать какой-нибудь простой скрипт (например, в sed или же awk...), или используйте какой-нибудь другой препроцессор, такой как GPP, для создания файла C из чего-то другого.

Обратите внимание, что наиболее серьезные инструменты автоматизации сборки (такие как GNU make или ninja) - или даже IDE (их можно настроить) позволяют довольно легко (путем добавления дополнительных целей, рецептов, команд и т. Д.) Генерировать C (или C++) из какого-то другого файла, и эта практика метапрограммирования регулярно используется на протяжении десятилетий (например, bison, flex, autoconf, rpcgen, Qt). moc, SWIG...) так что я удивлен, что вы не можете сделать это. Создание файла заголовка, содержащего много #define Это настолько распространенная практика, что я удивлен, что вам запрещено это делать. Возможно, вам просто нужно обсудить с вашим менеджером или коллегами. Может быть, вам нужно искать более интересную работу.

Лично мне очень нравятся такие подходы метапрограммирования (я защитил докторскую диссертацию по ним в 1990 году и обсуждал их на каждом собеседовании; работа, в которой метапрограммирование запрещено, не для меня. Посмотрите, например, на мой прошлый GCC Проект MELT, и мой будущий проект также будет иметь метапрограммирование). Еще один способ продвижения этого подхода - защитить языки, специфичные для предметной области (и возможность сделать свой DSL внутри какого-то большого программного проекта; например, компилятор GCC содержит около дюжины таких DSL…). Затем ваш DSL может (естественно) быть скомпилирован в C, что является обычной практикой. В современных операционных системах, которые генерировали C-код, можно скомпилировать во время выполнения и динамически загрузить как (сгенерированный) плагин (используя dlopen в POSIX...)


Иногда вы можете обмануть компилятор. Для проекта, скомпилированного GCC, вы можете подумать о написании своего плагина GCC..... (это намного больше, чем добавление команды, генерирующей код на C; ваш плагин может предоставить дополнительные магические прагмы или встроенные функции или атрибуты, используемые некоторыми другими макросами).

Вы также можете настроить файл спецификации вашего gcc специально обрабатывать некоторые C-файлы. Осторожно, это может повлиять на любой будущий сборник!

Другие вопросы по тегам