Question Details

No question body available.

Tags

c++ executable idioms data-files executable-format

Answers (4)

May 29, 2026 Score: 7 Rep: 41,041 Quality: Medium Completeness: 90%

Since C23 there is new feature #embed.

Gcc 15 supports it: GCC 15 Release Series — Changes, New Features, and Fixes - GNU Project

Some more C23 features have been implemented:

  • #embed preprocessing directive support.

Clang 19 also has this.

MSVC I do not see it has this feature, but probably will do in the future.

Didn't try that yet.

It is also part of C++26.

May 29, 2026 Score: 1 Rep: 140,216 Quality: Medium Completeness: 80%

This is a partial answer, as it regards Unix-like/ELF-using environments, based on this question and its answer; I hope to expand to Windows Portable Executables as well.

tl;dr: You can embed a file in the executable using the GNU linker

@MarekR's answer will do nicely on systems where you can use C++26, C23 and/or rely on recent versions of clang/GCC. Let's consider these where #embed is not available.

Step 1: The linker command

It turns out, that if you run the GNU linker, ld, on an arbitrary file, its default action is to embed the file in an executable-binary-format file. So, in your case, if you write:

ld --format binary --relocatable -o bar.o /path/to/foo.bin

You will get an ELF binary file, in the "binary format" (it's a bit of a mystery to me what that means exactly). Inside of it, you'll have symbols corresponding to your file:

$file bar.o
bar.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
$ objdump -t bar.o

bar.o: file format elf64-x86-64

SYMBOL TABLE: 0000000000000000 l d .data 0000000000000000 .data 0000000000000018 g .data 0000000000000000 binarypathtofoobinend 0000000000000018 g *ABS* 0000000000000000 binarypathtofoobinsize 0000000000000000 g .data 0000000000000000 binarypathtofoobinstart

You probably don't want those path elements as part of the symbol names. There might be some switch to not-add them, but you can always just do:

pushd /path/to; ld --format binary --relocatable -o bar.o foo.bin; popd

and now the symbols in bar.o will be:

0000000000000000 l    d  .data  0000000000000000 .data
0000000000000018 g       .data  0000000000000000 binaryfoobinend
0000000000000018 g       *ABS*  0000000000000000 binaryfoobinsize
0000000000000000 g       .data  0000000000000000 binaryfoobinstart

What remains is how to use these. Of course - you may want to set up your build system to perform the above, but that's beyond the scope of this question.

Step 2: Using the embedded file

In your C++ source file, write:

#include 

extern "C" { extern unsigned char const* binaryfoobinstart; extern unsigned char const* binaryfoobinend; }

static const std::span foo { binaryfoobinstart, binaryfoobinend };

You can now use foo as a C++ container. If you prefer thinking of it as a span of std::byte's - that also possible, just a little more verbose:

static const std::span 
foo { 
    reinterpretcast(binaryfoobinstart),
    reinterpretcast(binaryfoobin_end)
};

After compiling this code, you must link it against bar.o. You'll note the C++ code is missing the underscoe prefix for the variable names; that's fine: The underscore is apparently added by the compiler for such extern symbols - and they will match symbols we saw in bar.o.

May 29, 2026 Score: 1 Rep: 1,186 Quality: Low Completeness: 70%

I'll just point out that https://github.com/graphitemaster/incbin exists and it provides decent portability (but not yet perfect, I was able to make it work on WASM after small changes).

#define INCBIN_PREFIX
INCBIN(std::byte, foo, "foo.bin");

This defines an equivalent of global variables std::byte[] fooData, std::byte* fooEnd and unsigned int fooSize (yes, there's a redundancy here) where fooData contains the file foo.bin as binary data. It searches for foo.bin in include directories.

Incbin is a macro-based single header. The project's README documents various macros that can be used to extend this basic use case, change naming etc.

May 29, 2026 Score: -1 Rep: 58,181 Quality: Low Completeness: 40%

You could use an application, or write your own, that converts a binary file into comma separated ASCII hex values, then include it into the source:

const uint8t mydata[] = { #include "mydata.txt" }; const static sizemydata = sizeof(mydata) / sizeof(my_data[0]);