【问题标题】:How to include a file in cpp as both a string and code?如何将文件作为字符串和代码包含在 cpp 中?
【发布时间】:2015-01-23 04:24:21
【问题描述】:

所以我想要一个可以有条件地包含为代码或字符串的文件。 像这样:

#define something
#include "myfile.inc" 

#undef something
const char myfileasastring = 
#include "myfile.inc" 

myfile.inc 将是这样的简单代码:

something // anything extra here is fine as long as it goes away in the code include case
int myfunc() { return 23; } 

这可能吗?我尝试使用#define something R("mystring 并用它启动 myfile,但这似乎不起作用。

我的具体用例是一个 GLSL 着色器,当在 GL 下运行时,我需要作为字符串传递给 GL 驱动程序,但在软件仿真中运行它时,我想将其编译为 CPP 代码。

我希望这两种情况都使用相同的文件。我知道项目/制作/构建级别的许多解决方法,即使只是剪切和粘贴也不可怕,但如果有一个可移植的编译器级别的方法来做到这一点,那就太好了。

检测到的不是骗子。我知道如何作为一个 xor 包含另一个,但不是作为其中一个或与同一个文件。我也知道如何使用外部工具来做到这一点,我真的只是想知道是否只是简单的、可移植的 c++11。这可能是不可能的。 (尝试再次删除欺骗标签..如果您仍然认为它是欺骗,请发表评论)

【问题讨论】:

  • 对不起,骗子问的不是同一个问题。仅包含文本与 R( 作为第一行对我来说效果很好。但是我不能再将它包含为代码!
  • 我不认为可以这样做,因为预处理器在处理 #include 或任何其他预处理器指令之前对输入进行标记。有一些程序可以将文本文件转换为适当的字符串声明。
  • 假设你确实让它工作,你的着色器代码恰好兼容 GLSL 和 C++?
  • 乔纳森:是的,我担心。我现在正在使用外部工具。但问以防我错过了什么。
  • BWG:是的,只需要一些结构,#defines,函数,一个带有虚拟主的基类。到目前为止,唯一不起作用的是像 .yx 这样的非常规混合。但是我只是用 vec2(a.y,a.x) 而不是 a.yx.Stuff 来修复 GLSL,比如 .xy 可以通过匿名工会工作。我还必须手动导出名称和指针。例如:RegisterVar ("name", varying, 3, &name)。

标签: c++ c++11 macros glsl


【解决方案1】:

创建一个名为 include.cpp 的文件,其中包含以下内容:

int f()
{
  return 42;
}

那么,main.cpp(灵感来自https://stackoverflow.com/a/25021520/4323):

#include <iostream>
#include "include.cpp"

int main()
{
  const char* const str =
    R"(include(include.cpp))"
  ;
  std::cout << str << '\n';
  return f();
}

include() 语法是什么?那是m4!所以你这样编译:

m4 main.cpp | g++ -x c++ -std=c++11 -

或者,如果您不能在编译命令中使用管道:

g++ -x c++ -std=c++11 <(m4 main.cpp)

如果您使用第二个选项,您将需要一个 -I 规则来告诉编译器 #include "include.cpp" 的位置,因为源文件“存在”在 /dev 中(这是一个 Bash 主义)。您可以改为使用include(include.cpp) 而不是在顶部使用#include,无论哪种方式都可以。

程序打印来自include.cpp 的代码,然后调用其中的函数。

对于使用 m4,我深表歉意,但它在许多系统上都可用(您可以为其他人编写自己的简单实现)。

【讨论】:

  • 感谢您的回答,它完全符合我想要的结果,但我真的在寻找一种无需外部工具的方法。我现在正在使用像您这样的项目级解决方案!这个问题的意思不是“我怎样才能实现这个目标?”但是“这可能只使用便携式 cpp 吗?”。
  • @starmole:我很确定使用便携式 C++ 是不可能的。
【解决方案2】:

我在一个项目中做过类似的事情。我们编写了一个脚本来将代码编译为 C++,然后将我们的主要目标(我们的应用程序)与结果相关联。然后,我们还将代码作为资源包含在我们正在构建的应用程序或库中,并在我们想将其作为 glsl 传递给 OpenGL 时从磁盘读取它。我们在 Xcode 中做到了这一点,首先将代码作为 C++ 编译到共享库中,并为我们的应用程序创建第二个目标,该目标链接到生成的共享库,并将代码作为文本文件复制到我们的应用程序包中。您可以对 make 文件或 Visual Studio 或您正在使用的任何工具执行相同的操作。

【讨论】:

  • 感谢您的回答!这很像我现在正在做的事情。但我正在寻找一种仅在 cpp 中执行此操作的方法,而不涉及额外的工具/项目/构建步骤。毕竟可能是不可能的。
【解决方案3】:

抱歉浪费了大家的时间。我想通了,这很容易:

test.inc:

#ifdef ASSTRING
R"foo(
#else
int do () { return 23; }
// )foo";
#endif

main.cpp

#define ASSTRING
const char s[] =
#include "test.inc";

#undef ASSTRING
#include "test.inc"

printf ( "hello\n%s\n%i\n", s+6, me() );

将打印代码并运行它。

【讨论】:

  • 您设法在哪个编译器上运行此代码?它看起来像一个简洁的解决方案,但它不适用于 g++ 5.3.0。编译器总是将代码解析为#ifdef ASSTRING \n R"(something)"; \n #endif
  • @HolyBlackCat 不幸的是,它只适用于 VC++。在这里查看我的相关问题stackoverflow.com/questions/30997129/…
【解决方案4】:

正如评论和解释的(由其他两个答案 by John Zwinckby user1118321),没有外部工具是不可能的。看起来您正在梦想一些(但不存在的)预处理器指令 #include_verbatim,以便 #include_verbatim "myfile.inc" 扩展为包含 myfile.inc 内容的长字符串常量。

这还不存在。您可能会自定义一个编译器(例如,如果使用最近的 GCC... 进行编译,则使用 MELT),例如将处理 #pragma MAKE_VERBATIM_LITERAL_FROM_FILE_CONTENT(MYCONTENT,myfile.inc) 以将预处理器符号 MYCONTENT 定义为 myfile.inc 的文字内容;但这需要付出很大的努力,并且是特定于编译器的。

最实用的解决方案是接受使用一些外部工具(例如,一个简单的make 规则将myfile.inc 转换为myfile.inc.data,这样您就可以适当地#include "myfile.inc.data")。这将花费您几分钟的开发时间(例如,m4awkhexdumpreswrap 来自 FOX toolkit ...)

如果您不想依赖某些外部工具,请将其在您的项目内部通过在您的项目中编写一个自治的transform_to_hex_string.cpp 程序编译为transform_to_hex_string.bin 并添加make 处理它的规则 - 即在一侧从 transform_to_hex_string.cpp 构建 transform_to_hex_string.bin,并在另一个 make 规则中运行 transform_to_hex_string.bin &lt; myfile.inc &gt; myfile.inc.data;但它仍然在编译器之外!

自定义编译器(无论是 GCC 还是 LLVM)是特定于编译器的(并且可能是特定于版本的),并且需要付出更多的努力(可能需要一周)。

您可能会尝试游说某些 C++ 标准化委员会成员将此类语言功能包含在某些未来(C++17 后)标准中。

但请记住,即使在没有文件或目录的假设实现上也可以读取 C++ 标准(C++11 编译器需要处理“translation units”,而不是操作系统意义上的“源文件”,在标准措辞;处理来自某个由某个 IDE 填充的某个数据库的 source code 的编译器将符合标准 - 上个世纪已经存在这样的编译器,可能来自 IBM 的 VisualAge

来自最新的 C++11 草案规范(n3337§2.1 单独翻译

程序的文本保存在本国际标准中称为源文件的单元中。一个源文件 连同通过预处理指令#include 包含的所有头文件 (17.6.1.2) 和源文件 (16.2),减去任何条件包含 (16.1) 预处理指令跳过的任何源代码行,是 称为翻译单元。 [ 注意:一个 C++ 程序不需要同时翻译。 ——尾注] [注:以前翻译的翻译单元和实例化单元可以单独保存或保存在 图书馆。程序的单独翻译单元通过(例如)调用函数进行通信(3.5) 其标识符具有外部链接,操作其标识符具有外部链接的对象,或 数据文件的操作。翻译单元可以单独翻译,然后链接生成 一个可执行程序(3.5)。 ——尾注]

另请阅读 C++11 标准的§2.2 翻译阶段,特别是:

翻译的语法规则之间的优先级由以下阶段指定。

  1. 物理源文件字符以实现定义的方式映射到基本源 字符集 [....]

  2. 每个反斜杠字符 () 的实例都被删除,紧跟一个换行符, 拼接物理源线形成逻辑源线。 [....]

  3. 源文件被分解为预处理标记 (2.5) 和空白字符序列 (包括 cmets)。源文件不应以部分预处理标记或部分com- 更换。 12 每条注释被一个空格字符替换。保留换行符。无论 除换行符以外的每个非空空白字符序列都被保留或替换为 未指定空格字符。将源文件的字符划分为预处理的过程,以 - kens 是上下文相关的。 [ 示例:参见 #include 预处理指令中对

  4. 执行预处理指令,扩展宏调用,_Pragma 一元运算符 表达式被执行。如果与通用字符名称的语法匹配的字符序列由标记连接 (16.3.3) 生成,则行为未定义。 #include 预处理指令导致命名的头文件或源文件从阶段 1 到阶段 4 以递归方式进行处理。 然后删除所有预处理指令。

另见维基页面Quine (computing)

顺便说一句,使用外部工具从外部源生成 C++ 代码是非常常见的做法:Yacc(或 GNU bison)和Lex(或Flex)&ANTLR&MOC 来自@987654338 @ 是众所周知的例子(MELT 被翻译成 C++)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-23
    • 2018-10-01
    • 2017-09-01
    • 2018-11-01
    相关资源
    最近更新 更多