Embedding Resources
Saturday, March 15, 2025
The distribution of Layers definitions is being reworked as a result of user-specified directories .
Part of this rework includes storing definitions with source code. It probably didn't make sense when definitions weren't included with the source considering they are a requirement.
Definitions will continue to be created and stored as human-readable JSON files. This is because they need to be distributable to users so they can create styles for their applications. However, the definitions bundled with Layers software can be compiled and embedded internally. This will be handled by a new resource system.
Resource Compiler
Layers will include a small application called the LayersResourceCompiler which will function essentially as a script for converting resource files into byte-arrays within C++ source files. These source files can then be compiled along with the software, effectively embedding the resource files.
Lets consider an example of how the compiler would be used. The example will consider how definitions for the QLayers library can be compiled.
A set of definitions should be stored directly with a project. I decided to just store them in a directory labeled definitions/ in the project directory:
QLayers/
├── definitions/
├── images/
├── include/
├── src/
├── QLayers.vcxproj
└── QLayers.vcxproj.filters
The following is an example of what the definitions/ directory contains:
definitions/
├── definitions.lrc
├── qlbox.json
├── qlcheckbox.json
├── qldialog.json
├── qllabel.json
├── qllineeditor.json
├── qlradiobutton.json
├── qlscrollbar.json
├── qlslider.json
└── qlwidget.json
The definitions.lrc file is a JSON-formatted resource file that identifies the desired resources that need to be compiled. The following is an example of what it contains:
{
"/definitions/qlayers": [
"qlbox.json",
"qlcheckbox.json",
"qldialog.json",
"qllabel.json",
"qllineeditor.json",
"qlradiobutton.json",
"qlscrollbar.json",
"qlslider.json",
"qlwidget.json"
]
}
The resource compiler should be invoked prior to project compilation. To do this with Visual Studio, add the following command to your project's Pre-Build Event in the project configuration:
"$(Layers)$(IntDir)LayersResourceCompiler.exe" "$(ProjectDir)definitions\definitions.lrc" "$(ProjectDir)definitions\definitions.cpp"
Once this command has been added, you should be able to build the project and generate the compiled resource file (definitions.cpp), which is a C++ source file containing byte-arrays of the resource data. Take note that you will need to add this file to your project and build the project again to officially make these embedded resources available.
The definitions.cpp file implements a function called initResources_definitions() that adds the resources to the Layers Resource Manager. The part of the function identifier after the underscore is deduced from the filename. This function is only implemented and must be manually invoked. Use a forward declaration like the following to make the function available:
namespace Layers {
namespace Resources {
extern void initResources_definitions();
}
}
Resource Manager
Layers will include a resource manager in the form of the LResourceManager class. This class will implement the singleton pattern, with a convenience macro lResourceManager for accessing the manager instance.
The resource initialization function implemented by the compiled resource file adds the resources to the resource manager.
In my other Layers projects, I like to handle resource initialization with a general static-initializer. The following is an example from my QLayers library:
// From .../QLayers/src/qlayers_init.cpp
void initialize_resources()
{
// Initialize Qt resources
Q_INIT_RESOURCE(images);
// Initialize Layers resources
Layers::Resources::initResources_definitions();
}
QLAYERS_NAMESPACE_BEGIN
Initializer::Initializer()
{
initialize_resources();
lController.include_internal("/definitions/qlayers");
qDebug() << "QLayers: Loaded Definitions";
}
static Initializer qlayers_initializer;
QLAYERS_NAMESPACE_END
In the example above, initialize_resources() calls the initResources_definitions() function, making the definitions available in the resource manager. The include_internal() call loads those definitions into the Layers controller.