如何将文件同时作为字符串和代码包含在 cpp 中?

How to include a file in cpp as both a string and code?

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

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

我希望两种情况都使用相同的文件。我知道 project/make/build 级别的许多解决方法,即使只是剪切和粘贴也不是很糟糕,但是如果有一个可移植的编译器级别的方法来执行此操作,那就太好了。

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

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

int f()
{
  return 42;
}

然后,main.cpp(灵感来自):

#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 中的源文件 "exists" (这是一种 Bash 主义)。您可以在顶部说 include(include.cpp) 而不是使用 #include,这两种方法都有效。

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

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

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

正如评论和解释的那样(由其他两个答案 and ),没有外部工具是不可能的。看起来你正在梦想一些(但不存在的)预处理器指令 #include_verbatim 这样 #include_verbatim "myfile.inc" 扩展为包含 myfile.inc.

内容的长文字字符串常量

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

最实用的解决方案是接受使用一些外部工具(例如,将 myfile.inc 转换为 myfile.inc.data 的简单 make 规则,这样您就可以 #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,另一方面从 运行 transform_to_hex_string.bin < myfile.inc > myfile.inc.data 构建make 规则;但它仍然在编译器外部!

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

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

但是请记住,即使在没有文件或目录的假设实现上也可以阅读 C++ 标准(来自 IBM 的 C++11 compiler is required to process "translation units", not "source files" in the operating system sense, in the standard wording; a compiler handling source code from some database filled by some IDE would be standard compliant - and there have existed such compilers in the previous century, perhaps VisualAge

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

The text of the program is kept in units called source files in this International Standard. A source file together with all the headers (17.6.1.2) and source files included (16.2) via the preprocessing directive #include, less any source lines skipped by any of the conditional inclusion (16.1) preprocessing directives, is called a translation unit. [ Note: A C ++ program need not all be translated at the same time. — end note ] [ Note: Previously translated translation units and instantiation units can be preserved individually or in libraries. The separate translation units of a program communicate (3.5) by (for example) calls to functions whose identifiers have external linkage, manipulation of objects whose identifiers have external linkage, or manipulation of data files. Translation units can be separately translated and then later linked to produce an executable program (3.5). — end note ]

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

The precedence among the syntax rules of translation is specified by the following phases.

  1. Physical source file characters are mapped, in an implementation-defined manner, to the basic source character set [....]

  2. Each instance of a backslash character () immediately followed by a new-line character is deleted, splicing physical source lines to form logical source lines. [....]

  3. The source file is decomposed into preprocessing tokens (2.5) and sequences of white-space characters (including comments). A source file shall not end in a partial preprocessing token or in a partial com- ment. 12 Each comment is replaced by one space character. New-line characters are retained. Whether each nonempty sequence of white-space characters other than new-line is retained or replaced by one space character is unspecified. The process of dividing a source file’s characters into preprocessing to- kens is context-dependent. [ Example: see the handling of < within a #include preprocessing directive. — end example ]

  4. Preprocessing directives are executed, macro invocations are expanded, and _Pragma unary operator expressions are executed. If a character sequence that matches the syntax of a universal-character-name is produced by token concatenation (16.3.3), the behavior is undefined. A #include preprocessing directive causes the named header or source file to be processed from phase 1 through phase 4, recursively. All preprocessing directives are then deleted.

另请参阅 Quine (computing)

上的维基页面

顺便说一句,使用外部工具从外部源生成 C++ 代码是很常见的做法:Yacc (or GNU bison) & Lex (or Flex) & ANTLR & MOC from Qt are very well known examples (and MELT 被翻译成 C++)。

不好意思耽误了大家的时间。我想通了,这很容易:

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() );

将同时打印代码和 运行 它。