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.

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 itself to fetch the data from the same location the resource compiler would, by pointing it to the original resource.conf file on disk:

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"
));

Resource instance created after this point will parse the configuration file and fetch the data from there, or fall back to the compiled-in resource on 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 an instance is created.

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. When using only the hasGroup() and getRaw() APIs with compile-time string literals, no memory allocation or heap access is involved either.

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::Array<Containers::StringView>
List of all files in the 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, which is useful during development and debugging. Subsequently created Resource instances with the same group will take data from a live filesystem instead and fallback to compiled-in resources only for files that are not found in the overriden file.

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

Constructor.

Expects that the group exists.

Containers::Array<Containers::StringView> 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.

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

Get resource data.

Expects that the group contains given filename. If the file is empty, returns a zero-sized nullptr view. If the file is not 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 filename is expected to be in in UTF-8. Unlike with Path::read(), no OS-specific treatment of non-null terminated strings nor any encoding conversion is done — this function never allocates.

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

Get resource data as a string.

Expects that the group contains given filename. If the file is empty, returns a zero-sized nullptr view. If the file is not coming from an overriden group, the returned string has Containers::StringViewFlag::Global set, otherwise it's alive only until the next overrideGroup() call on the same group. The returned string is not Containers::StringViewFlag::NullTerminated.

The filename is expected to be in in UTF-8. Unlike with Path::read(), no OS-specific treatment of non-null terminated strings nor any encoding conversion is done — this function never allocates.

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

Get resource data as a string.