c++ - Tareas de preprocesador(de cadena de un#include)
opencl c-preprocessor (4)
Esto es lo que hago en xcode C:
const char oursource[60000];
const char * oursourceptr = oursource;
const char * * oursourceptrptr = & oursourceptr;
// in function "readfileintostring":
char *fylnm = "/Developer/projs/myproj/mykernel.cl";
long enby; short pathref;
FSRef dink; FSPathMakeRef( (const UInt8 *) &fylnm, &dink, NULL );
SInt16 forkRefNum; HFSUniStr255 dataForkName; FSGetDataForkName(&dataForkName);
FSOpenFork( &dink, dataForkName.length, dataForkName.unicode, fsRdPerm, (FSIORefNum *) &pathref );
enby = 100000; FSRead( pathref, &enby, (void *) oursourceptr );
// .. then later ..
program = clCreateProgramWithSource(context, 1, (const char **) oursourceptrptr, NULL, &err);
... no es voodoo del preprocesador, pero funciona para mí, puedo ver la sintaxis resaltada en mi archivo .cl, e incluso puedo copiar mi .cl en un .c, cambiar uno #define, y funciona como xcode C ....
Nota: esta pregunta no tiene nada que ver con OpenCL per se ... verifique el último párrafo para una declaración sucinta de mi pregunta. Pero para proporcionar algunos antecedentes:
Estoy escribiendo un código C ++ que hace uso de OpenCL. Me gusta mantener la fuente de mis núcleos OpenCL en sus propios archivos, para facilitar la codificación y el mantenimiento (en lugar de incrustar las fuentes directamente como constantes de cadena en el código C ++ asociado). Esto conduce inevitablemente a la pregunta de cómo cargarlos en el tiempo de ejecución de OpenCL una vez que llega el momento de distribuir los binarios. Lo ideal es que la fuente de OpenCL esté incluida en el binario, de modo que el binario no tenga que estar en un lugar específico. dentro de alguna estructura de directorios para saber dónde está el código fuente de OpenCL.
Me gustaría incluir los archivos OpenCL como constantes de cadena en algún lugar, y preferiblemente sin el uso de pasos de compilación adicionales o herramientas externas (para facilitar el uso entre compiladores y multiplataformas ... es decir, no a xxd
y similares) . Pensé que me había topado con una técnica basada en la segunda respuesta en this hilo, así:
#define STRINGIFY(src) #src
inline const char* Kernels() {
static const char* kernels = STRINGIFY(
#include "kernels/util.cl"
#include "kernels/basic.cl"
);
return kernels;
}
Tenga en cuenta que preferiría no incrustar la macro STRINGIFY
en mi código OpenCL si es posible (como se hizo en la pregunta SO mencionada anteriormente). Ahora, esto funciona maravillosamente en el compilador Clang / LLVM, pero GCC muere de una muerte horrible ("Aparece la lista de argumentos no terminados que invoca a la macro STRINGIFY" y varios "errores" de sintaxis relacionados con el contenido de los archivos .cl). Entonces, claramente esta técnica exacta no se puede usar en los compiladores (no he probado MSVC, pero me gustaría que funcionara allí también) ... ¿Cómo podría aplicar un masaje mínimo para que funcione en todos los compiladores?
En resumen, me gustaría una técnica compatible con los estándares para incluir el contenido de un archivo como una constante de cadena C / C ++ sin invocar herramientas externas o contaminar los archivos con código extraño. Ideas?
EDITAR : como señaló Potatoswatter, el comportamiento de lo anterior no está definido, por lo que probablemente no sea posible utilizar una verdadera técnica de preprocesador de compilación cruzada que no implique tocar los archivos que deben ser encadenados. hack que funciona para la mayoría / todos los compiladores obtiene los puntos de respuesta). Para los curiosos, terminé haciendo lo que se sugirió en la segunda respuesta this ... es decir, agregué la macro STRINGIFY
directamente a los archivos de OpenCL que incluía:
En somefile.cl
:
STRINGIFY(
... // Lots of OpenCL code
)
En somefile.cpp
:
#define STRINGIFY(src) #src
inline const char* Kernels() {
static const char* kernels =
#include "somefile.cl"
;
return kernels;
}
Esto funciona en los compiladores en los que lo he probado (Clang y GCC también, ya que no tiene directivas de preprocesador dentro de la macro), y no es una carga demasiado grande al menos en mi contexto (es decir, no lo hace) t interfiere con el resaltado de sintaxis / edición de los archivos OpenCL). Una característica de los enfoques de preprocesador como este es que, dado que las cadenas adyacentes se concatenan, puede escribir
inline const char* Kernels() {
static const char* kernels =
#include "utility_functions.cl"
#include "somefile.cl"
;
return kernels;
}
y mientras la macro STRINGIFY esté en ambos archivos .cl
, las cadenas se concatenarán, lo que le permitirá modularizar su código OpenCL.
La parte más relevante de la Norma es §16.3 / 10:
La secuencia de tokens de preprocesamiento delimitados por los paréntesis coincidentes más externos forma la lista de argumentos para la macro similar a una función. Los argumentos individuales dentro de la lista están separados por tokens de preprocesamiento de coma, pero los tokens de preprocesamiento de coma entre paréntesis internos no separan los argumentos. Si (antes de la sustitución de argumentos) algún argumento no consiste en tokens de preprocesamiento, el comportamiento no está definido. Si hay secuencias de tokens de preprocesamiento dentro de la lista de argumentos que, de lo contrario, actuarían como directivas de preprocesamiento, el comportamiento no está definido.
Extrayendo los puntos clave:
- Debe encerrar los archivos de encabezado dentro de un par de paréntesis para que la macro no piense que cada carácter de coma en el archivo presente otro argumento. Estos paréntesis también estarán encadenados, pero no deberían ser difíciles de solucionar.
- Poner
#include
en una lista de argumentos es un comportamiento oficialmente indefinido, por lo que será inportable. El compilador oficialmente no sabe si desea que la cadena resultante sea"#include /"kernels/util.cl/""
.
La técnica convencional es usar un programa como bin2c, generalmente escrito a toda prisa. Otro método es usar objcopy de GNU binutils:
$ objcopy -I binary extensions.cfg -O elf32-little -B i386 --rename-section .data=.rodata extensions.o
$ objdump -x extensions.o
extensions.o: file format elf32-i386
extensions.o
architecture: i386, flags 0x00000010:
HAS_SYMS
start address 0x00000000
Sections:
Idx Name Size VMA LMA File off Algn
0 .rodata 00000447 00000000 00000000 00000034 2**0
CONTENTS, ALLOC, LOAD, DATA
SYMBOL TABLE:
00000000 l d .rodata 00000000 .rodata
00000000 g .rodata 00000000 _binary_extensions_cfg_start
00000447 g .rodata 00000000 _binary_extensions_cfg_end
00000447 g *ABS* 00000000 _binary_extensions_cfg_size
Los indicadores -O y -B tienen que coincidir con la salida de objdump para uno de sus archivos de objeto compilados, para satisfacer al vinculador, mientras que el cambio de nombre de la sección es solo para informar al vinculador de tiempo de ejecución que estos datos son de solo lectura. Tenga en cuenta los símbolos, la asignación a la dirección de inicio, la dirección final y el tamaño de los datos. Cada una cuenta como direcciones, por lo que en C las usarías con algo como:
extern const char _binary_extensions_cfg_start, _binary_extensions_cfg_end;
extern const char _binary_extensions_cfg_size;
for (const char *p=&_binary_extensions_cfg_start; p<&_binary_extensions_cfg_end; p++)
do_something(p);
memcpy(somewhere, &_binary_extensions_cfg_start, (intptr_t)&_binary_extensions_cfg_size);
Me doy cuenta de que ninguno de estos es el preprocesador que estás pidiendo, pero simplemente no fue diseñado para hacer eso. Sin embargo, me interesaría saber si es posible.
No puedes hacerlo de esa manera; Me sorprende que haya funcionado en clang. Si desea incluir archivos de texto directamente en su binario, tiene algunas opciones:
1: un preprocesamiento que se ejecuta antes de la compilación que convierte sus archivos .cl en un archivo .cpp que define la cadena.
2: Almacenar los datos de cadena a través de un almacenamiento específico del compilador en su ejecutable compilado. Así es como las herramientas como Visual Studio incluyen .rc, .ico y otros archivos que se usan para compilar aplicaciones de Windows. Por supuesto, como se ha indicado, estos son específicos del compilador.
La forma más segura es la opción 1.