c++ macros include boost-preprocessor

c++ - Construya la ruta para la directiva#include con macro



macros boost-preprocessor (4)

Me gustaría incluir rutas de archivos creadas dinámicamente por una macro para una parte de mi programa que depende de la configuración de destino.

Por ejemplo, me gustaría construir una macro que se invocaría así:

#include TARGET_PATH_OF(header.h)

Lo que se expandirá a algo como esto:

#include "corefoundation/header.h"

cuando la fuente está configurada (en este caso) para OSX

Hasta ahora todos los intentos han fallado. ¿Espero que alguien haya hecho esto antes?

ejemplo de lo que no funciona:

#include <iostream> #include <boost/preprocessor.hpp> #define Dir directory/ #define File filename.h #define MakePath(f) BOOST_PP_STRINGIZE(BOOST_PP_CAT(Dir,f)) #define MyPath MakePath(File) using namespace std; int main() { // this is a test - yes I know I could just concatenate strings here // but that is not the case for #include cout << MyPath << endl; }

errores:

./enableif.cpp:31:13: error: pasting formed ''/filename'', an invalid preprocessing token cout << MyPath << endl; ^ ./enableif.cpp:26:16: note: expanded from macro ''MyPath'' #define MyPath MakePath(File) ^ ./enableif.cpp:25:40: note: expanded from macro ''MakePath'' #define MakePath(f) BOOST_PP_STRINGIZE(BOOST_PP_CAT(Dir,f)) ^ /usr/local/include/boost/preprocessor/cat.hpp:22:32: note: expanded from macro ''BOOST_PP_CAT'' # define BOOST_PP_CAT(a, b) BOOST_PP_CAT_I(a, b) ^ /usr/local/include/boost/preprocessor/cat.hpp:29:36: note: expanded from macro ''BOOST_PP_CAT_I'' # define BOOST_PP_CAT_I(a, b) a ## b ^ 1 error generated.


Me gustaría incluir rutas de archivos creadas dinámicamente por una macro para una parte de mi programa que depende de la configuración de destino.

No debería poder hacerlo (y si puede hacerlo, probablemente no debería hacerlo).

Está intentando efectivamente hacer el trabajo del compilador en un archivo fuente, lo que no tiene mucho sentido. Si desea cambiar las rutas de acceso basadas en la máquina en la que compila, este es un problema resuelto (pero no resuelto en un archivo de encabezado).

Solución canónica:

Use un IF en su Makefile o CMakeLists.txt, use páginas de propiedades personalizadas dependiendo de la configuración de compilación en Visual Studio (o simplemente establezca la configuración particular para su compilación en el entorno del sistema operativo para su usuario).

Luego, escriba la directiva include como:

#include <filename.h> // no path here

y confíe en el entorno / sistema de compilación para que la ruta esté disponible cuando se invoque el compilador.


Esto funciona para VS2013. (Se puede hacer más fácil, por supuesto).

#define myIDENT(x) x #define myXSTR(x) #x #define mySTR(x) myXSTR(x) #define myPATH(x,y) mySTR(myIDENT(x)myIDENT(y)) #define myLIBAEdir D://Georgy//myprojects//LibraryAE//build//native//include// //here whitespace! #define myFile libae.h #include myPATH(myLIBAEdir,myFile)


Según su descripción, parece que descubrió que no todos los "" son una cadena. En particular, #include "corefoundation/header.h" parece una cadena normal pero no lo es. Gramaticalmente, el texto citado fuera de las directivas de preprocesador está destinado al compilador y se compila en literales de cadena terminados en nulo. El texto citado en las directivas del preprocesador es interpretado por el preprocesador de una manera definida por la implementación.

Dicho esto, el error en su ejemplo se debe a que Boost pegó el segundo y tercer token: / y filename . El primer, cuarto y quinto token ( directory h Y h ) no se modifican. Esto no es lo que querías, obviamente.

Es mucho más fácil confiar en la concatenación automática de cadenas. "directory/" "filename" es el mismo literal de cadena que "directory/filename" Tenga en cuenta que no hay + entre los dos fragmentos.


Tiendo a estar de acuerdo con el comentario en la respuesta de utnapistim de que no debes hacer esto aunque puedas. Pero, de hecho, puede hacerlo, con compiladores de C conformes al estándar. [Nota 1]

Hay dos problemas a superar. La primera es que no puede usar el operador ## para crear algo que no sea un token de preprocesador válido, y los nombres de ruta no califican como tokens de preprocesador válidos porque incluyen / y . caracteres. (El . Estaría bien si el token comenzara con un dígito, pero el / nunca funcionará).

En realidad, no es necesario concatenar tokens para encadenarlos con el operador # , ya que ese operador encadenará un argumento macro completo, y el argumento puede consistir en múltiples tokens. Sin embargo, stringify respeta los espacios en blanco [Nota 2], por lo que STRINGIFY(Dir File) no funcionará; dará como resultado "directory/ filename.h" y el espacio extraño en el nombre de archivo hará que #include falle. Por lo tanto, debe concatenar Dir y File sin espacios en blanco.

Lo siguiente resuelve el segundo problema mediante el uso de una macro de función que simplemente devuelve su argumento:

#define IDENT(x) x #define XSTR(x) #x #define STR(x) XSTR(x) #define PATH(x,y) STR(IDENT(x)IDENT(y)) #define Dir sys/ #define File socket.h #include PATH(Dir,File)

Advertencia : (Gracias a @jed por pasar este problema). Si las cadenas que se concatenan contienen identificadores que se definen en otras partes como macros, entonces se producirá una sustitución de macro inesperada aquí. Se debe tener precaución para evitar este escenario, particularmente si Dir y / o File no están controlados (por ejemplo, al definirse como un parámetro de línea de comandos en la invocación del compilador).

También debe tener en cuenta que algunas implementaciones pueden definir palabras que probablemente aparezcan de forma simbólica en una ruta de archivo. Por ejemplo, GCC puede definir macros con nombres como unix y linux menos que se invoque con un estándar C explícito (que no es el predeterminado). Eso podría ser provocado por rutas como platform/linux/my-header.h o incluso linux-specific/my-header.h .

Para evitar estos problemas, te recomiendo que si utilizas este truco:

  • utiliza una configuración de compilador conforme a los estándares C (o C11), y

  • coloca la secuencia muy temprano en su archivo fuente, idealmente antes de incluir cualquier otro encabezado, o al menos cualquier encabezado fuera de la biblioteca estándar.

Además, no necesitaría la complicación de la macro IDENT si pudiera escribir la concatenación sin espacios. Por ejemplo:

#define XSTR(x) #x #define STR(x) XSTR(x) #define Dir sys #define File socket.h #include STR(Dir/File)

Notas

  1. Lo probé con clang, gcc e icc, como está disponible en godbolt . No sé si funciona con Visual Studio.

  2. Más exactamente, respeta parcialmente el espacio en blanco: el espacio en blanco se convierte en un solo carácter de espacio.