Very often we need to include into our programs some resources like textures, sounds, static data etc. Usually, we distribute them with the programs as files. However, if you want to distribute the application as a single executable, it may become a bit tricky.
Classical approach for GCC
The most elegant way to do it, was described here. It works perfectly but only on Linux with GCC.
Portable approach
If you want a portable approach for including your resources,
you can use cmake
and generate a header file with the resources as strings.
Let's create a template with name resource.h.in
:
#ifndef RESOURCE_H
#define RESOURCE_H
#include <string_view>
namespace app {
static std::string_view kResource = "@RESOURCE_DATA@";
}
#endif // RESOURCE_H
In your CMakeLists.txt
you should read the resource from its file and use configure_file
to generate a header:
file(READ ${CMAKE_CURRENT_BINARY_DIR}/path/to/resource.data RESOURCE_DATA HEX)
configure_file(resource.h.in ${CMAKE_BINARY_DIR}/app/resource.h @ONLY)
We need to represent our data as hex-digits, overwise your binary data breaks the C++ strings. It isn't a big deal, you can later convert it into a normal string in your C++ code:
auto hex_str_to_str = [](std::string hex) {
auto len = hex.length();
std::transform(hex.begin(), hex.end(), hex.begin(), toupper);
std::string new_string;
for (int i = 0; i < len; i += 2) {
auto byte = hex.substr(i, 2);
char chr = ((byte[0] - (byte[0] < 'A' ? 0x30 : 0x37)) << 4) + (byte[1] - (byte[1] < 'A' ? 0x30 : 0x37));
new_string.push_back(chr);
}
return new_string;
};
auto data = hex_str_to_str(std::move(hex));
Unfortunately, if your resources are big, you may meet this error on Windows:
C2026: string too big, trailing characters truncated
It happens if your string is bigger than 16380 chars.
Annoying… but it's possible to work around the problem.
We can reformat the strings in Python:
"""Script to reformat long string for Windows"""
import sys
STEP = 16000
if __name__ == "__main__":
filename = sys.argv[1]
split = []
with open(filename, "r") as file:
while True:
chunk = file.read(STEP)
split.append(chunk)
if len(chunk) < STEP:
break
with open(filename, "w") as file:
file.write('"\n"'.join(split))
and run it in our CMakeLists.txt
:
file(READ ${CMAKE_CURRENT_BINARY_DIR}/path/to/resource.data RESOURCE_DATA HEX)
configure_file(resource.h.in ${CMAKE_BINARY_DIR}/app/resource.h @ONLY)
execute_process(COMMAND python3 ${CMAKE_SOURCE_DIR}/cmake/split_string.py ${CMAKE_BINARY_DIR}/app/resource.h)
Of course, you need Python on your machine now. If it is an issue, you can find out how to do it in pure-CMake as well.
Top comments (0)