预处理器tomfoolery(字符串化#include)

Dan*_*Dan 17 c++ include opencl stringification c-preprocessor

注意:这个问题与OpenCL本身无关......请查看最后一段,以获得我的问题的简洁陈述.但要提供一些背景知识:

我正在编写一些使用OpenCL的C++代码.我喜欢将OpenCL内核的源代码保存在自己的文件中,以保持编码和维护的简单性(而不是将源代码直接嵌入到相关C++代码中的字符串常量中).这一问题不可避免地导致了一旦分配二进制文件时如何将它们加载到OpenCL运行时的问题 - 理想情况下,OpenCL源包含在二进制文件中,因此二进制文件不需要在特定的位置在一些目录结构中,知道OpenCL源代码的位置.

我想将OpenCL文件作为字符串常量包含在某处,最好不使用额外的构建步骤或外部工具(对于交叉编译器/跨平台易用性...即,不xxd等等).我以为我偶然发现了一个基于这个帖子中第二个答案的技术,就像这样:

#define STRINGIFY(src) #src

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

请注意,STRINGIFY如果可能的话,我宁愿不在我的OpenCL代码中嵌入宏(如上面引用的SO问题中所做的那样).现在,这在Clang/LLVM编译器上运行得非常好,但是GCC死了一个可怕的死亡("未终止的参数列表调用宏STRINGIFY"和与.cl文件的内容相关的各种语法"错误"出现).所以,很明显这个确切的技术在编译器中是不可用的(没有尝试过MSVC,但我也希望它能在那里工作)...我怎么能最低限度地按摩它以便它可以在编译器中工作?

总之,我想要一种符合标准的技术,将文件内容作为C/C++字符串常量包含在内,而无需调用外部工具或使用无关代码污染文件.想法?

编辑:正如Potatoswatter指出的那样,上面的行为是未定义的,所以一个真正的交叉编译器预处理器技术不涉及触摸要被字符串化的文件可能是不可能的(第一个找出令人发指的人)黑客工具,的工作对大多数/所有的编译器得到的答案要点).对于好奇的,最后我做什么在第二个响应建议在这里 ......也就是说,我添加了STRINGIFY直接宏OpenCL的文件,我包括:

somefile.cl:

STRINGIFY(
  ... // Lots of OpenCL code
)
Run Code Online (Sandbox Code Playgroud)

somefile.cpp:

#define STRINGIFY(src) #src

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

这在我尝试过的编译器中起作用(Clang和GCC也是如此,因为它在宏内部没有预处理器指令),并且至少在我的上下文中并没有太大的负担(即,它没有' t干扰语法高亮/编辑OpenCL文件).像这样的预处理器方法的一个特点是,由于相邻的字符串被连接起来,你可以写

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

只要STRINGIFY宏在两个.cl文件中,字符串就会连接起来,这样就可以模块化你的OpenCL代码.

Pot*_*ter 4

标准最相关的部分是 \xc2\xa716.3/10:

\n\n
\n

由最外面的匹配括号界定的预处理标记序列形成了类函数宏的参数列表。列表中的各个参数由逗号预处理标记分​​隔,但匹配内括号之间的逗号预处理标记不会分隔参数。如果(在参数替换之前)任何参数不包含预处理标记,则行为未定义。如果参数列表中存在预处理标记序列,否则这些预处理标记将充当预处理指令,则行为未定义。

\n
\n\n

提取关键点:

\n\n
    \n
  • 您需要将头文件括在一对括号内,以便宏不会认为文件中的每个逗号字符都会引入另一个参数。这些括号也将被字符串化,但应该不难解决。
  • \n
  • 放入#include参数列表根本就是正式未定义的行为,因此这将是不可移植的。编译器正式不知道您是否希望结果字符串为"#include \\"kernels/util.cl\\"".
  • \n
\n