22

我想在一个文件中写一些东西(让我们称之为它foo.cpp)并在编译时将它作为一个字符串包含到我的程序中,类似于#include它的方式。

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

#define toString(src) #src

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

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

如果需要,您可以在那里阅读有关宏字符串化的信息。

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

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

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

g++似乎完全糊涂了,但clang++给了我这个错误:

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

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

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

PS:在你告诉我这是这个问题的重复之前,将我的代码放在自己文件中的巨大字符串中或使用外部工具(例如xxd)转储其十六进制表示对我来说不是“解决方案”,因为它们是并不比我目前的方法更好(即更容易/更清洁)。


几年后更新:
我才意识到我从来没有回答过这个问题,因为它被关闭为重复。当我看到这个提交时,我找到了我正在寻找的答案,它本身基于对这篇文章的评论,并且从那以后一直在使用它。

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

这样,您就拥有了一个只包含您想要的代码的普通文件,并且在编译时会自动读取它,而不必在运行时读取它或必须跳过xxd循环。

4

3 回答 3

17

我不太确定您要完成什么,但 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;

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

于 2013-10-28T15:23:46.397 回答
10

You say you don't like xxd because it leaves the file unreadable. Fair enough. It would be simple to write your own utility that encodes the data in a different format, since you specifically want strings as input and output. You can take advantage of string literal concatenation to make this easily readable.

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

foo.h:

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

I briefly tried using C++11's raw string literals, which would have allowed using the file without any reformatting, but it did not work. The #include is considered to be part of the string rather than including the file as desired.

于 2013-10-28T15:48:39.043 回答
1

The simplest thing in this sort of case is to write a small preprocessor, which reads your file, and outputs it wrapping each line in quotes. I'd probably do this in Python, but it is pretty straight forward in C++ as well. Supposing that you've got inputFile, outputFile and variableName from somewhere (probably argv, but you might want to derive the latter two from the input filename:

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;
}

Depending on what is in the files you're including, you might have to mangle the line before outputting it, to escape things like " or \.

If you wanted to get fancy, you could add some tests to insert the semicolon on the last wrapped line, rather than a line of its own, but that's not really necessary.

This will result in a '\0' terminated string. Given the probable length of the strings, it might be preferable to add a second variable with the length:

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

(Don't forget the -1, since you don't want to count the terminating '\0' that the compiler will add to your string literal.) If you're including the generated file where it will be used, you don't necessarily need this, since std::begin and std::end will provide the necessary information (but again, don't forget to use std::end( variableName ) - 1, to ignore the '\n').

If you're using make, it's fairly easy to make your generated file depend on the file being wrapped, and the executable which does the wrapping (which in turn depends on the source above, etc.). With Visual Studios, you'll need to create a separate project for the wrapping code, if you write it in C++ (one of the reasons I'd use Python for this), and you'll likely have some problems with dependency management; Visual Studios isn't really designed for professional work (where large blocks of code are regularly generated using such techniques).

于 2013-10-28T15:49:16.480 回答