预处理器愚蠢的做法(对 #include 进行字符串化)

2024-01-16

注意:这个问题与 OpenCL 本身无关......请检查最后一段以获取我的问题的简洁陈述。但提供一些背景:

我正在编写一些使用 OpenCL 的 C++ 代码。我喜欢将 OpenCL 内核的源代码保存在自己的文件中,以便于编码和维护(而不是直接将源代码作为字符串常量嵌入到关联的 C++ 代码中)。这不可避免地会导致一个问题,即在分发二进制文件时如何将它们加载到 OpenCL 运行时中——理想情况下,OpenCL 源代码包含在二进制文件中,这样二进制文件就不需要位于特定位置在某些目录结构中了解 OpenCL 源代码的位置。

我想将 OpenCL 文件作为字符串常量包含在某处,并且最好不使用额外的构建步骤或外部工具(为了跨编译器/跨平台的易用性......即,不xxd等)。我以为我偶然发现了一种基于第二个答案的技术this https://stackoverflow.com/questions/1415538/using-include-to-load-opencl-code线程,像这样:

#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 却死得很惨(“未终止的参数列表调用宏 STRINGIFY”以及与 .cl 文件内容相关的各种语法“错误”出现)。所以,显然这种精确的技术不能跨编译器使用(没有尝试过 MSVC,但我希望它也能在那里工作)...我怎样才能最小化它以使其跨编译器工作?

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

EDIT:正如 Potatoswatter 指出的那样,上述行为是未定义的,因此不涉及接触要字符串化的文件的真正的交叉编译器预处理器技术可能是不可能的(第一个找出令人发指的黑客的人那does大多数/所有编译器的工作都会得到答案点)。出于好奇,我最终按照第二个回复中的建议进行了操作here https://stackoverflow.com/questions/1415538/using-include-to-load-opencl-code...也就是说,我添加了STRINGIFY直接宏到我包含的 OpenCL 文件:

In somefile.cl:

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

In 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 代码。


该标准最相关的部分是 §16.3/10:

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

提取关键点:

  • 您需要将头文件括在一对括号内,以便宏不会认为文件中的每个逗号字符都会引入另一个参数。这些括号也将被字符串化,但应该不难解决。
  • Putting #include在参数列表中根本就是正式未定义的行为,因此这将是不可移植的。编译器正式不知道您是否希望结果字符串成为"#include \"kernels/util.cl\"".
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

预处理器愚蠢的做法(对 #include 进行字符串化) 的相关文章