определение макроса, содержащее директиву #include

Есть ли способ определить макрос, который содержит директиву #include в своем теле?

Если просто поставить "#include", выдает ошибку

C2162: "expected macro formal parameter"

поскольку здесь я не использую # для объединения строк.
Если я использую "\# include", я получаю следующие две ошибки:

error C2017: illegal escape sequence
error C2121: '#' : invalid character : possibly the result of a macro expansion

Любая помощь?


person Bing Jian    schedule 05.11.2008    source источник


Ответы (9)


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

Поймите, что директивы препроцессора ничего не делают внутри макроса, однако они БУДУТ что-то делать в файле. Таким образом, вы можете вставить блок кода, который хотите изменить, в файл, думая о нем как об определении макроса (с фрагментами, которые могут быть изменены другими макросами), а затем #include этот файл псевдомакроса в различных местах (make конечно, у него нет охранников!). Он не ведет себя точно так же, как макрос, но он может дать довольно похожие на макрос результаты, поскольку #include просто выгружает содержимое одного файла в другой.

Например, рассмотрите возможность включения большого количества заголовков с одинаковыми именами, которые входят в группы. Утомительно записывать их все, или, возможно, они даже генерируются автоматически. Вы можете частично автоматизировать их включение, сделав что-то вроде этого:

Заголовок вспомогательных макросов:

/* tools.hpp */

#ifndef __TOOLS_HPP__
#def __TOOLS_HPP__

// Macro for adding quotes
#define STRINGIFY(X) STRINGIFY2(X)    
#define STRINGIFY2(X) #X

// Macros for concatenating tokens
#define CAT(X,Y) CAT2(X,Y)
#define CAT2(X,Y) X##Y
#define CAT_2 CAT
#define CAT_3(X,Y,Z) CAT(X,CAT(Y,Z))
#define CAT_4(A,X,Y,Z) CAT(A,CAT_3(X,Y,Z))
// etc...

#endif

Псевдомакрофайл

/* pseudomacro.hpp */

#include "tools.hpp"
// NO INCLUDE GUARD ON PURPOSE
// Note especially FOO, which we can #define before #include-ing this file,
// in order to alter which files it will in turn #include.
// FOO fulfils the role of "parameter" in this pseudo-macro.

#define INCLUDE_FILE(HEAD,TAIL) STRINGIFY( CAT_3(HEAD,FOO,TAIL) )

#include INCLUDE_FILE(head1,tail1.hpp) // expands to #head1FOOtail1.hpp
#include INCLUDE_FILE(head2,tail2.hpp)
#include INCLUDE_FILE(head3,tail3.hpp)
#include INCLUDE_FILE(head4,tail4.hpp)
// etc..

#undef INCLUDE_FILE

Исходный файл

/* mainfile.cpp */

// Here we automate the including of groups of similarly named files

#define FOO _groupA_
#include "pseudomacro.hpp"
// "expands" to: 
// #include "head1_groupA_tail1.hpp"
// #include "head2_groupA_tail2.hpp"
// #include "head3_groupA_tail3.hpp"
// #include "head4_groupA_tail4.hpp"
#undef FOO

#define FOO _groupB_
#include "pseudomacro.hpp"
// "expands" to: 
// #include "head1_groupB_tail1.hpp"
// #include "head2_groupB_tail2.hpp"
// #include "head3_groupB_tail3.hpp"
// #include "head4_groupB_tail4.hpp"
#undef FOO

#define FOO _groupC_
#include "pseudomacro.hpp"
#undef FOO

// etc.

Эти включения могут быть даже в середине блоков кодов, которые вы хотите повторить (с измененным FOO), как ответ запросов Bing Jian: определение макроса, содержащее директиву #include

Я не использовал этот трюк широко, но он выполняет свою работу. Очевидно, его можно расширить, чтобы иметь столько «параметров», сколько необходимо, и вы можете запускать там любые команды препроцессора, которые вам нравятся, а также генерировать фактический код. Вы просто не можете использовать материал, который он создает, в качестве входных данных в другой макрос, как вы можете с обычными макросами, поскольку вы не можете вставить включение в макрос. Но это может быть внутри другого псевдомакроса :).

У других могут быть комментарии по другим ограничениям и тому, что может пойти не так :).

person Ben Farmer    schedule 07.01.2015
comment
Звучит здорово, но теоретически это не определено в стандарте и зависит от реализации компилятора. См. stackoverflow.com/questions/20524491/ - person Antonio; 24.08.2016
comment
Чудесный. Что я действительно хотел бы сделать, так это добавить #define во включение, чтобы иметь макросы, определяющие макросы. Такие вещи, как Openssl STACK_OF, выиграют от этого. Это давно должно было быть в стандарте C, но лобби макросов, похоже, победило. Поэтому вместо этого мы используем Perl, вряд ли меньшее зло. - person Tuntable; 19.05.2017
comment
Хм, ты можешь, не так ли? Вы можете определить все, что хотите, во включенном файле, и он должен работать как обычно. - person Ben Farmer; 19.05.2017

Я не буду спорить о его достоинствах, но freetype (www.freetype.org) делает следующее:

#include FT_FREETYPE_H

где они определяют FT_FREETYPE_H в другом месте

person Dan Hewett    schedule 05.11.2008
comment
Это явно разрешено стандартами и, следовательно, является переносимым, пока FT_FREETYPE_H расширяется либо до формы ‹header.h›, либо до формы header.h. - person Jonathan Leffler; 06.11.2008
comment
+1, так как это позволяет избежать зависимости от поведения, определяемого реализацией, даже если это означает очень доверять вашей системе сборки. - person einpoklum; 11.03.2017

Языки C и C++ явно запрещают формирование директив препроцессора в результате раскрытия макросов. Это означает, что вы не можете включить директиву препроцессора в список замены макросов. И если вы попытаетесь обмануть препроцессор, «построив» новую директиву препроцессора с помощью конкатенации (и подобных трюков), поведение не будет определено.

person AnT    schedule 31.08.2010

Я считаю, что препроцессор C/C++ выполняет только один проход по коду, поэтому я не думаю, что это сработает. Вы могли бы получить "#include" для размещения в коде с помощью макроса, но компилятор задохнется от него, так как он не знает, что с этим делать. Для того, что вы пытаетесь сделать, препроцессор должен будет выполнить второй проход по файлу, чтобы подобрать #include.

person Herms    schedule 05.11.2008

Я тоже хотел это сделать, и вот причина:

Некоторые заголовочные файлы (особенно mpi.h в OpenMPI) работают иначе, если вы компилируете на C или C++. Я ссылаюсь на код C MPI из моей программы на C++. Чтобы включить заголовок, я делаю обычно:

extern "C" {
#include "blah.h"
}

Но это не работает, потому что __cplusplus все еще определено даже в компоновке C. Это означает, что mpi.h, который включается blah.h, начинает определять шаблоны, и компилятор умирает, говоря, что вы не можете использовать шаблоны с привязкой к C.

Следовательно, что мне нужно сделать в blah.h, так это заменить

#include <mpi.h>

с участием

#ifdef __cplusplus
#undef __cplusplus
#include <mpi.h>
#define __cplusplus
#else
#include <mpi.h>
#endif

Примечательно, что не только mpi.h делает эту патологическую вещь. Следовательно, я хочу определить макрос INCLUDE_AS_C, который делает то же самое для указанного файла. Но я думаю, это не работает.

Если кто-то может найти другой способ выполнить это, пожалуйста, дайте мне знать.

person Lutorm    schedule 31.08.2010
comment
Псевдо-макросы, которые я описываю в своем ответе, позволят вам это сделать :). Синтаксис не такой приятный, как у обычного макроса, но работать будет :). В итоге вы получите что-то вроде #define FILENAME ‹mpi.h›, #include include_as_c.hpp, #undef FILENAME с вашим особым способом включения внутри include_as_c.hpp. stackoverflow.com/questions/266501/< /а> - person Ben Farmer; 08.01.2015

Я думаю, вы правы в том, что эта задача кажется невыполнимой, как я также понял из

http://groups.google.com/group/comp.lang.c++/browse_thread/thread/03d20d234539a85c#

Нет, директивы препроцессора в C++ (и C) не отражают.

Павел Дзепак

В любом случае, причина этой попытки заключается в том, что я пытаюсь сделать следующий многократно используемый фрагмент кода макросом:

void foo(AbstractClass object)
{
    switch (object.data_type())
    {
    case AbstractClass::TYPE_UCHAR :
        {
        typedef unsigned char PixelType;
        #include "snippets/foo.cpp"
        }
        break;
    case AbstractClass::TYPE_UINT:
        {
        typedef unsigned int PixelType;
        #include "snippets/foo.cpp"
        }
        break;
    default:
        break;
    }
}

Для другой задачи мне нужно иметь аналогичную функцию

void bar(AbstractClass object)

где я размещу

#include "snippets/bar.cpp"

и, конечно же, именно в «snippets/foo.cpp» и «snippets/bar.cpp» написан код для конкретной задачи.

person Bing Jian    schedule 05.11.2008
comment
Вероятно, вам следует взглянуть на шаблоны (как предложил stbuton). Вы, безусловно, должны стремиться избежать необходимости делать это. - person Jonathan Leffler; 06.11.2008
comment
как правило, вы, вероятно, должны помещать все свои операторы #include в одно и то же место (обычно вверху). это делает поддержку и отладку вашего кода НАМНОГО проще. - person helloandre; 06.11.2008
comment
У меня немного сумасшедший способ делать то, что вы хотите (используя препроцессор); см. мой ответ :) stackoverflow.com/a/27830271/1447953 - person Ben Farmer; 08.01.2015

Почему макрос должен иметь #include? если вы включаете #include любой файл, в котором находится макрос, вы можете просто поместить #include над макросом со всеми остальными операторами #include, и все должно быть красиво и красиво.

Я не вижу причин включать в макрос что-либо, что нельзя просто включить в файл.

person helloandre    schedule 05.11.2008

Я понятия не имею, что вы на самом деле пытаетесь сделать, но похоже, что вам может понадобиться шаблонная функция.

Таким образом, PixelType является просто параметром шаблона для блока кода.

person user21714    schedule 05.11.2008

Заразный — это правильно, если вы делаете:

мой файл.c:

#include "standardAppDefs.h"
#myStandardIncludeMacro

стандартныйAppDefs.h:

#define myStandardIncludeMacro #include <foo.h>

Почему бы просто не сказать:

мой файл.c:

#include "standardAppDefs.h"

стандартныйAppDefs.h:

#include <foo.h>

И забыть макросы?

person HanClinto    schedule 05.11.2008
comment
Это не работает, и #myStandardIncludeMacro является ошибкой синтаксического анализа, если я не ошибаюсь. - person RecursiveExceptionException; 07.10.2018