ALib C++ Framework
by
Library Version: 2605 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
ALib Module Camp - Programmer's Manual

1. Introduction

This module is the fundament for the subset of ALib Modules that we call ALib Camps.

Please Read Now:
To avoid redundancy, we would ask you to read the short chapter 2.6 ALib Camps of the Programmer's Manual of ALib, which gives an explanation to this statement.

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.

2. Class Camp

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:

2.1 Resource Management

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.

2.2 Configuration Data

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

  • command-line parameters,
  • environment variables,
  • configuration files, or
  • any other external configuration system.

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.

2.3 Bootstrapping and Shutdown

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

  • how default bootstrapping works,
  • how an application can alter this default to adjust the distribution of different configuration and resource management instances, and
  • how a using code may inject own ALib Camps into these processes,

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.

3. The Basecamp

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:

Module Camp Class Singleton Instance
ALox alib::lox::ALoxCamp alib::ALOX
ALib Camp alib::camp::Basecamp alib::BASECAMP
ALib App alib::app::AppCamp alib::APP
ALib Expressions alib::expressions::ExpressionsCamp alib::EXPRESSIONS
ALib FileTree alib::filetree::FilesCamp alib::FILETREE

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.

4. Resource Compilation

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:

  • Built-in development-time compilation: class DevtimeResourceCompiler may parse a .alibrc file during program startup, register the resources immediately and, optionally, patch a C++ source file with generated bulk-load code.
  • Load-only development mode: if the optional parameter cppFileName of Do is omitted, resources are always taken directly from the .alibrc file and no source file is modified. This is useful for fast test cycles because no recompilation is needed after changing resources.
  • External build-step compilation: the standalone tool ALibRC, located in directory tools/ResourceCompiler, performs the same source generation outside the running application and can be integrated into custom workflows or build systems such as CMake.

With these choices, projects may switch freely between immediate in-process loading, automatic source patching during development, and explicit pre-build compilation.

4.1 Class DevtimeResourceCompiler

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:

  • Maintainability: resources are defined in editable .alibrc text files..
  • Performance: production builds can avoid runtime file I/O and parsing.
  • Flexible workflows: the same resource description can be used in-process or in external build steps.
  • Type safety: resources integrate with ALib's type-safe resource access.

4.2 The .alibrc File Format

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:

Key [=] Value

with optional whitespace around the optional '=' sign.

Keys

  • Keys consist of printable ASCII characters and end at whitespace or '='.
  • The double quote character is not allowed in keys.
  • The maximum key length is 511 characters.

Comments

  • A line whose first non-whitespace characters are '#' or '//' is ignored.

Value Forms

  • Quoted values: start with '"' and preserve leading and trailing whitespace.
  • Plain values: consume the rest of the line, with trailing whitespace removed.
  • Block scalars: start with '|' or '>' and continue on following indented lines.
  • Compact scalars: start with '~' and ignore whitespace outside quoted portions, which is useful for machine-parsed token streams that should remain human-editable.

Escapes

  • Backslash escapes such as \n, \t, \" and \\ are processed.

Block Scalars

  • '|' preserves line breaks.
  • '>' folds line breaks to spaces, while empty lines create paragraph breaks.
  • Block content ends when indentation falls back to the indentation level of the key line
  • Trailing empty lines are ignored.

Compact Scalars

  • 'Key ~' defines a compact scalar on the same line.
  • 'Key ~|' starts a compact block that may span several indented lines.
  • Outside quoted parts, whitespace is ignored completely.

Example .alibrc file: The following example is taken from the resource-compiler tool itself:

10
11AppInfo |
12 @HL-ALib Resource Compiler V. {}.{}
13 (c) 2023-{} AWorx GmbH. Published under MIT License (Open Source).
14 For more information, see: https://alib.dev/alib_mod_resources.html
15 @HL-
16
17MSNGRCFILE No resource file name given.
18MSNGCPPFILE No C++ file name given.
19
20NOTACCESSIBLE File {!Q} is not accessible.
21UKNFILE Unknown error with file {!Q}.
22ISDIR Given path {!Q} is a directory instead of a file.
23
24DryRunStart ALibRC dry-run mode.
25DryRunEnd ALibRC dry-run done.
26
27//-------------------- CLI Commands/Options/Parameters/ExitCodes --------------------
28ARCO< ALibRC::Options::
29//enum ident minread identChar in-arg-separ. args to consume ShortcutTo
30ARCO ~|
31 1, validate ,1, v, = ,0 ,
32
33
34ARCE< ALibRC::ExitCodes::
35ARCE ~|
36 101,ErrMissingRCFilename ,
37 102,ErrRCFileNotFound ,
38 103,ErrRCFileNotAccessible ,
39 104,ErrMissingCPPFilename ,
40 105,ErrCPPFileNotFound ,
41 106,ErrCPPFileNotAccessible ,
42 107,ErrInResources
43
44TExit101 No input resource file name given.
45TExit102 Given resource file not found.
46TExit103 Resource file is not readable.
47TExit104 No C++ source file to patch given.
48TExit105 Given C++ resource file not found.
49TExit106 C++ file is not writable.
50TExit107 The resource file is erroneous.
51
52//-------------------- Help Texts --------------------
53HlpCLIAppName ALibRC
54
55HlpUsage "alibrc resourcefile cppfile"
56
57HlpGeneral >
58 \nABOUT ALibRC\n
59 @>>This is a tool provided by the C++ Framework ALib that compiles external resources and inserts
60 a corresponding code snippet into a C++ source file.
61 The resources are specified in a resource file, while the C++ source file must exist and
62 contain special markers for the insertion position.\n
63 All details are given in the documentation of the ALib C++ Framework at:\n
64 https://alib.dev/
65 \n\n@<<
66
67THlpCmdSht_validate Parses the given resource file and checks for errors.
68THlpCmdLng_validate > Parses the given resource file and reports any errors.
69 No C++ code is generated and the cpp-file (if given) is not modified.
70
71TOptUsg_validate --validate resourcefile [cppfile]
72TOptHlp_validate >
73 If this option is given (anywhere in the command line), the resource file is only parsed and no
74 C++ code is generated. The cpp-file parameter is ignored and can be omitted.
75
76
77TOptUsg_format --format[=]\"placeholders\"
78TOptHlp_format >
79 Sets the output format. The format specification is given with the documentation of the
80 ALib method CalendarDateTime::Format, found here:\n
81 https://alib.dev/classalib_1_1strings_1_1util_1_1CalendarDateTime.html
82
83THlpParSht_rc-file The '*.alibrc' input file.
84THlpParLng_rc-file =>
85 Denotes the input file, usually of extension '*.alibrc'. This is a mandatory parameter to give.
86
87

For precise parsing rules and edge cases, consult the API documentation (or implementation) of the function alib::camp::LoadResourceFile.

4.3 Workflow Options

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

  1. resolve the .alibrc file path, mostly given relative to the file calling the function (specified with preprocessor macro __FILE__)
  2. If a cpp-file to patch is given, compare the file dates. If the cpp-file is newer than the resource file, do nothing.
  1. Parse the resource file,
  2. register the resources in the camp's resource pool, and
  3. patch a designated C++ source file with generated bulk-load code.

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:

// with debug-builds, invoke the resource compiler and execute 'bulkloadResources()' only if
// it returned 'false'.
#if ALIB_DEBUG
if(!dtrc.Do( "alibrc.alibrc", __FILE__,
APP, APP.ResourceCategory, true, // allow replacements
__FILE__ )
)
#endif
bulkloadResources();

The method DevtimeResourceCompiler::Do takes:

  • alibrcFileName: path to the .alibrc resource file
  • callingFile: reference file for path resolution, typically __FILE__
  • campInstance: the camp whose resource pool receives the resources
  • resourceCategory: category name for resource registration
  • allowReplacements: determines whether duplicate keys may replace existing entries
  • cppFileName: optional path to the C++ file to patch

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:

void Sample::bulkloadResources() {
// ALIB-RESOURCE-COMPILER-REPLACEMENT-START
// ALIB-RESOURCE-COMPILER-REPLACEMENT-END
nullptr );
}
virtual void BootstrapBulk(const nchar *category,...)=0

4.4 Target File Requirements

If a C++ file is to be patched, the file specified in cppFileName must contain two special marker comments:

// ALIB-RESOURCE-COMPILER-REPLACEMENT-START
... generated code replaces this section ...
// ALIB-RESOURCE-COMPILER-REPLACEMENT-END

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.

4.5 Timestamp-Based Re-compilation

When source patching is enabled, the compiler uses file modification timestamps to avoid unnecessary work:

  • If the C++ file is newer than the .alibrc file (with 10ms tolerance), no action is taken
  • This allows checking out or copying files without triggering recompilation
  • Only when the .alibrc file is genuinely modified does regeneration occur

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:

CAMP/RESCMP: Compiling resources:
Source: /path/to/sample.alibrc:1
Target: /path/to/sample.cpp:1

4.6 Integration with Camps

The typical integration pattern in a camp's bootstrap method:

void MyCamp::Bootstrap(BootstrapPhases phase) {
if (phase == BootstrapPhases::PrepareResources) {
camp::DevtimeResourceCompiler rc;
if (!rc.Do("myresources.alibrc", __FILE__,
*this, this->ResourceCategory, false,
"mycamp.cpp")) {
// Patching didn't occur:
// production build, source already up to date, or an error.
// In such cases, load the previously generated code instead.
bulkloadResources();
}
// Bootstrap enum records and other resource-dependent components
enumrecords::bootstrap::Bootstrap<MyEnums>();
}
}

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:

alibrc resourcefile cppfile

It also supports validation-only runs:

alibrc --validate resourcefile [cppfile]

4.7 Error Handling

The resource compilers perform validation and report errors with file/line information:

  • Illegal resource name: Non-ASCII or invalid characters
  • Name too long: Exceeds 511 character limit
  • Line ended in quotes: Missing closing quote
  • Duplicate resource name: The same key appears more than once in the input
  • Missing markers: Target C++ file lacks required markers
  • File not found: .alibrc or target C++ file does not exist
  • Access problems: input or target files are not readable or writable

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.

4.8 Best Practices

  1. Choose the workflow deliberately: use load-only mode for fastest text iteration, built-in patching for convenient local synchronization, or ALibRC for explicit build integration
  2. Version control: Commit both .alibrc files and generated C++ sections
  3. File organization: Place .alibrc files alongside the C++ files they generate or the camp sources that consume them
  4. Relative paths: Use relative paths and __FILE__ for portability
  5. Comments: Use comments in .alibrc files to document resource purpose
  6. Testing: Test both the direct .alibrc loading path and the generated-code path to ensure they stay synchronized

5. Extensions For ALib Variables

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.