Подтвердить что ты не робот

Препроцессорный tomfoolery (строгая #include)

Примечание. Этот вопрос не имеет ничего общего с OpenCL per se... проверьте последний абзац для краткого изложения моего вопроса. Но для обеспечения некоторого фона:

Я пишу код на С++, который использует OpenCL. Мне нравится сохранять исходный код для своих ядер OpenCL в своих собственных файлах, чтобы упростить кодирование и обслуживание (в отличие от встраивания источников непосредственно в виде строковых констант в связанном С++-коде). Это неизбежно приводит к вопросу о том, как загрузить их в рабочую среду OpenCL, когда придет время для распространения двоичных файлов. В идеале источник OpenCL включен в двоичный файл, так что двоичный код не должен находиться в определенном месте в какой-либо структуре каталогов, чтобы узнать, где находится исходный код OpenCL.

Я хотел бы включать файлы OpenCL в виде строковых констант где-то и, желательно, без использования дополнительных шагов сборки или внешних инструментов (для кросс-компилятора/кросс-платформенной простоты использования... т.е. no to xxd и тому подобное). Я думал, что наткнулся на технику, основанную на втором ответе в этом потоке, например:

#define STRINGIFY(src) #src

inline const char* Kernels() {
  static const char* kernels = STRINGIFY(
    #include "kernels/util.cl"
    #include "kernels/basic.cl"
  );
  return kernels;
}

Обратите внимание, что я бы предпочел не встраивать макрос STRINGIFY в мой код OpenCL, если это вообще возможно (как это было сделано в вышеупомянутом вопросе SO). Теперь это прекрасно работает на компиляторе Clang/LLVM, но GCC умирает ужасной смертью (появляется список Unterminated arguments, вызывающий макрос STRINGIFY) и различные синтаксические "ошибки", связанные с содержимым файлов .cl). Итак, ясно, что эта точная техника не применима для компиляторов (не пробовал MSVC, но я бы хотел, чтобы она там тоже работала)... Как я мог бы массировать ее минимально, чтобы она работала через компиляторы?

В заключение я хотел бы, чтобы стандарт, совместимый со стандартами, включал содержимое файла в виде строковой константы C/С++, не вызывая внешние инструменты или не загрязнял файлы посторонним кодом. Идеи?

EDIT. Как отметил Potatoswatter, поведение вышеопределено undefined, поэтому метод препроцессора по-настоящему кросс-компилятора, который не предполагает касания файлов, (первый человек, чтобы выяснить отвратительный взлом, который работает для большинства/всех компиляторов, получает точки ответа). Для любопытных я в конечном итоге сделал то, что было предложено во втором ответе здесь... то есть я добавил макрос STRINGIFY непосредственно в файлы OpenCL, которые я был в том числе:

В somefile.cl:

STRINGIFY(
  ... // Lots of OpenCL code
)

В somefile.cpp:

#define STRINGIFY(src) #src

inline const char* Kernels() {
  static const char* kernels =
    #include "somefile.cl"
    ;
  return kernels;
}

Это работает в компиляторах, в которых я это пробовал (Clang и GCC, так как он не имеет директив препроцессора внутри макроса) и не является слишком большим бременем, по крайней мере, в моем контексте (т.е. он не мешает синтаксическому освещению/редактированию файлов OpenCL). Одна из особенностей подхода к препроцессору, подобная этой, заключается в том, что, поскольку смежные строки конкатенируются, вы можете написать

inline const char* Kernels() {
  static const char* kernels =
    #include "utility_functions.cl"
    #include "somefile.cl"
    ;
  return kernels;
}

и до тех пор, пока макрос STRINGIFY находится в обоих файлах .cl, строки объединяются, что позволяет вам модулизовать ваш код OpenCL.

4b9b3361

Ответ 1

Наиболее важной частью Стандарта является §16.3/10:

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

Извлечение ключевых точек:

  • Вам нужно заключить файлы заголовков в пару круглых скобок, чтобы макрос не думал, что каждый запятый символ в файле вводит другой аргумент. Эти круглые скобки также будут стробированы, но не должны быть трудными для работы.
  • Помещение #include в список аргументов вообще официально undefined, так что это будет невозможно. Компилятор официально не знает, хотите ли вы, чтобы результирующая строка была "#include \"kernels/util.cl\"".

Ответ 2

Традиционная техника использует программу, подобную bin2c, обычно поспешно написанную. Другой метод - использование objcopy из 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

Флаги -O и -B должны соответствовать выходу objdump для одного из ваших файлов скомпилированных объектов, чтобы удовлетворить компоновщик, в то время как переименование раздела - это просто информировать компоновщик времени выполнения, эти данные доступны только для чтения. Обратите внимание на символы, отображающие начальный адрес, конечный адрес и размер данных. Каждый из них считается адресом, поэтому в C вы будете использовать их с чем-то вроде:

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);

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

Ответ 3

Вы не можете так поступать; Я удивлен, что он работал в clang. Если вы хотите включить текстовые файлы непосредственно в свой двоичный файл, у вас есть несколько вариантов:

1: препроцесс, который выполняется перед компиляцией, которая преобразует ваши .cl файлы в файл .cpp, который определяет строку.

2: Сохранение строковых данных через хранилище, специфичное для компилятора, в скомпилированном исполняемом файле. Вот как инструменты, такие как Visual Studio, включают в себя .rc,.ico и другие файлы, которые используются для создания приложений Windows. Конечно, как указано, они специфичны для компилятора.

Самый безопасный способ - вариант 1.

Ответ 4

Вот что я делаю в 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);

... это не препроцессор voodoo, но он работает для меня, я вижу синтаксис, выделенный в моем .cl файле, и я даже могу скопировать мой .cl в .c, изменить один #define, и он запускается как xcode C....