22

I would like to write something in a file (let's call it foo.cpp) and include it as a string into my program at compile time, similar to the way #include does it.

Right now I'm using this C preprocessor #define:

#define toString(src) #src

to convert a bunch of code to a string, used as in this example:

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

You can read about macro stringification there if you want.

I would like to move that code to an external file, which would be "linked" at compile-time. I don't want the file to have to be distributed with the program, which would be the case if I were to read it a run-time.

I tried to use an #include directive as shown below, but the compiler refused it:

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

g++ seems to be completely confused, but clang++ gave me this error:

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

Does anyone know if/how this can be done?

Note: I'm using this to write my GLSL shaders, although I doubt this information is of any use.

PS: Before you tell me this is a duplicate of this question, putting my code in a giant string in its own file or using an external tool (eg. xxd) to dump its hex representation are not "solutions" for me, as they are not better (ie. easier/cleaner) than my current method.


Update, a few years later:
I just realized I never got to answer this question as it was closed as duplicate. I found the answer I was looking for when I saw this commit, itself based on a comment on this article, and have been using it ever since.

In a nutshell, a small assembly file includes the files you want and exposes them under a given NAME, with the three variables NAME_begin, NAME_end and NAME_len allowing you to access their contents from your C code.

This way, you have a normal file containing nothing but the code you want, and it gets read automatically at compile time, instead of having to read it at runtime or having to jump through xxd hoops.

Community
  • 1
  • 1
1ace
  • 5,272
  • 4
  • 23
  • 30
  • 4
    It is a duplicate of the linked question. You just don't like any of the provided answers. But the answer is that you can't do it the way you want to do it, so you're just going to get a bunch of answers which mirror the linked question's answers. – rici Oct 28 '13 at 15:24
  • What platform are you attempting to compile for ... or are you attempting to make it cross platform? – Zac Howland Oct 28 '13 at 15:25
  • I'm trying to fathom how the terms "better", "easier", and "cleaner" apply to *this* method, much less any other. Just for understanding, you want to preprocess to a source-level variable set of strings which are then written to a file using as-of-yet unseen code, then later compile said-file from source, and the trouble you're having is embedding preprocessor statements in the "source" of step 1 ? – WhozCraig Oct 28 '13 at 15:28
  • @ZacHowland: I'd like it to be cross-platform, but it is not a requirement. – 1ace Oct 28 '13 at 15:31
  • @WhozCraig Something that takes me more time and is uglier *(you can't read it anymore)* is not an improvement. I do not understand you last sentence, though. – 1ace Oct 28 '13 at 15:36
  • What you're asking for is something unusual enough that nobody ever considered adding it to the language. You're really looking for something like a [Python triple quoted string](http://docs.python.org/release/1.4/tut/node70.html) to allow multi-line strings, which doesn't exist in C or C++. – Mark Ransom Oct 28 '13 at 15:39
  • 2
    @MarkRansom: Actually it is in C++: `raw-string: " d-char-sequenceopt( r-char-sequenceopt) d-char-sequenceopt"` (The lexical grammar requires that sequence to be immediately preceded by a capital R.) – rici Oct 28 '13 at 15:45
  • @WhozCraig "Better", "easier" and "cleaner" mean (apparently) things that don't work. – James Kanze Oct 28 '13 at 15:46
  • @rici I'd say that should actually be an answer. – Angew is no longer proud of SO Oct 28 '13 at 15:50
  • @rici, thanks for that - there's so much new stuff in C++11 that it's easy to forget. P.S. you should make it into an answer. – Mark Ransom Oct 28 '13 at 15:50
  • @Angew: I don't think it's superior to OP's macro hack. – rici Oct 28 '13 at 15:50
  • @rici Given that neither work:-). – James Kanze Oct 28 '13 at 15:56
  • 1
    I'm voting to reopen this since it has a C++ answer that wouldn't be appropriate to the C-only "duplicate". @rici why not make it an answer and let the OP decide if it's better, given that the macro hack doesn't do what he wants? – Mark Ransom Oct 28 '13 at 16:28
  • @WhozCraig I think I got it, you trying to understand why I have to do this. The short answer is GLSL, and you can read the long answer [there](http://en.wikipedia.org/wiki/OpenGL_Shading_Language) ;) – 1ace Oct 28 '13 at 17:57
  • @MarkRansom How is reading a file into a string "something unusual enough that nobody ever considered adding it to the language" ? – 1ace Oct 28 '13 at 17:58
  • I guess there is no better way to do it, so I'll keep doing it my old way. (The C++ raw string is the equivalent of wrapping my whole program in quotes, so the same argument applies.) Thanks anyways :) – 1ace Oct 28 '13 at 18:05
  • @1ace reading a file is typically done at run time not compile time. C and C++ are already unusual in allowing a file to be inserted into the source at compile time. I'm not sure why the raw string wouldn't work for you? You wouldn't need to put quotes around the program, just the lines before and after the `#include`. – Mark Ransom Oct 28 '13 at 18:24
  • @JamesKanze: Presumably the macro hack "works" since OP is using it, but it is certainly not an answer to the question. Certainly the preprocessing solution would be easier with raw strings since it only requires a prefix and a postfix; it's not necessary to search for characters which need escaping. I'd use the preprocessing solution. But tastes vary. – rici Oct 28 '13 at 18:56
  • @rici The whole point of his question is that it didn't work. He wants the text to come from an external file, which is included. – James Kanze Oct 28 '13 at 19:06

3 Answers3

19

I am not quite sure what you are trying to accomplish but but Linux command line utility xxd may be what you are looking for:

xxd -i [filename]

will generate a C style header file containing an array with your file contents in full binary encoding and a variable with its length.

Example:

xxd -i /proc/cpuinfo

makes a file with

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;

You can include the resulting header in your code and access the array and file length via those variables.

wallyk
  • 56,922
  • 16
  • 83
  • 148
Sergey L.
  • 21,822
  • 5
  • 49
  • 75
  • 3
    As I said in the question, this solution is neither easier nor cleaner *(which is subjective)* than my current method, but thanks for the copy/paste :) – 1ace Oct 28 '13 at 15:34
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.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
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).

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • When using VS, you can just bring in the text file as a resource and access it using the LoadResource functions; though that is Windows-specific. – Zac Howland Oct 28 '13 at 15:56
  • @ZacHowland Yes. I know that Windows has a couple of options here. Regretfully, not very portable. – James Kanze Oct 28 '13 at 15:57
  • Sadly. My comment was more directed at the notion that Visual Studio isn't really designed for professional work. It is ... but it is focused on Windows-specific professional work (perhaps that may change with their new-found push in the open-source arena, though). – Zac Howland Oct 28 '13 at 16:01
  • @ZacHowland This isn't the only case where you'd want machine generated code. Every professional project I've worked on has used significant amounts of machine generated code, for various reasons. And Visual Studios makes it particularly difficult if the programs generating the code are also written in C or C++. – James Kanze Oct 28 '13 at 18:33
  • I've never had a problem with it (other than when people check the generated code into source control, but that is not a problem with VS, nor specific to it), but then again, I can't recall any project I've been on where we used C/C++ to write code generation for C/C++. Typically, the projects I've been on have always used a scripting language for code generation. – Zac Howland Oct 28 '13 at 20:53
  • @ZacHowland I've seen both. A lot of the time, a scripting language is more appropriate. (It's certainly what I'd use here.) But we use C++ code here, and I've seen it used in a lot of other cases. (Of course, once the C++ code becomes fairly complicated, and you use it in all, or at least most of the projects, you'll want it in a separate project anyway.) – James Kanze Oct 29 '13 at 09:15