Расширение макроса препроцессора в другую директиву препроцессора

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

Может ли макрос препроцессора, например, в

#include "MyClass.h"

INSTANTIATE_FOO_TEMPLATE_CLASS(MyClass)

расшириться до другого включения, как в

#include "MyClass.h"

#include "FooTemplate.h"
template class FooTemplate<MyClass>;

?


person moala    schedule 11.08.2009    source источник
comment
Однажды вы захотите перенести свой код в ОС, такую ​​как Linux, где регистр имеет значение (например, Foo.h и foo.h — два разных файла), и тогда все орфографические ошибки, которые не обнаружены в Windows, вернутся домой. в качестве альтернативы, если вы человек Linux, однажды вы захотите пойти другим путем, с другими, но такими же ужасными проблемами.   -  person    schedule 11.08.2009
comment
Я согласен с правилом всегда в нижнем регистре для имен файлов. Я написал это так, потому что хотел исключить проблему преобразования нижнего регистра (имя класса -> имя файла). Но это стоит упомянуть, спасибо. +1!   -  person moala    schedule 11.08.2009
comment
Наше правило состоит в том, что имена файлов точно соответствуют схеме именования наших типов и функций. Здесь мы поступаем точно так же, как и вопрошающий, имя типа FooType будет определено в FooType.h. Как и в любом руководстве по стилю, выберите стиль и придерживайтесь его. Сказав, что мы активно разрабатываем Linux, поэтому это правило автоматически применяется для нас ОС.... хммммм.....   -  person Richard Corden    schedule 12.08.2009
comment
Ok. Это была ошибка. Мне не нравятся заглавные буквы в именах файлов, но мне нравятся заглавные буквы в именах типов. Но я должен был написать обе строчные буквы только для вопроса. @Richard Corden: соблюдение правила файловой системой и компилятором может быть временным, и любой перенос на другие ОС полностью стирает эту безопасность. По крайней мере, с тоталитарным правилом имени файла в нижнем регистре рискованное поведение и ошибки видны сразу, до того, как возникнет какая-либо проблема. Что я предпочитаю.   -  person moala    schedule 12.08.2009
comment
@моала. Подумав об этом немного подробнее, я понял, что независимо от того, что вы делаете (соответствие нижнего регистра или смешанного регистра), это должно быть принудительно выполнено какой-либо внешней утилитой. Всегда есть шанс, что кто-то совершит ошибку, и если у вас нет поддержки ОС (как в моем случае), вам нужно проверить это с помощью какого-либо скрипта/инструмента. Легче проверить все строчные буквы, но не так сложно проверить, что имя совпадает хотя бы с одним идентификатором в файле. ‹ blaent plug › Возможно, у вас даже есть инструмент статического анализа, который сделает всю работу за вас!!! ;) ‹ / тупая вилка ›.   -  person Richard Corden    schedule 12.08.2009


Ответы (3)


Я считаю, что это невозможно, потому что препроцессор однопроходный. Поэтому он не может выдавать другие директивы препроцессора.

В частности, из стандарта C99 (6.10.3.4, пункт 3):

3 Результирующая последовательность токенов предварительной обработки, полностью замененная макросом, не обрабатывается как директива предварительной обработки, даже если она похожа на таковую, ...

Интересно, что именно поэтому в c99 был добавлен унарный оператор _Pragma. Потому что #pragma нельзя было сгенерировать макросами, а _Pragma можно.

person Evan Teran    schedule 11.08.2009
comment
Ну, конечно, он может испускать директивы. чего он не может сделать, так это обработать их в одном и том же проходе препроцессора. - person ; 11.08.2009
comment
Поскольку символы # и ## имеют особое значение в макросах, я не понимаю, как вы могли бы на самом деле выдать директиву... - person Evan Teran; 11.08.2009
comment
На самом деле препроцессор GCC, похоже, разрешает #define X #ifdef X, где пробел между #ifdef и вторым X на самом деле является новой строкой, и это фактически выдает #ifdef, когда вы запускаете cpp на нем. Кто-то еще может проверить это, так как я только что выпил пару бутылок пива :-) - person ; 11.08.2009
comment
Да, кажется, что можно создать что-то похожее на директиву, но в стандарте специально указано (см. мое редактирование), что такая конструкция не обрабатывается как директива. - person Evan Teran; 11.08.2009
comment
Да, я не предполагал, что ОП может делать то, что он хочет. - person ; 11.08.2009

Стандарт C говорит следующее о директивах предварительной обработки (C99 - 6.10(2) - Директивы предварительной обработки):

Директива предварительной обработки состоит из последовательности токенов предварительной обработки, которая начинается с токена предварительной обработки #, который (в начале фазы трансляции 4) ...

и (С99-6.10(7)):

Маркеры предварительной обработки в директиве предварительной обработки не подлежат макрорасширению, если не указано иное.

ПРИМЕР В:

#define EMPTY
EMPTY # include <file.h>

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

Итак, нет, макросы не могут расширяться в директиву предварительной обработки '#include'. Эти директивы должны быть на месте в начале фазы трансляции 4 (при обработке этих директив происходит предварительная обработка). Поскольку расширение макроса происходит во время фазы 4, макросы не могут создать что-либо в начале фазы 4.

Однако я хотел бы отметить, что следующее действительно работает:

#ifdef WIN32
#define PLATFORM_HEADER "platform/windows/platform.h"
#else
#define PLATFORM_HEADER "platform/linux/platform.h"

#include PLATFORM_HEADER

потому что стандарт C говорит об этом (C99, 6.10.2 (4) - включение исходного файла):

Предварительная обработка директивы формы

# include pp-tokens new-line

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

person Michael Burr    schedule 11.08.2009
comment
я просмотрел эту часть стандарта, но я не думаю, что это самый важный раздел. Пример не тот, который пытается выдать директиву. Вместо этого это пример директивы, которой не предшествует пробел (даже несмотря на то, что макрос EMPTY разрешается в пробел). - person Evan Teran; 11.08.2009
comment
Я согласен, что ваша цитата из стандарта имеет более прямое отношение, но ее там не было, когда я ответил. Даже если приведенный выше пример не совпадает с тем, о чем спрашивали, бит о директиве предварительной обработки, которая должна быть на месте в начале фазы 4, также запрещает макросам разворачиваться в полезные директивы предварительной обработки, даже если 6.10.3.4(3) говорит об этом более прямо. (другими словами, даже если я не думаю, что мой ответ неверен, ваш ответ явно лучше). - person Michael Burr; 11.08.2009
comment
Извините, но я должен был выбрать хороший ответ. Еще один +1 к вашему проницательному комментарию, чтобы быть максимально справедливым, надеюсь, вы понимаете. - person moala; 12.08.2009
comment
Не нужно извиняться - ответ Эвана определенно лучше. Не нужно извиняться, даже если это было не так. - person Michael Burr; 12.08.2009

Все директивы препроцессора интерпретируются до начала расширения макроса, поэтому нет, вы не можете расширить макрос в директиву #include и интерпретировать его как таковой. Вместо этого он будет интерпретирован как (ошибочный) код C++.

person John Bode    schedule 11.08.2009
comment
У вас может сложиться такое впечатление, потому что многие директивы отключают раскрытие макросов своих аргументов, но это не так. Расширение макроса происходит одновременно с разбором директивы. Если бы это было правдой, #if не работало бы, а #undef не давало ожидаемого эффекта. - person zwol; 11.07.2010