在编译时将文件读入字符串

时间:2013-10-28 15:18:07

标签: c++ c include c-preprocessor

我想在文件(我们称之为foo.cpp中写一些内容,并将其作为字符串包含在我的程序编译时中,类似#include的方式。

现在我正在使用这个C预处理器#define

#define toString(src) #src

将一堆代码转换为字符串,如下例所示:

const char* str = toString(
  int x;
  void main(){}
);

如果需要,您可以阅读宏字符串化there

我想将该代码移动到外部文件,该文件将在编译时“链接”。 我不希望文件必须随程序一起分发,如果我在运行时读它就会出现这种情况。

我尝试使用#include指令,如下所示,但编译器拒绝了它:

const char* str = toString(
#include "foo.cpp"
);

g++似乎完全混淆了,但是clang++给了我这个错误:

error: embedding a #include directive within macro arguments is not supported

有谁知道是否/如何做到这一点?

注意:我正在使用它来编写我的GLSL着色器,但我怀疑这些信息是否有用。

PS:在您告诉我这是this question的副本之前,将我的代码放在一个巨大的字符串中,或​​者使用外部工具(例如xxd)转储它的十六进制表示对我来说不是“解决方案”,因为它们并不比我当前的方法更好(即更容易/更清洁)。


几年后更新:
我刚刚意识到我从来没有回答这个问题,因为它被重复关闭了。 当我看到this commit时,我找到了我正在寻找的答案,a comment on this article基于{{3}},从那时起就一直在使用它。

简而言之,一个小的汇编文件包含您想要的文件,并在给定的NAME下公开它们,允许三个变量NAME_beginNAME_endNAME_len您可以从C代码访问其内容。

这样,你有一个普通的文件,只包含你想要的代码,它会在编译时自动读取,而不必在运行时读取它或者必须跳过xxd箍。

3 个答案:

答案 0 :(得分:12)

我不太确定你要完成什么,但Linux命令行实用程序xxd可能正是你要找的:

xxd -i [filename]

将生成一个C样式头文件,其中包含一个数组,其中包含完整二进制编码的文件内容和一个长度为的变量。

示例:

xxd -i /proc/cpuinfo

使用

制作文件
unsigned char _proc_cpuinfo[] = {
  0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x09, 0x3a, 0x20,
  0x30, 0x0a, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x69, 0x64, 0x09,
...
};
unsigned int _proc_cpuinfo_len = 654390;

您可以在代码中包含结果标头,并通过这些变量访问数组和文件长度。

答案 1 :(得分:8)

你说你不喜欢xxd,因为它使文件不可读。很公平。编写自己的实用程序可以很简单,它以不同的格式对数据进行编码,因为您特别希望字符串作为输入和输出。您可以利用字符串文字串联来使其易于阅读。

const char* str =
#include "foo.h"
    ;

foo.h中:

"  int x;" "\n"
"  void main(){}" "\n"

我简单地尝试使用C ++ 11的raw string literals,它允许在不重新格式化的情况下使用该文件,但它不起作用。 #include被认为是字符串的一部分,而不是根据需要包含文件。

答案 2 :(得分:1)

在这种情况下最简单的事情是写一个小的 预处理器,它读取您的文件,并输出它包装 引号中的每一行。我可能会在Python中这样做,但确实如此 在C ++中也非常直接。假设你已经 来自inputFileoutputFilevariableName 在某个地方(可能argv,但你可能想要得到 后两个来自输入文件名:

void
wrapFile( std::istream& inputFile,
          std::ostream& outputFile,
          std::string const& variableName )
{
    outputFile << "extern char const " << variableName << "[] =\n";
    std::string line;
    while ( std::getline( inputFile, line ) ) {
        outputFile << "    \"" << line << "\\n\"\n";
    }
    std::outputFile << ";" << std::endl;
}

根据您所包含的文件中的内容,您可以 在输出它之前必须对line进行处理,以逃避事情 例如"\

如果你想获得幻想,你可以添加一些测试来插入 最后一条包裹线上的分号,而不是一条线 它本身,但这不是必要的。

这将导致'\0'终止字符串。鉴于 可能的字符串长度,最好添加 第二个变量,长度为:

std::outputFile << "extern int const "
                << variableName
                << "_len = sizeof(" << variableName << ") - 1;\n";

(不要忘记-1,因为你不想算数 终止编译器将添加到字符串的'\0' 文字。)如果你将生成的文件包括在内 使用,你不一定需要这个,因为std::beginstd::end将提供必要的信息(但同样, 不要忘记使用std::end( variableName ) - 1来忽略 '\n')。

如果你正在使用make,那么生成你的内容相当容易 文件取决于被包装的文件,可执行文件 包装(包装依赖于来源) 以上等)。使用Visual Studios,您需要创建 如果用C ++编写包装代码的单独项目 (我之所以使用Python的原因之一),你很可能会这样做 在依赖管理方面存在一些问题;视觉工作室 并不是专为专业工作而设计的(大块 使用这些技术定期生成代码。)