This module is the fundament for the subset of ALib Modules that we call ALib Camps.
The three key aspects mentioned in the link above — 1) managing externalized resources, 2) accessing external configuration, and 3) defining a bootstrap process — are relevant to most software, even to small command-line tools. However, these areas are not well-supported by the C++ language and also the introduction of C++20-Modules left these challenges unresolved.
This module addresses these issues by introducing the virtual interface Camp, which is designed to handle these three concerns. Other ALib Modules already integrate this interface, and custom code units can also derive one or more types from it as needed.
In traditional application design, there is typically a centralized application class managing these responsibilities. From a broader perspective, ALib Camps offer an alternative by allowing the delegation of these tasks to the specific code entities responsible for them. Put simply, conventional software design often relies on each code unit being aware of a central application class that provides the necessary interfaces. This can lead to circular dependencies between the application class and the code units. One of the primary goals of this module is to eliminate such design limitations.
To implement the proposed functionality, class Camp relies on the aggregation of functionality stemming from other ALib Modules.
Let us look at them one by one:
For resource management, the class provides a pointer to an instance of the virtual class ResourcePool defined in the module ALib Resources. Derived types can use the inherited methods
to access externalized string resources conveniently.
In addition, this module extends the concepts imposed by the module ALib Resources by a resource compiler.
All details are explained in the chapter 4. Resource Compilation.
For external configuration data, the class provides a pointer to an instance of class Configuration, defined in the module ALib Variables. Derived types can retrieve this instance with method SharedConfiguration & GetConfig() and with that get access to ALib runtime variables.
In this context, one core feature of ALib Variables is that their values can transparently emerge from
This gives access to such data at the place where it is needed.
Note that runtime variables can also be defined by other code entities to share information between modules.
Implementing a well-defined bootstrap and shutdown process is the responsibility of the overarching module ALib Bootstrap. The namespace functions alib::Bootstrap and alib::Shutdown change their signature, depending on the inclusion of this module ALib Camp in the ALib Build. Without the inclusion of any ALib Camp, a heavily simplified bootstrap and shutdown process is in place, which calls corresponding bootstrap- and shutdown-functions of certain non-camp modules (among only a few other things).
The altered versions of the functions allow to either share or not share resources and configuration data between the different ALib Camps. This is why the internal field members resourcePool (inherited) and config are implemented as pointer types.
Furthermore, the altered functions divide the processes into different phases. For each phase, they invoke the abstract virtual methods Bootstrap, and Shutdown which have to be implemented by derived classes.
Finally, the bootstrap functions also make sure that the built-in ALib Camps are initialized in the right order, namely from lower-level camps to higher ones. The same applies for the shutdown process, but in reverse order.
All information about
is documented with the Programmer's Manual of module ALib Bootstrap.
When developing custom camps, the Programmer's Manuals of the modules ALib Resources and ALib Variables should be consulted as well. A good jump-start to copy from could be the straightforward reference implementation found in the ALib FileTree camp.
In the introduction to this manual, it was explained that the ALib Camp module introduces the Camp class. Each module designed to be an ALib Camp must provide a singleton instance derived from this class.
Below is a summary of the currently available camps in ALib:
As shown in the table, this very module creates its own singleton instance with alib::BASECAMP. Consequently, it qualifies as an ALib Camp itself.
A natural question might arise: why would a module like this, which mainly provides the interface class Camp, require any extensive setup, resources, or configuration?
The answer lies in the way this module subtly enhances functionality. When included in the ALib Build, it replaces some lower-level resources, primarily related to ALib Enum Records. Without this module, these records are usually hardcoded or just unavailable. By incorporating this module, the build process dynamically constructs these records during the module's bootstrap phase. This approach takes advantage of the externalized resources managed by the Basecamp class, doing away with the hard-coded implementation via preprocessor directives.
This improvement applies not just to enum records but also to a variety of format strings and exception-related messages (notably in the ALib Format and ALib System modules).
Additionally, during bootstrap, the Basecamp class reads and processes external ALib Configuration Variables.
For those curious about the full range of tasks this module performs, the source code of the Basecamp implementation serves as a useful reference.
For developers looking to create custom camp-modules, it is recommended to begin with the much simpler and more straightforward reference implementation found in the ALib FileTree camp.
This ALib Module extends the concepts of resource management introduced in the module ALib Resources by introducing a resource compiler for external resource description files of type .alibrc.
This allows resources to remain editable text files during development while still being converted into C++ code for production builds. The concept is deliberately flexible:
With these choices, projects may switch freely between immediate in-process loading, automatic source patching during development, and explicit pre-build compilation.
The development-time compiler, implemented by class DevtimeResourceCompiler, processes .alibrc resource files and registers the contained resources in a camp's resource pool. Optionally, it also generates C++ code that loads these resources efficiently at runtime. This combines the benefits of:
The authoritative description of the .alibrc syntax is given with function LoadResourceFile. The following summary reflects that parser and serves as an overview.
Each resource starts on a non-empty, non-comment line and follows the general scheme:
with optional whitespace around the optional '=' sign.
Keys
'='.Comments
'#' or '//' is ignored.Value Forms
'"' and preserve leading and trailing whitespace.'|' or '>' and continue on following indented lines.'~' and ignore whitespace outside quoted portions, which is useful for machine-parsed token streams that should remain human-editable.Escapes
\n, \t, \" and \\ are processed.Block Scalars
'|' preserves line breaks.'>' folds line breaks to spaces, while empty lines create paragraph breaks.Compact Scalars
'Key ~' defines a compact scalar on the same line.'Key ~|' starts a compact block that may span several indented lines.Example .alibrc file: The following example is taken from the resource-compiler tool itself:
For precise parsing rules and edge cases, consult the API documentation (or implementation) of the function alib::camp::LoadResourceFile.
The following workflow variants are supported:
Variant 1: Built-in compilation and source patching
During development, the configuration macro ALIB_CAMP_RESOURCE_COMPILATION may be defined. Then, at application startup, Do can
__FILE__)This keeps the generated code synchronized while the application is run during development.
Variant 2: Built-in load-only mode
If DevtimeResourceCompiler::Do is invoked without a cppFileName, the .alibrc file is always parsed and loaded into the resource pool, but no C++ file is touched. This mode is especially useful while testing resource texts, because modifications to the .alibrc file are picked up on the next start without requiring a recompilation and linking of the executable.
Variant 3: External build-step compilation
As an alternative, the dedicated executable ALibRC found in tools/ResourceCompiler can be built and invoked separately. This allows resource compilation to become an explicit custom build step, for example, in CMake-based projects. Compilation and installation details of that tool are documented 1. ALib Resource Compiler.
Typical built-in usage in camp bootstrap:
The method DevtimeResourceCompiler::Do takes:
If ALIB_CAMP_RESOURCE_COMPILATION is not defined, Do becomes a no-op and returns false. In such builds, resources are typically provided by generated code that was produced earlier, either by the built-in development-time workflow or by the external tool.
Example fallback method:
If a C++ file is to be patched, the file specified in cppFileName must contain two special marker comments:
All content between these markers is replaced with generated resource loading code. The markers themselves are preserved. The generated code uses BootstrapBulk with properly escaped string literals.
This requirement applies both to Do when cppFileName is given and to the external tool ALibRC.
For a complete example, see the resource compiler tool's source code itself. The tool defines resources to implement its command-line interface.
When source patching is enabled, the compiler uses file modification timestamps to avoid unnecessary work:
In load-only mode, this optimization is intentionally bypassed, because the .alibrc file is always read and registered at startup.
When regeneration is needed, a message is logged:
The typical integration pattern in a camp's bootstrap method:
For quick test cycles, the same code may omit cppFileName. In that case, the fallback method is usually not needed, because resources are taken directly from the .alibrc file on each start.
The external tool ALibRC provides the equivalent patching step outside the application. Its basic usage is:
It also supports validation-only runs:
The resource compilers perform validation and report errors with file/line information:
Errors are reported using ALib's error reporting system with the category "CAMP/RESCMP" or, when using the external tool, by corresponding command line error messages and exit codes.
The inclusion of this module with the header ALib.Camp.H, injects various overloads of the function variables::CampVariable(camp::Camp&) into the namespace alib::variables
of the module ALib Variables.
These functions are inline shortcuts used for creating variables associated with the Configuration instance found in a Camp.