Corrade::Utility::Resource class

Access to compiled-in resources.

This class provides access to data files compiled into the executable using the corrade_add_resource() CMake macro or the corrade-rc utility.

Resource compilation

Resources are organized in groups, where a group is a set of files that are encoded in a hexadecimal form into a single *.cpp file which is then compiled alongside your other sources.

The corrade-rc executable and the corrade_add_resource() CMake macro take a configuration file as an input, which lists files to be compiled as resources. All filenames are expected to be in UTF-8. A configuration file can look for example like this, with syntax matching what Configuration understands:

group=game-data

[file]
filename=license.txt

[file]
filename=../resources/intro-new-final.ogg
alias=intro.ogg

[file]
filename=levels/insane.conf
alias=levels/easy.conf

The group is an identifier that you'll subsequently pass to the Resource() constructor, each [file] section then describes one file to be compiled in, with paths relative to location of the configuration file. By default, the filename is the name under which the files will be available when calling getRaw() or getString() later, including any directory separators. Use the alias option to override the name.

There can be just one resource group or several, organization of the files is completely up to you. For example, if there's a set of files used only by a particular library but not other parts of the application, it might be useful to have them in a dedicated group. Or if there's a lot of files, you might wish to split them up into multiple groups to speed up the compilation and reduce compiler memory use.

Using CMake

Assuming the above file was named resources.conf, the following CMake snippet will compile the referenced files into a C++ source stored inside the build directory. Its filename gets saved into a ${MyGame_RESOURCES} variable, which subsequently gets passed to the add_executable() call:

corrade_add_resource(MyGame_RESOURCES resources.conf)

add_executable(MyGame  ${MyGame_RESOURCES})

The corrade_add_resource() macro also takes care of dependency management — if either the configuration file or any files referenced by it are changed, it triggers a recompilation of the resources, same as with usual C++ sources.

The variable name also acts as a name used for symbols in the generated file — it has to be a valid C identifier and has to be unique among all resources compiled into the same executable. But apart from that, you'd need the name only if you deal with resources in static libraries as explained below.

Compiling the resources manually

If you're not using CMake, you can execute the corrade-rc utility manually to produce a C++ file that you then compile together with your project. The following invocation would be equivalent to the above CMake macro call:

corrade-rc MyGame_RESOURCES path/to/resources.conf output.cpp

This will generate output.cpp in current directory, which you then compile together with your sources. The first parameter is again a name used for the symbols in the generated file.

Resource null termination and alignment

By default, resource data are tightly packed right after each other. Use nullTerminated=true to mark them null-terminated, for example to pass strings to C APIs without having to make a null-terminated copy first, or to use them with Containers::String::nullTerminatedGlobalView().

The resources are compiled in a char[], meaning you can't rely on them being aligned in any way by default. Specify an align option to override this, allowed values are power-of-two values up to 128, with 1 being the default.

These options can be specified either globally or for particular [file] section, which will then override the global setting. For example:

group=data

# Files will be null-terminated and unaligned by default
nullTerminated=true

[file]
filename=shader.vert

[file]
filename=shader.frag

# This file is four-byte aligned but doesn't need null termination
[file]
filename=shader.spv
nullTerminated=false
align=4

Accessing the resources

If you compiled the resources directly into an executable or into a shared library, you can access them from the C++ code without having to do anything else. First instantiate the class with a group name matching the group value in the configuration file, and then access the files by their filenames:

Utility::Resource rs{"game-data"};

Containers::StringView licenseText = rs.getString("license.txt");
Containers::ArrayView<const char> soundData = rs.getRaw("intro.ogg");


std::istringstream in{rs.getString("levels/easy.conf")};
Utility::Configuration easyLevel{in};

Because the data are coming from a readonly memory inside the executable itself, the class returns non-owning views. In most conditions you can assume unlimited lifetime of the data, see getRaw() and getString() for details.

Resources in static libraries

If you compile the resources into a static library, the linker will implicitly treat the data as unreferenced and won't include them in the final executable, leading to a not-found assertion during Resource construction. To prevent this, you need to reference them. This can be done using the CORRADE_RESOURCE_INITIALIZE() macro, to which you pass the symbol name used in the CMake macro or command-line invocation earlier:

int main(int argc, char** argv) {
    CORRADE_RESOURCE_INITIALIZE(MyGame_RESOURCES)

    
}

It's important to call it outside of any namespace, otherwise you'll get a linker error. The main() function is ideal for this, or you can create a dedicated function outside a namespace and then call it from within a namespace.

Overriding compiled-in resources

For shorter turnaround times when iterating on compiled-in resources it's possible to override them at runtime using overrideGroup(). That way you won't need to wait for a recompilation, relink and restart of the application when making changes — instead you tell the application to fetch the data at runtime from the same location the resource compiler would, by pointing it to the original resources.conf file on disk.

Resource instances created after that point will parse the configuration file and fetch the data from there, falling back to the compiled-in resources if the files are not found in the override or if there's an error. The files get cached for the lifetime of a particular Resource instance, any subsequent changes in files thus get picked up only next time a Resource instance is created.

Utility::Resource::overrideGroup("game-data", Utility::Path::join(
    /* Assuming resources.conf is next to this C++ source file */
    Utility::Path::split(Utility::Path::fromNativeSeparators(__FILE__)).first(),
    "resources.conf"
));



Utility::Resource rs{"game-data"};

/* This now loads license.txt from a file instead */
Containers::StringView licenseText = rs.getString("license.txt");

Memory access and operation complexity

Resource registration (either automatic or using CORRADE_RESOURCE_INITIALIZE()) is a simple operation without any heap access or other operations that could potentially fail. With the exception of overrideGroup(), no memory allocation or heap access is involved when constructing a Resource instance or calling any of its APIs. If overrideGroup() is used, getRaw() and getString() accesses the filesystem and allocates.

The group lookup during construction and hasGroup() is done with a $ \mathcal{O}(n) $ complexity as the resource groups register themselves into a linked list. Actual file lookup after is done in-place on the compiled-in data in a $ \mathcal{O}(\log{}n) $ time.

Thread safety

The resources register themselves into a global storage. If done implicitly, the registration is executed before entering main() and thus serially. If done explicitly via CORRADE_RESOURCE_INITIALIZE() / CORRADE_RESOURCE_FINALIZE(), these macros have to be called from a single thread or externally guarded to avoid data races. Same goes for the overrideGroup() function.

On the other hand, all other functionality only reads from the global storage and thus is thread-safe.

Public static functions

static void overrideGroup(Containers::StringView group, Containers::StringView configurationFile)
Override a group.
static auto hasGroup(Containers::StringView group) -> bool
Whether given group exists.

Constructors, destructors, conversion operators

Resource(Containers::StringView group) explicit
Constructor.

Public functions

auto list() const -> Containers::StringIterable
List of all files in the group.
auto hasFile(Containers::StringView filename) const -> bool new in Git master
Whether a file is present in the resource group.
auto getRaw(Containers::StringView filename) const -> Containers::ArrayView<const char>
Get resource data.
auto getString(Containers::StringView filename) const -> Containers::StringView new in Git master
Get resource data as a string.
auto get(const std::string& filename) const -> std::string deprecated in Git master
Get resource data as a string.

Function documentation

static void Corrade::Utility::Resource::overrideGroup(Containers::StringView group, Containers::StringView configurationFile)

Override a group.

Parameters
group Group name
configurationFile Filename of the configuration file. Use an empty string to discard a previously set override.

Overrides compiled-in resources of given group with live data specified in given configuration file. See Overriding compiled-in resources for more information.

static bool Corrade::Utility::Resource::hasGroup(Containers::StringView group)

Whether given group exists.

Corrade::Utility::Resource::Resource(Containers::StringView group) explicit

Constructor.

Expects that the group exists.

Containers::StringIterable Corrade::Utility::Resource::list() const

List of all files in the group.

The resource group has no concept of a directory hierarchy — if filenames in the input configuration file contain path separators, the returned list will contain them verbatim. The returned strings all have Containers::StringViewFlag::Global set, but are not Containers::StringViewFlag::NullTerminated.

Note that the list contains only the compiled-in files, no additional filenames supplied by an overriden group are included. This is done to avoid overrides causing unexpected behavior in code that assumes a fixed set of files.

bool Corrade::Utility::Resource::hasFile(Containers::StringView filename) const new in Git master

Whether a file is present in the resource group.

The filename is expected to be in in UTF-8.

Containers::ArrayView<const char> Corrade::Utility::Resource::getRaw(Containers::StringView filename) const

Get resource data.

Expects that the resource group contains given filename. If the file is empty, returns a zero-sized nullptr view. The filename is expected to be in in UTF-8. Unlike with Path::read(), and unless the file is coming from an overriden group, no OS-specific treatment of non-null-terminated strings nor any encoding conversion is done — this function never allocates.

Unless the file is coming from an overriden group, the returned view can be assumed to have unlimited lifetime, otherwise it's alive only until the next overrideGroup() call on the same group. The data pointer is aligned according to the align option if it was set for given file. If the file is coming from an overriden group however, its alignment isn't respected at the moment.

Containers::StringView Corrade::Utility::Resource::getString(Containers::StringView filename) const new in Git master

Get resource data as a string.

Expects that the resource group contains given filename. If the file is empty, returns a zero-sized nullptr view. The filename is expected to be in in UTF-8. Unlike with Path::read(), and unless the file is coming from an overriden group, no OS-specific treatment of non-null-terminated filenames nor any encoding conversion is done — this function never allocates.

The returned string has Containers::StringViewFlag::Global set unless it's coming from an overriden group, otherwise it's alive only until the next overrideGroup() call on the same group. The data pointer is aligned according to the align option if it was set for given file. If the file is coming from an overriden group however, its alignment isn't respected at the moment.

The returned string is Containers::StringViewFlag::NullTerminated if the nullTerminated option was set for given file, or if it's coming from an overriden group. Otherwise it may be marked as null terminated if there's some padding after in order to align the next file, but such behavior is specific to the internal implementation and thus null termination isn't guaranteed.

std::string Corrade::Utility::Resource::get(const std::string& filename) const

Get resource data as a string.