9

I have a file module.hpp

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

and a main.cpp program

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

What can I put at ...?... to let the contents of the header file printed here?

A basic idea would be

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

but multi-line-strings are only available in C++11, and #include does not work inside multi-line strings (which is good)?

If there is a non-portable way for the gcc... that would be a start.

Clarification (update): The substitution should be done at compile time.

towi
  • 21,587
  • 28
  • 106
  • 187
  • AFAIK there is no way other then the one you used, unless you are willing to open the file using a stream and output it. – RedX Dec 20 '12 at 10:56
  • There _is_ a non-porbable way of embedding the header into the executable under Windows, but it won't work under other systems. – James Kanze Dec 20 '12 at 11:23
  • There's also a non-portable way with gcc. You have the sources, so you can modify the compiler to do whatever you want. (And yes, I'm being facetious.) – James Kanze Dec 20 '12 at 11:44
  • 1
    @JamesKanze: Less facetiously, you could use `objcopy -O binary` to convert the header into an object file containing its contents. – Mike Seymour Dec 20 '12 at 12:30
  • MikeSeymour: Yes! Can you make the a full-blown answer, please? How to use/link the obj-files sysmbols from my C(++)-code then? – towi Dec 20 '12 at 12:38
  • @towi Mike has given you the tip; if you're on Linux (or have access to the GNU binutils otherwise), the man page for `objcopy` should tell you all you need. (From a quick perusal, I think the option you need is `-B`, and not `-O`. But I've no real experience with it to be sure.) Of course, Mike _should_ post it as an answer, since that's what it really is. – James Kanze Dec 20 '12 at 12:47
  • @towi: It's a while since I've done it, so I can't remember the exact heiroglyphs needed. I'll see if I can figure it out in enough detail to make an answer. – Mike Seymour Dec 20 '12 at 12:47

4 Answers4

5

The only real solution I know is to write a small program which converts a file into a C++ definition of a string variable containing it. This is fairly simple to write: output a simple header along the lines of:

char const variableName[] =

Then copy each line of the file, wrapping it in "...\n", and escaping any characters necessary. (If you can be sure of C++11, then you might be able to do something with R"...", but I've no experience with this.)

[update: refering to the original question with a typo in it]: Your solution should not work; if it does, it is an error in the compiler. According to §2.2, tokenization occurs before the execution of preprocessing directives. So when the execution of preprocessing directives occurs, you have a string literal, and not a # preprocessing token. (Compiler errors are to be expected when using C++11 features. There's not been enough time yet for the implementers to get all of the bugs out.)

towi
  • 21,587
  • 28
  • 106
  • 187
James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • I had a typo in my original question, no it does not work with Raw-Strings, of course. I corrected it. – towi Dec 20 '12 at 12:45
2

As a GNU-only hack, you could convert the header into a binary object file and link that with the executable.

First, use objcopy to do the conversion:

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

replacing i386 with the architecture you're building for if necessary. The resulting object file will contain symbols for the header contents and its size, which you can access as follows:

#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);
}
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • I see. And during linking I just add the `.o`-file? And I do not have to handle symbol names inside the `.o`-file somehow? How is the connection from the linked `.o` to the symbol `_binary_module_hpp_start` and -`size` done? Those are introduced by `objdump`? Quite straight forward -- I like it. Someone has to hold me back from using this hack all the time ;-) – towi Dec 21 '12 at 11:36
1

Apart from external tools, I think it cannot be done. The C++11 way you given does not work, #include is not expanded in a string. See here for example.

The C++03 way would have been the following, with macros:

#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, nothing is outputed. With Visual Studio 2010, #include <string> is outputed.

If you can modify the compilation chain, you can add a prebuild step which will include the contents of the file you want as a string (can be done easily with tools like CMake, or custom makefile).

Synxis
  • 9,236
  • 2
  • 42
  • 64
  • The C++03 way hasn't changed in C++11. But it doesn't give the desired results either; §16.3.2/2 "[...] If the replacement that results is not a valid character string literal, the behavior is undefined" (and a valid character string literal cannot contain a new line character), and §16.3.4/3 "The resulting completely macro-replaced preprocessing token sequence is not processed as a preprocessing directive even if it resembles one." – James Kanze Dec 20 '12 at 11:32
  • Agree. I put "*would have been*", which I think is conditional and does not say that it "works". But as English is not my native language, I might be wrong... – Synxis Dec 20 '12 at 11:34
  • I had a typo on my Raw-String explanation. I figured out that `#include` does *not* work in them. I corrected the question now. – towi Dec 20 '12 at 11:38
0

You can use ofstream to open a file stream and read and output the contents.

Kevin
  • 2,739
  • 33
  • 57