Макрос C — динамический #include

Я пытаюсь понять, как создать переменную строку для оператора #include с помощью GCC.

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

Создать этот файл не проблема. В том числе он, к сожалению, есть.

Что у меня есть до сих пор (identities.h):

// identities.h

# define PASTER2(str)  #str
# define PASTER(str)   PASTER2(str ## .iden)
# define EVALUATOR(x)  PASTER(x)

# define IDENTITIES_FILE EVALUATOR(__FILE__)
# include IDENTITIES_FILE

В идеале это можно было бы использовать так (main.c):

//main.c

# include "identities.h"

int main() {return 0;}

Который будет расширен за один проход препроцессором перед компиляцией, чтобы получить:

//main.c (preprocessed)

# include "main.c.iden"

int main() {return 0;}

Два уровня косвенности, которые я использую (PASTER и EVALUATOR), являются результатом this< /а> сообщение.

К сожалению, это не работает, и я остаюсь с ошибкой:

obj/win32/dbg/main.o
In file included from main.c:1:0:
identities.h:42:1: error: #include expects "FILENAME" or <FILENAME>

Я думаю, проблема в том, что в операторе include отсутствуют кавычки. Любые идеи?


person J T    schedule 03.05.2011    source источник
comment
Если у вас есть динамически сгенерированный исходный код C, можете ли вы динамически сгенерировать новый заголовочный файл из всех заголовочных файлов, которые вы хотите включить? Хотя мне все еще любопытно посмотреть, возможно ли сделать то, что вы хотите сделать.   -  person leetNightshade    schedule 03.05.2011
comment
Краткий ответ: вы не можете этого сделать, и точка.   -  person Alexandre C.    schedule 03.05.2011
comment
@leet: Нет, проблема требует абсолютно минималистского подхода к изменению текущей системы сборки. Было решено, что мы попытаемся сделать это, отредактировав каждый файл, чтобы включить один общий заголовок identities.h - и только этот один файл.   -  person J T    schedule 03.05.2011
comment
@АлександрК. Странно, я могу это сделать. Я не должен существовать в вашей вселенной ;-). Я думаю, вы имели в виду, что вы не можете сделать это с помощью стандартного C/C++.   -  person artless noise    schedule 13.12.2013


Ответы (5)


Как насчет BOOST_PP_STRINGIZE из библиотеки препроцессора Boost . Это специально сделано для добавления кавычек вокруг имени.

person Mikael Persson    schedule 03.05.2011
comment
К сожалению, это всего лишь добавляет фунт перед текстом: # define BOOST_PP_STRINGIZE_I(text) #text - person J T; 03.05.2011
comment
BOOST_PP_STRINGIZE не просто добавляет фунт #. В документе конкретно говорится, что в отличие от простого использования #, этот макрос фактически позволяет расширить свой аргумент (а # - нет). После нескольких тестов выяснилось, что ваша основная проблема заключается в том, что FILE расширяется до имени файла, которое уже находится в кавычках, и удалить их невозможно (по крайней мере, насколько я знаю). - person Mikael Persson; 03.05.2011
comment
@Mikael: Вы правы, я только что сам пришел к такому выводу. - person J T; 04.05.2011
comment
Я предлагаю вам решить эту проблему с помощью системы сборки (например, cmake) или с помощью домашнего скрипта. Вот как Qt генерирует заголовки, например, для файлов .ui. - person Mikael Persson; 04.05.2011
comment
Да, я использовал GCC и Make: gcc -c -DEVALUTOR_ARGUMENT=$‹ $‹ - person J T; 04.05.2011
comment
Вау, это красиво и просто! Очень хорошо. - person Mikael Persson; 04.05.2011

На самом деле это делается в дереве исходных текстов Linux; См. строку 100 компилятора. gcc.h.

#define __gcc_header(x) #x
#define _gcc_header(x) __gcc_header(linux/compiler-gcc##x.h)
#define gcc_header(x) _gcc_header(x)
#include gcc_header(__GNUC__)

Я пытаюсь понять, как создать переменную строку для оператора #include с помощью GCC.

Этот токен вставляет значение __GNUC__ в строку; "linux/compiler-gcc" __GNUC__ ".h", а затем упорядочивает результат. Возможно, это расширение препроцессора gcc.

Вот пример,

t1.h

#define FOO 10

t2.h

#define FOO 20

a.c

#ifndef VERSION
#define VERSION 1
#endif
#define __gcc_header(x) #x
#define _gcc_header(x) __gcc_header(t##x.h)
#define gcc_header(x) _gcc_header(x)
#include gcc_header(VERSION)
#include <stdio.h>

int main(void)
{
        printf("FOO is %d\n", FOO);
        return 0;
}

Вот две компиляции,

g++ -o a a.cc
g++ -DVERSION=2 -o a a.cc

Вывод любой компиляции дает ожидаемый результат.

Как и в случае с исходным кодом Linux, вы можете отключить предварительно определенные значения gcc. echo | g++ -dM -E - даст список.

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

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

person artless noise    schedule 12.12.2013
comment
Вы не можете использовать __FILE__, конечно, как указывали другие. В C++ и более позднем C это указатель, а не строковый литерал (согласно arelius). - person artless noise; 12.12.2013
comment
Linux объединил файлы и теперь просто использует условия препроцессора для проверки. Вот ссылка на старую версию. Другая возможность — has_include extensions в обоих новых GCC и лязг. - person artless noise; 17.06.2016

Я совершенно уверен, что вы не можете делать то, что хотите, __FILE__ возвращает строку, а ## работает с токенами, а макрос препроцессора concat строки CPP отсутствует. Обычно это обходится из-за того, что две строки подряд, например.

"Hello" " World"

будет обрабатываться синтаксическим анализатором C++ как одна строка, однако #include является частью препроцессора и, следовательно, не может использовать этот факт.

Старый ответ:

Почему вы это делаете

{ #str, str ## .iden }

Я уверен, что это не синтаксис препроцессора, чего вы надеетесь достичь с помощью этого? Вы пробовали только:

str ## .iden

'{' может объяснить ошибку, которую вы получаете.

person Arelius    schedule 03.05.2011
comment
К сожалению, я пробовал последний подход ранее и получил ту же ошибку. Подход, который я использую, взят из документации GCC: tigcc.ticalc.org/doc/cpp. html#SEC18 - person J T; 03.05.2011

Пропуская весь синтаксис включения на некоторое время, я не понимаю, что ваш код пытается сделать. Ты говоришь:

# define PASTER(str)  { #str, str ## .iden }

Вы даете ему main.c и ожидаете "main.c.iden", но возвращает {"main.c", main.c.iden }.

Вместо этого вы ищете это?

#define PASTER2(str) #str
#define PASTER(str) PASTER2(str ## .iden)
person Blindy    schedule 03.05.2011
comment
Вы правы, и я изменил его, хотя, к сожалению, это дает ту же ошибку. - person J T; 03.05.2011

Вы не можете использовать препроцессор таким образом. Вы должны указать имя файла в директиве #include, это не может быть какой-то другой макрос.

person ognian    schedule 03.05.2011
comment
Я знаю, что это неверно (поиск вычисляемых включений): tigcc.ticalc.org/doc/cpp.html - person J T; 03.05.2011
comment
@JT Это особенность препроцессора GCC - она ​​не является частью стандартов C или C ++ для предварительной обработки. - person ; 03.05.2011
comment
@JT, на самом деле он во многом прав, вычисляемые включения являются расширением GCC, и вы не пометили свой вопрос как только для GCC. - person Blindy; 03.05.2011
comment
Извиняюсь за отсутствие тегов, хотя в вопросе я указал, что использую GCC. - person J T; 03.05.2011
comment
Если вы хотите полагаться на расширение GNU, вы должны удалить скобки области действия из своего макроса. Однако старайтесь оставаться портативным, это окупается - person ognian; 03.05.2011
comment
Фактически вычисляемые включения (или некоторые их формы) разрешены стандартом C --- стандартом C99. См. 6.10.2/4 ... "# include pp-tokens newline", где pp-токены должны расширяться до одного из <header> или "header-file". - person pmg; 03.05.2011