【问题标题】:How can I make the contents of an #include-file a compile-time constant in a cpp-file?如何使 #include 文件的内容成为 cpp 文件中的编译时常量?
【发布时间】:2012-12-20 10:53:44
【问题描述】:

我有一个文件module.hpp

struct ModuleBase {
    virtual void run() = 0;
};

还有一个main.cpp 程序

int main() {
    cout << ...?...; // here should go the contents of module.hpp
}

我可以在...?... 输入什么来让这里打印头文件的内容?

一个基本的想法是

int main() {
    static const string content = R"(
#include <module.hpp>
)";
    cout << content;
}

但多行字符串仅在 C++11 中可用,#include 在多行字符串中工作(这很好)吗?

如果 gcc 有一种非便携式的方式......那将是一个开始。

澄清(更新):替换应该在编译时完成。

【问题讨论】:

  • AFAIK 除了你使用的那个之外别无他法,除非你愿意使用流打开文件并输出它。
  • 在Windows下一种不可移植的方式将header嵌入到可执行文件中,但在其他系统下无法使用。
  • gcc 还有一种不可移植的方式。你有源代码,所以你可以修改编译器来做任何你想做的事情。 (是的,我在开玩笑。)
  • @JamesKanze:不那么夸张,您可以使用objcopy -O binary 将标头转换为包含其内容的目标文件。
  • 迈克西摩:是的!请给个完整的答案好吗?那么如何使用/链接我的 C(++) 代码中的 obj 文件 sysmbols?

标签: c++ compiler-construction syntax self-modifying


【解决方案1】:

我知道的唯一真正的解决方案是编写一个小程序 将文件转换为字符串变量的 C++ 定义 包含它。这写起来相当简单:输出一个简单的 标题如下:

char const variableName[] =

然后复制文件的每一行,将其包裹在"...\n"中,然后 转义任何必要的字符。 (如果你能确定 C++11,那么你也许可以用R"..."做点什么,但是 我没有这方面的经验。)

[更新:参考原始问题,其中有错字]: 您的解决方案应该起作用;如果是这样,这是一个错误 编译器。根据 §2.2,标记化发生在之前 预处理指令的执行。所以当 执行预处理指令时,你有一个字符串 文字,并且 不是 # 预处理令牌。 (编译器错误 在使用 C++11 特性时是可以预料的。没有 有足够的时间让实施者得到所有的错误 出来。)

【讨论】:

  • 我原来的问题中有一个错字,当然,它不适用于 Raw-Strings。我更正了。
【解决方案2】:

作为仅限 GNU 的 hack,您可以将标头转换为二进制对象文件并将其与可执行文件链接。

首先,使用objcopy进行转换:

objcopy -I binary -O default -B i386 module.hpp module.hpp.o 

如有必要,将 i386 替换为您正在构建的架构。生成的目标文件将包含标头内容及其大小的符号,您可以按如下方式访问:

#include <iostream>

extern char _binary_module_hpp_start;
extern char _binary_module_hpp_size;

int main()
{
    char * header_start = &_binary_module_hpp_start;
    size_t header_size = reinterpret_cast<size_t>(&_binary_module_hpp_size);

    std::cout.write(header_start, header_size);
}

【讨论】:

  • 我明白了。在链接过程中,我只需添加.o-file?而且我不必以某种方式处理.o-文件中的符号名称?从链接的.o 到符号_binary_module_hpp_start 和-size 的连接是如何完成的?那些是objdump介绍的?很直截了当——我喜欢它。必须有人阻止我一直使用这个 hack ;-)
【解决方案3】:

除了外部工具,我认为是做不到的。您给出的 C++11 方式不起作用,#include 未在字符串中展开。 See here for example.

C++03 的方式应该是以下,带有宏:

#define TO_STR__(...) #__VA_ARGS__
#define TO_STR_(...) TO_STR__(__VA_ARGS__)
#define TO_STR(...) TO_STR_(__VA_ARGS__)

#include <iostream>
int main()
{
    std::cout << "String from #include <string>, ";
    static const char* str = TO_STR(
#include <string>
    );

    std::cout << sizeof(str) / sizeof(char) << " characters:\n\n";
    std::cout << str << "\n";
}

With GCC,没有输出。使用 Visual Studio 2010,会输出 #include &lt;string&gt;

如果您可以修改编译链,则可以添加一个预构建步骤,其中将包含您想要的文件内容作为字符串(可以使用 CMake 或自定义 makefile 等工具轻松完成)。

【讨论】:

  • C++03 的方式在 C++11 中没有改变。但它也没有给出预期的结果。 §16.3.2/2“[...] 如果替换结果不是有效的字符串文字,则行为未定义”(并且有效的字符串文字不能包含换行符)和 §16.3.4 /3 "生成的完全被宏替换的预处理标记序列即使与预处理指令相似,也不会作为预处理指令处理。"
  • 同意。我输入了“would have been”,我认为这是有条件的,并不是说它“有效”。但由于英语不是我的母语,我可能错了......
  • 我的原始字符串解释有误。我发现#include 确实 not 在它们中工作。我现在更正了这个问题。
【解决方案4】:

您可以使用ofstream打开文件流并读取和输出内容。

【讨论】:

  • 保持他的语法的方法是使用 lambdas(见我的回答)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-12
相关资源
最近更新 更多