The ALib C++ Framework offers several modules that directly support building complete applications:
Together, these four modules already form a coherent foundation for building applications. This fifth top-level module presented here brings all of them together under a unified umbrella, which offers several key benefits:
As a user of ALib, the most important effect is that you save substantial time when getting started and when maintaining your applications over the long term.
The central type of this module is class App that implements a linear state machine with pre-implemented helper facilities. For the CLI-processing, class CommandLine is provided. While its basic initialization and use is nicely abstracted in class App, the class can also be used stand-alone. Thus, this class and its helpers are documented in the later chapter 6. The Command Line Interface Implementation.
While most ALib modules are primarily optimized for orthogonality and performance, this module pursues a slightly different goal: it aims to make the application’s startup, run, and shutdown flow explicit and easy to follow. In other words, the code itself is structured as a step-by-step guide to what an application does when it runs.
To achieve this, the central class alib::app::App orchestrates a simple, linear state machine that drives your application through all relevant phases: bootstrap, run, and shutdown. The philosophy is to provide a clear, overridable sequence of small methods that you can extend or replace without having to re-implement infrastructure.
As common with other frameworks, class App is designed to be a base class for your application class and its method App::Main is the entry point:
At the heart of class App is inner class StateMachine with its internal "program" represented by the field App::StateMachine::Program. The fields' type StateMachine::CommandList is a vector of commands, each tying a "state" to the method that should execute at that state.
The built-in states are listed in enumeration App::States and roughly follow this pattern:
The actual execution order is defined by the underlying integral value of the field StateMachine::Command::State.
Custom applications may define their own enumeration type of custom states and add them, along with a custom method to be executed, to the program vector. Custom methods can be a regular member functions of your derived type and do not need to be virtual. Adding states has to be done before calling App::Main. The constructor of the derived application class is a good place to do this. Finally, adding a custom state can be conveniently done with StateMachine::CommandList::Add (which is using some C++20 template magic).
The following sample demonstrates this:
All built-in steps are implemented as virtual methods. You can override any of them to customize behavior or keep defaults where appropriate. Typical hooks include:
| Phase | Methods | Description |
|---|---|---|
| Bootstrap | onBsSetCamps | Calls alib::BootstrapAddDefaultCamps (see ALib Camp). |
| onBsPrepareResources | Initialize resource pools and output buffers. | |
| onBsSetNameVersionAndInfo | Collects name/version/info strings(optionally via resources). | |
| onBsPrepareConfig | Calls alib::Bootstrap passing BootstrapPhases::PrepareConfig. | |
| onBsPreloadVariables | Allows preloading variables which should later be written to a configuration file, even if they have not been used during the execution of the application. | |
| onBsCLIDefine | Defines command-line processing arguments. | |
| onBsCLIReadOptions | Read command-line options. This is done early to allow influencing the configuration sources in the next state. | |
| onBsImportConfig | Load configuration data. | |
| onBsConfigureCLI | Finalizes CLI after configuration import. | |
| onBsFinalizeBootstrap | Finalize common bootstrap. | |
| onBsReadDryRunOption | Read dry-run option and set field dryrun. | |
| onBsSetupALox | Set up logging. (The default behaviour is manipulateable with Flags.) | |
| Run | onRunStart | Optional pre-step around an application's main logic. |
| onRun | Provides the default CLI processing loop. May be overridden if an application omits CLI processing. | |
| onRunEnd | Optional post-step around an application's main logic. | |
| Shutdown | onSdAnnounceShutdown | Calls Shutdown passing ShutdownPhases::Announce. |
| onSdCleanALox | Cleans the logging subsystem as established in onBsSetupALox. | |
| onSdExportConfig | Exports the configuration data (if wanted). | |
| onSdOutput | Write collected application output. | |
| onSdFinalizeShutdown | Calls Shutdown passing ShutdownPhases::Destruct. |
Because these methods are virtual, you can keep the structure but replace the implementation of any step, or even insert/remove steps by editing the program vector as described above.
Configuration files
The default flow
On shutdown, only new values or variables with writeback-flag are exported, honoring per-file export lists.
This module offers two complementary channels for producing text:
The key idea is to keep user-facing console output minimal and deterministic, while providing rich diagnostics when needed or verbosity is requested. This is especially important for command-line tools that print results for end users or for further processing (e.g., piping into other tools).
Buffers Paragraphs * cOut (for standard output) and Paragraphs * cErr (for error output) are foremost designed for command-line tools that print results for end users or for further processing (e.g., piping into other tools).
In contrast, ALox log output is primarily for diagnostics and typically includes meta-information that is not suitable for machine consumption via pipes.
For classic CLI-style applications, the recommended policy is:
Other applications may still mix the two: use cOut/cErr for user-visible results and employ logging for additional detail where formatted log output is helpful. Just be deliberate to avoid confusing users with interleaved formats on the console.
Buffered user output:
Selective logging (release/debug-logging, different verbosity levels):
With these conventions, command-line tools remain predictable and composable (clean stdout/stderr), while developers and power users can still access detailed diagnostics via logging when needed.
Some applications want to stop processing “right now” and return a well‑defined exit code without plumbing return values through many layers of code. For this common scenario, the module provides the exception type ControlledEarlyExit.
In short:
This replaces scattered “if (err) return err;” checks with a single, explicit signal that is understood by the application framework. Because the exception is caught internally, user code does not need to add handlers unless it wants to perform cleanup or logging at the throw site.
Important details:
Both approaches work together: mapping handles the "usual" errors, while ControlledEarlyExit covers the intentional, well-known early-termination path without return-code plumbing.
CLI support is implemented with a few separated types placed in this namespace/module. While these classes may be used directly (standalone), class App makes their use much easier. All details of the CLI interface are given in the later chapter 6. The Command Line Interface Implementation.
Every instance of App contains:
The built-in state machine contains these CLI-related steps:
All four hooks are virtual. Applications commonly override the first three of them by invoking the default implementation first and then performing the similar steps adding/handling the custom commands and options.
Then method processCLICmd is to be implemented to perform the actual work.
Applications that do not want CLI behavior can replace the methods with empty implementations and directly override the method onRun.
The default CLI bootstrap already defines a small standard vocabulary:
The default run path then provides these behaviors:
This is intentionally modest. Derived applications are expected to add their own commands and options and to override virtual bool processCLICmd(Command*) for the real work.
The default virtual void onBsCLIDefine() registers the built-in enums with class CommandLine. Derived applications typically extend that definition step with their own declarations. For this purpose, custom enumeration types have to be provided, which are resourced accordingly.
The best approach to do this, is to extend the sample application shown in the chapter 5. Sample Application. Otherwise, all details are given in chapter 6. The Command Line Interface Implementation.
Configuration file selection a further feature of the regular App workflow:
The default implementation of virtual void getConfigFilePathsFromCLIParam(StdVectorMA<ConfigFileDescriptor>&) interprets --config as a comma-separated list of file names. Entries left as Default... keep the resource-defined path at that position. Descendants may override the method to support different CLI syntax.
This preserves resource-based defaults while still giving users a straightforward way to redirect configuration storage.
The default virtual void onRun() is a simple CLI loop:
This gives derived types two common extension patterns:
The class CLIUtil provided with this module can generate rich help pages and write them into cOut:
Its output for command help may look as follows:
----------------------------------------------------------------------
ALib Resource Compiler V. 2605.0
(c) 2023-2026 AWorx GmbH. Published under MIT License (Open Source).
For more information, see: https://alib.dev/alib_mod_resources.html
----------------------------------------------------------------------
ABOUT ALibRC
This is a tool provided by the C++ Framework ALib that compiles external resources and inserts a corresponding code
snippet into a C++ source file. The resources are specified in a resource file, while the C++ source file must exist
and contain special markers for the insertion position.
All details are given in the documentation of the ALib C++ Framework at:
https://alib.dev/
USAGE:
alibrc resourcefile cppfile
COMMANDS:
* ALibRC help [TOPIC]
Shows general help or help on topics
* ALibRC version
Shows the version of this software.
OPTIONS:
--help[[=]TOPIC]
--version
-d|--dryrun[=yes|no]
--verbose[="level"]
--validate resourcefile [cppfile]
EXIT-CODES:
1: ErrNoCmdGiven
No command given.
3: ErrParsingOption
Unknown option {!Q} was given.
4: ErrParsingCommand
An error occured when parsing the command {!Q}:
5: ErrMissingCmdParam
Missing a mandatory parameter {!Q} of the given command {!Q}.
6: ErrMissingOptParam
Missing a mandatory parameter {!Q} of the given option {!Q}.
7: ErrBadParamValue
Parameter {!Q} is not suitable for command or option {!Q}.
101: ErrMissingRCFilename
No input resource file name given.
102: ErrRCFileNotFound
Given resource file not found.
103: ErrRCFileNotAccessible
Resource file is not readable.
104: ErrMissingCPPFilename
No C++ source file to patch given.
105: ErrCPPFileNotFound
Given C++ resource file not found.
106: ErrCPPFileNotAccessible
C++ file is not writable.
107: ErrInResources
The resource file is erroneous.
Note that for creating this output, not a single line of custom code has to be written. Instead, the help texts are dynamically generated from the defined commands, options, parameters and exit codes and their according resource strings.
Even though the class App includes CLI support by default, an application that does not expect commands or options is easy to create:
This keeps the common case convenient without forcing a command model onto every application.
The various small details of classes that need to be reflected when using App and CommandLine, is best handled by using a sample application as a jump-start.
A very small sample application is provided with the ALib C++ Framework which implements a command-line tool that reports the current date or the modification date of a file. The declaration of the class looks like this:
The full header file can be seen here: src.samples/App/sample.hpp. Its implementation is provided in the file src.samples/App/sample.cpp.
When an application becomes more complex, especially by the additional burden of defining some resource strings, we recomment to use the sources of the ResourceCompiler Tool as a jump-start.
From here, your custom application can be developed step by step, and things should become relatively easy and clear.
The compiler is a rather simple tool whose parsing and code-generation logic is encapsulated in the the module ALib Camp and hence just a little application-specific code is contained in the project.
This tool is found in the folder:
ALIB_BASE_DIR/tools/ResourceCompiler
App class for the most common use cases, because it provides a convenient way to set up the CLI and to perform the actual work.The types introduced in this chapter support parsing and processing command-line parameters. Such processing is not only useful with "pure CLI commands" (shell applications) but also with software that only optionally receives arguments, may it be daemons or GUI-driven applications.
This manual and library uses four terms for CLI input:
Command line parameter "languages" are in most cases not defined in very consistent ways, for example, in comparison to programming languages. In contrast, they are very much dependent on the field of application. In favor of simplicity of usage, in most cases no consistent grammar and syntax definition exists. Different "commands" sometimes share "parameters", others do not and often "parameters" may be given without a command. Then, commands and options/parameters are often allowed to be given in arbitrary order. Finally, options may allow to omit the hyphens and so forth.
Therefore, by definition, helper libraries like this one, can never support each and every use case and any unforeseen tweak that a programmer wants to implement for his specific CLI.
As a result, the software found in this module:
This means, that using the types provides basic tools and probably a guideline on how to implement the CLI processing, but not a full-featured CLI processor that just needs a grammar definition input and some callback functions.
This sort of "incompleteness" can also positively phrased: The implementable grammar and syntax of the CLI interface is open for implementing any weird interface and not limited to a certain scheme.
One major burden that this ALib Module may relieve a programmer from, is that CLI command names and other syntax tokens, and probably more important, the error messages and help texts that the CLI interface produces, are duly externalized and thus can be easily translated into other human languages. This is achieved by the provision of an ALib Camp and thus leveraging inner namespace alib::resources of the module ALib Resources.
The classes found in this namespace make a lot of use of ALib Enum Records retrieved from an instance of type ResourcePool, usually the one found in the singleton of the associated Camp.
Therefore, it is good advice to get familiar with the features provided by inner namespace alib::resources of the module ALib Resources as a start. That module in turn uses so-called Resourced ALib Enum Records which are provided with the module ALib EnumRecords.
Or, the other way round: The tutorial found below might also be a good starter to understand externalized resources and ALib EnumRecords in their basics, and therefore the better advice might be to just continue reading.
The CLI-types of this module ALib App offer the following features:
As a preparation to parsing command-line arguments, instances of the following structs are to be created and duly filled with data:
Please use the links above and quickly read the general descriptions of the types.
Such struct definition is considered static data, and as such is usually stored in so-called resources. How resourced data is used to automatically create and fill the structs is shown in the next chapter 6.3. Tutorial Sample.
All enums which are using resourced ALib Enum Records are demanding a specific record type to be associated to a custom enumeration.
In addition, for such custom enum types, a specialization of ResourcedTraits, has to be provided, which is used to load further resource strings.
The following table summarizes the types, the associated ALib Enum Record type and the additional resource strings that need to be available:
| Object Type | Record Type To Use | Additional Resource Strings |
|---|---|---|
| CommandDecl | ERCommandDecl | "THlpCmdSht_NN": Short version of help text for each parameter. "THlpCmdLng_NN": Long version of help text for each parameter |
| ParameterDecl | ERParameterDecl | "THlpParSht_NN": Short version of help text for each parameter. "THlpParLng_NN": Long version of help text for each parameter. |
| OptionDecl | EROptionDecl | "TOptUsg_NN": Help text for each option describing its usage. "TOptHlp_NN": Help text for each Option. |
| ExitCodeDecl | ERSerializable | "TExit_NN": A format string returned by method const String & FormatString() . |
With the above resourced definitions in place, class CommandLine is used to bootstrap all enum records to be ready for parsing the command-line.
To avoid cluttering this types' interface, various helper methods have been put into friend class CLIUtil.
See the reference documentation of types CommandLine and CLIUtil for details.
Alternatively, just follow the tutorial provided in the next chapter.
Due to the usage of resourced strings, and even more due to the usage of Resourced ALib Enum Records, to start a new project using the command-line facilities of this module becomes more complex than one might think.
The best way out is the provision of a "template" project, which can be used for jump-starting into an own development.
Such template is provided with this tutorial chapter!
The sample application is a little similar to the GNU/Linux "date" command. Its functionality is described as follows:
With this project scope in mind, let's start coding.
Commands, options, parameters, and exit codes of our software are easily defined with simple C++ enumerations:
The little magic comes with assigning ALib Enum Records to these enumerations using the helper macros. For each of the enumerations, a corresponding record type is defined with this module:
This assignment entitles the main class of this module to "know" how to parse the objects from the command-line.
The enum records that just have been assigned to the enums need to be filled with values. Such values can be automatically loaded from resources. To define such resources, the easiest way is to create a custom ALib Camp.
This is the declaration of the camp in the header file:
ALib Camps are singleton objects, and as such we create one global instance:
Now, as the camp is defined, a second macro is used which tells ALib where to find the resources for each enumeration:
The code shown above, was all from the header file. Now we turn to the implementation part. Let's start with the implementation of the custom Camp. It is full of resource strings!
Being an experienced programmer, if you have a brief look at the string data defined in the code above, the general idea about the sort of information that is provided should become clear. Together with the macros used in the headers, parts of the resources are now "attached" to the C++ enums.
Now, we are set to implement function main()!
To make this easier to read, we pull the command processing into a separate function, which is forward-declared like this:
With this extraction of the custom command processing itself, function main becomes quite short and straightforward.
Finally, we can look at our custom command processing function. Also here, just note the source comments. Everything should be quite understandable:
You might have noticed that this function uses static utility methods of class CLIUtil to generate help texts for general use, or specifically for commands, options and parameters.
After compiling this sample to binary "Sample" it can be invoked from the command-line. Lets have some tries:
1. Invocation without parameters
This is already a special case that method processCLI explicitly handles with the same code as if parameter now was given.
Sample
The result shown on the console is:
2026-05-13 15:19:26<Exit-code 0>
2. Command 'now'
With command now
Sample now
the same result is shown:
2026-05-13 15:19:26<Exit-code 0>
(Note that the date displayed in this tutorial is when the unit tests were run the last time before creating the documentation with doxygen.)
3. With option 'format'
Option format allows specifying a date format, as documented with Format. The format string can be given concatenated to the option name using an equal sign '=' or just as a next argument (with a whitespace instead of the equl sign):
Sample --format="dd MMM YYYY"
Outputs:
May 13, 2026<Exit-code 0>
4. Command 'file'
The command file expects a next command-line argument (separated by whitespaces), that specifies a file or directory name. We choose "/home" here and hope that this is available on the machine where this documentation was generated:
Sample file /home
Outputs:
2026-04-30 13:10:43<Exit-code 0>
5. Erroneous input
Lets see what happens if we omit the mandatory parameter of command file:
Sample file
Outputs:
Error: no filename given with command 'file' Usage: date file FILENAME<Exit-code 102>
Similar to this, we might omit the parameter of option format :
Sample --format
Outputs:
E1: <app::MissingOptionValue>
Missing argument(s) of option <format> found with CLI argument[0]="--format".
Expected 1, given 0.
[@ /hub/projects/ALib/src/alib/app/cliargtypes.cpp:90 from 'Option::Read()' by 'MAIN_THREAD(-1,0x00007F1FDA4247C0)']
E2: <app::ParsingOptions>
Error parsing option. Usage:
--format[=]"placeholders"
[@ /hub/projects/ALib/src/alib/app/cli.cpp:87 from 'CommandLine::ReadOptions()' by 'MAIN_THREAD(-1,0x00007F1FDA4247C0)']<Exit-code 10>
In contrast to the previous output, this one shows a very different error message. The reason for this is as follows: While the missing parameter of command file is detected by our simple sample program (which generates a quick error message), the missing mandatory parameter of option –format can be detected by the CLI system internally and automatically. Here, the library throws an Exception, which is "annotated" with further information while it is passed through the unwinded execution stack.
Consequently, this sample should be extended to fetch and analyze the internal exceptions and create a similar output as in the first case. We kept this here as it is to a) keep the sample code simple and b) demonstrate the power of ALib Exceptions in respect to the generation of human-readable, formatted error output.
More complex projects that use this library should follow the approach to use and throw ALib exceptions anywhere possible. Only in the very end (for example, at the end of the main() method), such exceptions should get "translated" to one of the more general exit codes that command-line software usually returns. The benefits: Error handling is then all in one place, and internally it is all done nicely with C++ exception handling.
A last erroneous input we want to test here is the provision of unknown commands or options:
Sample unknown
outputs:
Error: Unknown command given "unknown"<Exit-code 100>
while
Sample --unknown
shows:
Error: Unknown option given "--unknown"<Exit-code 101>
6. Help texts
The sample of this tutorial does not show too much code that generates help messages. The reader might be surprised about the output of
Sample --help
The library is able to generate the following output:
----------------------------------------------------------------------
Command line tool 'date'. V. 2511.0 (in fact a sample application
only)
(c) 2023-2026 AWorx GmbH. Published under MIT License (Open Source).
For more information, see: https://alib.dev
----------------------------------------------------------------------
ABOUT date
This is a sample application provided with C++ library 'ALib'
to demonstrate the use of its module "ALib CLI".
USAGE:
date [format="FORMATSPEC" [now]|[file FILENAME]
COMMANDS:
* date now
Reports the actual date/time
* date file FILENAME
Returns the date/time of a file.
* date help [TOPIC]
Displays usage information.
OPTIONS:
--format[=]"placeholders"
--help[[=]TOPIC]
EXIT-CODES:
0: OK
Success (no error).
100: ErrUnknownCommand
An unknown command was given. Valid commands are 'now' and 'file'
101: ErrUnknownOption
An unknown option was given. The only valid option is '--format='FORMATSPEC'.
102: ErrMissingFilename
Command 'file' given without a filename argument.
103: ErrUnknownHelpTopic
Command or option 'help' given without an unknown subtopic.
255: ErrInternalError
Unspecified internal error.<Exit-code 0>
Furthermore, help topics are supported. Possible topics are commands, options and parameters as shown in these final samples:
Sample --help now
----------------------------------------------------------------------
Command line tool 'date'. V. 2511.0 (in fact a sample application
only)
(c) 2023-2026 AWorx GmbH. Published under MIT License (Open Source).
For more information, see: https://alib.dev
----------------------------------------------------------------------
Help on command <now>:
USAGE: date now
DESCRIPTION:
Reports the actual date/time. May be omitted, as this is the
default if no command is given.<Exit-code 0>
Sample --help file
----------------------------------------------------------------------
Command line tool 'date'. V. 2511.0 (in fact a sample application
only)
(c) 2023-2026 AWorx GmbH. Published under MIT License (Open Source).
For more information, see: https://alib.dev
----------------------------------------------------------------------
Help on command <file>:
USAGE: date file FILENAME
DESCRIPTION:
Returns the last modification date/time of a file.
PARAMETER DESCRIPTION:
* FILENAME
Mandatory parameter of command 'file.<Exit-code 0>
Sample --help format
----------------------------------------------------------------------
Command line tool 'date'. V. 2511.0 (in fact a sample application
only)
(c) 2023-2026 AWorx GmbH. Published under MIT License (Open Source).
For more information, see: https://alib.dev
----------------------------------------------------------------------
Help on option <format>:
USAGE: --format[=]"placeholders"
DESCRIPTION:
Sets the output format. The format specification is given with
documentation of ALib method CalendarDateTime::Format, found here:
https://alib.dev/classalib_1_1strings_1_1util_1_1CalendarDateTime.html<Exit-code 0>
Independent of the use of this ALib Module, the original command-line strings are always available through the global ALib variables alib::ARG_C, alib::ARG_VN and alib::ARG_VW (if set by the user's main()-function properly).
Class CommandLine exposes field OptionArgsIgnored, that hold all CLI arguments that start with a hyphen '-' and are not recognized by OptionDecl. software might either generate an error if unrecognized options are left (just like the tutorial-sample of the previous chapter does), or pass this list to other parts and libraries that software is using..
With the use of camp ALib Variables such use of CLI arguments outside this camp already occurs: Configuration variables can be defined using CLI parameters the same as by using environment variables, configuration files or other data sources. Such use is still be possible, independent of the CLI interface.
The other way round, arguments may be removed from the internal list held by this class, prior to having this class processing the arguments! Note that this is a must for arguments that do not start with a hyphen (and are addressing other software parts), because such arguments would lead to "unknown command" errors. Such removal has to be done after the invocations of the various Define methods and prior to invoking virtual void ReadOptions() .
For each component type, class CommandLine provides a method to undefine single elements, namely UndefineCommand, UndefineOption, UndefineParameter, and UndefineExitCode. By using these methods, such components are not recognized by the CLI processing and will not be displayed in the generated help messages.
This can be used, for example, if software consists of different building blocks where some foundational elements like exit codes or options are provided, while some derived components want to disable a part of these. As an example, class App of module ALib App offers some built-in features like option –dryrun, that a using code may want to disable and/or redefine.