ALib C++ Library
Library Version: 2510 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
ALib Programmer's Manual

1. Introduction

Welcome the Programmer's Manual of the ALib C++ Class Library!

While we are proud of presenting ALib, we are likewise proud of providing you with not only this manual, but with additional separated manuals which are dedicated to the different parts of the library - which we call "ALib Modules".

This foundational manual explains the structure and concepts of the library, how to select only parts of it and build your stripped and lean version, and of-course how the library is used in general.

The first thing we need to discuss is how the library is structured into ALib Modules. This became especially true, since C++20 likewise introduced the concept and even the new keyword module, which this library supports seamlessly.

2. ALib Modules

As a "general purpose" C++ library, ALib tackles a wider range of programming areas, which is why its code and documentation are systematically structured in a top-down layered hierarchy.
For this reason, the library was divided into currently 25 different modules, a step taken well in advance of the introduction of C++20's module concept.

An ALib module:

  • Covers a certain topic or field of application.
  • Is a subset of the overall list of ALib types.
  • Comes with a dedicated Programmer's Manual as well as complete reference documentation.
  • Occupies a dedicated inner namespace of the overarching outer namespace alib.
  • Has two defined relationships to other ALib Modules, namely "mandatory dependencies" and "optional dependencies."
  • Has one main header to include the main types of the module in your compilation unit and optionally has one or more additional sub-headers. Header names always start with "ALib." and end with ".H"

As a sample, let us look at module ALib Strings:

Well, this is all quite normal, but we wanted to point out here that we care for a simple but reliable and everywhere complete structure and offering.

2.1 The Term "ALib Build"

Within this manual, within the distinct manuals of the ALib Modules and within the reference documentation, the term "ALib Build" is frequently used.

Definition
An ALib Build defines the "permutation of modules" that have been selected from the overall set of modules available. Such selection may optionally be performed at the time of compiling the library. In case it is not performed, simply all ALib Modules are included in the ALib Build.

It might be in a user's interest to reduce ALib to a subset of its functionality, and this way speed up compilation and decrease library footprint.

It is to be noted that the omission of single modules might have "side effects" on included modules: While they are still compilable and function well - just certain features might be dropped!
This is due to what we call an "optional dependency". An optional dependency from module A to module B could be phrased as:
"Module A provides extended functionality in case that module B is included in the ALib Build."

As a sample, ALib Files can be compiled with the absence of module ALib Expressions. But in this case functionality for filtering file trees using run-time expressions will not be available.

The reference documentation of functions, methods, or types that are dropped when reducing the ALib Build gives (in most cases) a prominent hint on this specific relationship. To stay with the sample, you can look out for such a note at the end of the reference documentation of class FileExpressions.

2.2 ALib Module Hierarchy

With the knowledge we now have about ALib Modules and ALib Builds, we can continue this manual with a graph that shows all ALib Modules and their mandatory and optional dependencies:

Note
Currently the graph-generation tool draws weird lines between the modules. We don't know why this happens and hope that it will be fixed in the future.

Besides the topmost Module ALib Bootstrap, the following foundational and lowest-level modules, which were excluded from the graph above, are always included in any ALib Build:

This graph is a good reference for answering various questions. I.e., "Which other modules will be included in an ALib Build if I need module Xyz?" For example, if a user aims to include the functionality of the module ALib ThreadModel in an application, the graph tells us that in addition, the modules ALib Boxing, ALib Containers and ALib Monomem are directly dependent. Furthermore, the module ALib Boxing has a next dependency to ALib Singletons. This means, together with the module ALib ThreadModel itself, five selectable modules are included.

This is not the full picture, yet: On the bottom of the graph there are seven foundational modules which are always included in any ALib Build.

The good news is, that the ALib build system will care about this automatically. To stay in the sample, all you have to do is announce the module ALib ThreadModel. The scripts will resolve the mandatory dependencies automatically.
Modules with optional dependencies have to be added explicitly. In this sample, we would recommend also adding the module ALib Strings. Details on this are given in the later chapter 5. Building The Library.

2.3 Tabular List Of Modules

The following table lists all ALib Modules in alphabetical order. The table includes links to the reference documentation as well to the Programmer's Manual of each module.

Name
(Programmer's Manual)
Namespace
(Reference Documentation)
Description
ALox alib::lox A very unique and powerful approach to implement software's debug and release logging facilities.
Assert alib::assert Raising assertions, warnings and other messages. Available only with debug-builds, respectively when the compiler-symbol ALIB_DEBUG is given.
BitBuffer alib::bitbuffer Provides types to read and write data into a stream of bits in a platform-independent manner. Furthermore different compression algorithms are given.
Bootstrap alib Well defined bootstrap mechanisms for ALib as well as - optionally - for consumeers of the library. A phased approach that, for example, to assure availablility of resources and configuration data at defined stages. This module is always ranked highest in the list and has optional dependencies to each module that needs bootstrapping.
Boxing alib::boxing Implements "auto-boxing" for C++, known from higher level programming languages. Transparently wraps C++ values of arbitrary type together with run-time type information into very lightweight objects. In other words, this library provides "type-safe void pointers which support virtual function calls", provides type-safe a variadic argument paradigm and much more.
Camp alib::camp Introduces the concept of ALib Camps, which a ALib Modules that manage externalized resources, configuration variables and which support extended bootstrapping strategies.
Characters alib::characters Mitigates C++ character type, character pointer and character array hell.
Containers alib::containers ALib container types. Furthermore tools to enable the containers of the C++ Standard Library to use alib allocators are included.
CLI alib::cli Implements parsing and partly the processing of command line arguments (CLI stands for "command line interface"). In case of pure CLI software, provides tools for formatted and localized help and result output.
EnumOps alib::enumops Unary and binary operators for C++ enum elements.
EnumRecords alib::enumrecords Named (printable) C++ enum elements.
Exceptions alib::exceptions C++ exceptions which allow the collection of information while unwinding the stack. Furthermore, externalized (resourced) user messages, leveraging ALib formatting capabilities to embed dynamic information about the errors.
Expressions alib::expressions Expression parser, compiler and a virtual machine for evaluation of compiled expressions. Excels in respect to speed and flexibility and is very easy to adopt in own projects.
Files alib::files Directory and File scanner, optionally filtering with ALib Expressions.
Format alib::format String formatting.
Lang alib::lang The most foundational module of ALib, providing specific to the (modern) C++ language.
Monomem alib::monomem Monotonic allocation utilities to avoid heap-memory usage. Together with the concepts introduced with module ALib Containers, the amount of potential use cases for monotonic allocation are tremendously extended. Note that the cost of heap allocation is largely underestimated by programmers.
Resources alib::resources Mangement of - optionally externalized - static string resources.
Singletons alib::singletons Implements the singleton paradigm that overcomes the Windows OS DLL boundaries (caused by Window DLL's local data segments), while under other operating systems chooses a faster, straight-forward implementation.
Strings alib::strings With a least intrusive design offers compatible, lightweight, secure, efficient, complete and convenient C++ string classes.
System alib::system Class Path and a few other types related to operating systems.
ThreadModel alib::threadmodel High-level multithreading tools.
Threads alib::threads Low-level thread primitives.
Time alib::time Types for calendrical and non-calendrical date and time processing. Always included in any ALib Build.
Variables alib::variables Transparently encapsulates access to configuration or other input data coming from various sources, like command line parameters, environment variables, INI-files, 3rd-party configuration databases, etc. Support for persistent write-back of such data is given.

2.4 Including/Importing ALib Modules

2.4.1 How Including/Importing Works

ALib employs a design that allows switching between C++20 Modules and legacy builds without requiring any changes to your codebase.

This is achieved by not supporting (or requiring!) the direct use of the keyword import to access ALib modules. Instead, the same header files for both compilation modes are to be used and to be included in the traditional way.

These headers manage the necessary preprocessor configuration (which are still needed with C++20 Modules) and either import modules or include traditional headers based on your chosen compilation mode. This approach also ensures that ALib's macro-based features (like type trait specialization helpers) work consistently across both modes.

This design approach not only ensures that a user's codebase remains independent of the compilation mode (aka whether C++20 Modules are used or not), but also offers additional advantages:

  • It maintains compatibility with environments and compilers that don't yet fully support C++20 modules.
  • Legacy compiles are facing a highly reduced set of includes. (Before the transition, ALib mostly followed the widespread "one type one header" approach).
  • In C++20 module mode, no extra header inclusion (to define necessary preprocessor symbols) is needed.
  • The headers automatically adopt to the ALib Build. This means, if certain (optional) ALib Modules are excluded from the library, the headers will silently skip otherwise mandatory imports, respectively recursive inclusion.
    Only if then dropped functionality is used, compilation errors will occur. (Those can be then managed in the using code by using the guard symbols ALIB_BOXING, ALIB_STRINGS, ALIB_MONOMEM, etc.)
  • There is no noticeable difference between the modular and legacy versions - both produce identical optimized code.
Note
For those interested, we share our observations and experiences from transitioning the ALib codebase to support C++20 Modules in this dedicated blog-page.

The header names that either import a C++20 Module or recursively include necessary sub-headers always are prefixed by "ALib.", followed by the module name in UpperCamelCase, and have the extension ".H". Sometimes after the module name, an extension name is given, separated by a period '.'. For example:

    #include "ALib.Strings.H"        
    #include "ALib.Strings.Tokenizer.H"        
    #include "ALib.Format.H"        
Note
All other filenames in the ALib codebase use lowercase letters, and neither the ".H" nor ".h" file extensions are used elsewhere in ALib. This design choice was deliberately made to help your IDE to easily propose and auto-complete your inclusion statements.
Attention
As a result of this design, the ALib documentation never references the C++20 Module entities directly, and never talks about their names. While the names are irrelevant to the code using them, it should be mentioned that their names correspond exactly to the filenames of the header files to include.
The names become relevant only in the case that a compilation error like this occurs:
  error: declaration of 'CalendarDateTime' must be imported from module 'ALib.Strings.Calendar' before it is required  
All you need to do in this case is to take the name, append ".H" and use this as your inclusion header to add:
  #include "ALib.Strings.Calendar.H"        

2.4.2 Include/Import Header Reference

The following table lists all header files available.

Module Headers to Include
ALox ALib.ALox.H
ALib.ALox.Impl.H
Assert ALib.Lang.H (No own include)
BitBuffer ALib.BitBuffer.H
Bootstrap ALib.Bootstrap.H
Boxing ALib.Boxing.H
ALib.Boxing.StdFunctors.H
Camp ALib.Camp.H
ALib.Camp.Base.H
Characters ALib.Lang.H (important type traits are imported here already)
ALib.Characters.Functions.H
Containers ALib.Containers.FixedCapacityVector.H
ALib.Containers.HashTable.H
ALib.Containers.List.H
ALib.Containers.LRUCacheTable.H
ALib.Containers.SharedPtr.H
ALib.Containers.SharedVal.H
ALib.Containers.StringTree.H
CLI ALib.CLI.H
EnumOps ALib.EnumOps.H
EnumRecords ALib.EnumRecords.H
ALib.EnumRecords.Bootstrap.H
Exceptions ALib.Exceptions.H
Expressions ALib.Expressions.H
ALib.Expressions.Impl.H
Files ALib.Files.H
ALib.Files.TextFile.H
Format ALib.Format.H
ALib.Format.FormatterJavaStyle.H
ALib.Format.FormatterPythonStyle.H
ALib.Format.Paragraphs.H
ALib.Format.PropertyFormatter.H
ALib.Format.PropertyFormatters.H
Lang ALib.Lang.H
ALib.Lang.CIFunctions.H
ALib.Lang.CIMethods.H
Monomem ALib.Monomem.H
ALib.Monomem.SharedMonoVal.H
ALib.Monomem.StdContainers.H
Resources ALib.Resources.H
Singletons ALib.Singletons.H
Strings ALib.Strings.H
ALib.Strings.AutoSizes.H
ALib.Strings.Calendar.H
ALib.Strings.Escaper.H
ALib.Strings.Monomem.H
ALib.Strings.Search.H
ALib.Strings.StdFunctors.H
ALib.Strings.StdIOStream.H
ALib.Strings.Token.H
ALib.Strings.Tokenizer.H
ALib.Strings.Vector.H
System ALib.System.H
ThreadModel ALib.ThreadModel.H
Threads ALib.Threads.H
Time ALib.Time.H
Variables ALib.Variables.H
ALib.Variables.IniFile.H
ALib.Variables.Plugins.H
ALib.Variables.ResourcePool.H

2.4.1 Further Public Headers

Besides the headers which either import or include portions of an ALib Module, a few further public headers are present.

  1. "Compatibility Headers"
    Five so called "compatibility header-files" exist. More information on those are given in chapter 5.5.4 External Library Dependencies.
  2. Switching Macro ALIB_CALLER
    The two headers ALib.Lang.CIFunctions.H and ALib.Lang.CIMethods.H are provided to switch the macro ALIB_CALLER to either work with functions and static methods, where no this pointer is available, or to work with method members of types. More information on this topic is provided in the appendix-chapter A.5 Collecting Caller Information.

2.5 Special Treatment For Module ALox

A special treatment is granted to the module ALox, by having the header-file ALib.ALox.H still defining the logging macros, even if this module is not included in an ALib Build.

This allows a user to place ALox log statements in a compilation unit without the need of testing preprocessor symbol ALIB_ALOX or the use of macros IF_ALIB_ALOX and IFNOT_ALIB_ALOX. Such custom sources are this way still ready to compile against an ALib Build that does not include ALox.

If the module ALox is not included, the macros are "empty" and this way, all log-statements just get "pruned".

Note
This is similar to the behaviour of macros like ALIB_LOCK and ALIB_LOCK_WITH, which remain available but empty, even with single-threaded compilations of the library.

2.6 ALib Camps

To close this section, we quickly want to explain the term ALib Camp.

Definition
An ALib Camp is an ALib Module which uses the facilities provided with the equally named module ALib Camp. These facilities include three things:
  1. Access to externalized string resources,
  2. access to external configuration data, and
  3. A well-defined bootstrap and shutdown process invoked at the start and termination of the software process.

In other words: ALib offers a module named ALib Camp, and that subset of other modules which rely on it, are called "ALib Camp Modules" or just ALib Camps.

If you look at the hierarchy graph of the previous section, you will quickly identify module ALib Camp and the camp-modules that build on it.

Today, those are:

More on this topic is given with the dedicated Programmer's Manual ALib Camp.

3. Bootstrapping And Shutting Down ALib

Bootstrapping a software process can often become complex, especially when various parts of an application fail to align seamlessly.

The C++ language itself does not offer a standard approach for initiating or managing the startup process effectively. As a result, larger projects frequently postpone creating their own bootstrapping mechanisms until later stages of development, leading to unnecessary difficulties and inefficiencies.

To address this, ALib not only provides an interface for bootstrapping the library but also introduces a structured concept that users can optionally integrate into their projects.

This bootstrapping approach is encapsulated as a dedicated ALib Module, with all related details and usage instructions discussed thoroughly in its dedicated Programmer's Manual.

4. Multithreading Support

ALib offers two distinct modules for multi-threading support: ALib Threads and ALib ThreadModel. The ALib ThreadModel module is a higher-level, optional component that can be included or excluded from an ALib Build as needed.

In contrast, the ALib Threads module operates at a much lower level. Like foundational modules such as ALib Lang or ALib Time, it cannot be excluded from the build. This module is relatively compact, containing the Thread class, along with various mutex implementations, such as Lock.

Now, the library can be compiled using the ALIB_SINGLE_THREADED preprocessor (and CMake) symbol. Single-threaded applications should do this, because with that, some superfluous overhead
is dropped from the build.

But even then, parts of the ALib Threads module are still available when compiled in single-threaded mode: They remain as empty skeleton types and methods.
Additionally, several corresponding preprocessor macros, for example, ALIB_LOCK_WITH, are preserved but are likewise emptied.

This approach offers significant advantages: The library code remains uncluttered, and your code benefits as well. By utilizing the macros and types provided by ALib, you can develop applications, which likewise support single-threaded and multi-threaded versions with minimal effort.

All further information on this topic is provided with the dedicated Programmer's Manual of module ALib Threads.

5. Building The Library

5.1 Overview

5.1.1 Platforms and Toolchains

The current release was compiled and tested under the following platforms and toolchain combinations:

  • GNU/Linux Arch 6.16.8, Clang++ 20.1.8, C++20/23, 32-Bit / 64-Bit, optional C++20 Module Support
    (This is the main development platform.)
  • GNU/Linux Arch 6.16.8, GNU C++ 15.2.1, C++20/23, 32-Bit / 64-Bit
  • WindowsOS 11, MSC 19.44 (Visual Studio 2026 Insiders, Platform v145), C++20, 32-Bit/64-Bit
  • WindowsOS 11, MinGW, 64-Bit, GCC 13.47, C++20
  • macOS Tahoe 26.0, Apple M2 / ARM64, Apple Clang Version 17.0.0, C++20/23, 64-Bit
  • Raspberry 3, aarch64, Cortex-A53, GNU C++ 12.2.0, C++20/23
  • Raspberry 4, aarch64, Cortex-A72, GNU C++ 12.2.0, C++20/23
  • Raspberry 4, armhf (32-bit), Cortex-A72, GNU C++ 12.2.0, C++20/23

All development was performed in a) CLion/CMake (all platforms) and b) Visual Studio Community Edition 2022. All necessary IDE files are included in the repository.

Adoptions to other platforms, toolchains, and IDEs should be implementable with limited efforts. All relevant code which selects platform/toolchain specific libraries, intrinsics, etc., will expose a preprocessor error if a section fails due to an unknown environment. This allows quickly inserting the right platform/toolchain specific code at these places.

Note
We would be very happy to receive your feedback/input on necessary changes for other platforms!

Especially errors occurring in internal header lang/integers.inl might be quite likely for unknown platform / toolchain combinations. Here, a set of five compiler-symbols might be passed using the build system (e.g., CMake), which are documented with symbol ALIB_SIZEOF_INTEGER.

The C++ compiler warning level is defaulted to the bearable maximum. This means, that the inclusion of ALib headers into a custom project's compilation process should never lead to compilation warnings or errors when similar high custom warning levels (of the including project) are used.

5.1.2 Reference Toolchain CMake

While project files for different IDEs might be provided with the codebase, the main development of ALib is performed using CMake scripts. Also, the relationship of source file and ALib Module selection is "officially defined" by CMake scripts.

The CMake scripts coming with ALib are to be seen as the reference guideline to building ALib. The scripts may even be more precise (and up to date!) than the documentation you currently read itself.

Even for non-experienced users (in respect to CMake), the syntax of the file should be easily understood. So, do not hesitate to open and read the CMake files for your project setup reference.

A Microsoft Visual Studio solution and the according project files which build an ALib DLL and the unit tests and sample applications, are included in the ALib project folder. These may be used to compile an ALib library that includes all of ALib, which can be used for own projects.
Limited library projects that include only a selection of the modules of ALib are not provided and thus have to be created if they are desired.

5.1.3 Library Installation

As of today, no installation process of a shared library version of ALib is available with the build process defined. Not installing a library has of course the disadvantage, that every software project needs to compile its own version of it, and the library is by default not shared between applications, even if compiled as a "shared library", respectively DLL.

While this may change in the future, the advantage of this approach is that an application has a lot of flexibility in respect to compiling ALib using the exact set of features it desires. And then: installing libraries that use C++20 Modules (if ALib is compiled so) is currently not even a well-defined process. It seems, due to the latter, the trend goes a little into the direction to have a dedicated compilation of C++ libraries per application.

Therefore, to enable software to use ALib, the sources of the library have to become a part of the build process in any form. As usual, there are three possible basic options:

  1. Compiling ALib to a static library,
  2. compiling ALib to a shared library or
  3. compiling ALib sources directly into another software entity.

When this manual section talks about "building the ALib library", one of the three options is meant.

Note
Of course, a custom installation process can be established to place library binary and header files in corresponding system folders. It is just not provided today.

5.1.4 Unit Tests

An extensive set of unit tests is included in the distribution, along with a corresponding CMake script to build those.

5.2 Performing A Test Compilation

Clone the ALib repository from ALib at GitHub to a place where you commonly store 3rd-party libraries that your projects are using. Alternatively, download and unpack the ZIP file to that same place.

In this documentation, we will refer to this library location as the ALIB_BASE_DIR. After a fresh installation, it should contain at least the following subfolders:

    ALIB_BASE_DIR/build
    ALIB_BASE_DIR/docs
    ALIB_BASE_DIR/src
    ALIB_BASE_DIR/src.samples
    ALIB_BASE_DIR/tools

To build the unit tests, perform the following steps:

  • open a console window and cd into directory:
      ALIB_BASE_DIR/build/cmake/unittests
    
  • create two subfolders from here, one named debug and the other release
  • cd into directory debug and type
      cmake ..
    
  • once CMake has run, type
      make -j
    

The compiled sample executable should have been created and you can start it with

./ALib_UT

Within a few seconds all unit tests should have been performed.

For a release build, the steps are similar. After you cd into the 'release' folder, the cmake command is:

cmake -DCMAKE_BUILD_TYPE=Release ..
Note
For the unit tests, Google gtest gets downloaded (once) and incorporated as a source project within the binary folder. This means:
  • This library does not need to be installed anywhere else on the system
  • This library gets deleted when deleting the CMake build folder (in the sample above named "debug" respectively "release").

5.3 A Step-By-Step CMake Sample

With using CMake, compiling and using ALib is very straight forward. This is because a set of easy to use CMake scripts is provided, of which one is to be included into the custom CMake script.

The following demonstrates this step-by-step along the sample project found in folder

    ALIB_BASE_DIR/src.samples/alox


Step 1: Creating the CMake file
A custom CMake file has to be created. To start with, the top of the file might look like this:

cmake_minimum_required(VERSION 3.20) # For C++ 20 module compilation, V. 3.28 is needed
project("ALox.Samples")


Step 2: Choose ALib Modules
The list of ALib modules to be included in the ALib Build is defined with CMake list variable ALIB_BUILD which has to be set before invoking the ALib CMake script. If the list is left empty, it will be defaulted to "ALL", which chooses all ALib modules available.
In our sample, we add "ALOX", which chooses module ALox and all dependent modules.

list( APPEND ALIB_BUILD "ALOX" )


Step 3: Set Other Feature Variables
Our project should be compiled using C++20. This is set with:

list( APPEND ALIB_COMPILER_FEATURES "cxx_std_20" )


Step 4: Include "ALib.cmake"
Now we are ready to include the main ALib CMake script:

include( ${CMAKE_CURRENT_LIST_DIR}/../../ALib.cmake )

Note that this sample is using a relative path. In a real-world sample, the path might as well be an absolute one. After the script is run, some global CMake variables and functions are defined. All of those are documented in the next chapter. Let us first continue the sample:


Step 5: Define A Library Project
We invoke CMake function ALibAddSharedLibrary, which creates a CMake target called "ALib_SharedLib" having all necessary settings:

ALibAddSharedLibrary()


Step 6: Define The Custom Project
Now we are good to define our custom project in a usual way:

add_executable ( ALoxSamples ${ALIB_BASE_DIR}/src.samples/ALox/sample.cpp )


Step 7: Add Compiler And Linker Settings
Our main project has to share some ALib compiler and linker settings with the ALib library project. To achieve this we invoke CMake function ALibSetCompilerAndLinker:

ALibSetCompilerAndLinker ( ALoxSamples )


Step 8: Add Library Project To Custom Project
The final step is to add the library project to the custom sample project, which is again a standard CMake task:

target_link_libraries ( ALoxSamples PRIVATE ALib_SharedLib )

That's it. With this simple CMake file we have created a tailored ALib Build (selection of modules) and have linked it to our sample project, which is now ready to be built!

5.4 The Build Process In Detail

The previous chapter demonstrated the use of the CMake script ALib.cmake provided with ALib. In the following sections a reference documentation on all aspects of the script is given.

Along these lines, the build requirements of ALib is explained - also for users of any other build system. independent of the build-system used, the following information has to be collected and accordingly set:

  • The set of ALib source files that comprise the desired permutation of ALib Modules, what we call an ALib Build.
  • C++ Version 20 or higher (to be set with the compiler).
  • compiler-symbols that select the ALib Modules have to be passed to the compiler.
  • compiler-symbols that choose ALib features have to be passed to the compiler.
  • External library dependencies have to be determined.
  • Linker flags have to be defined.
Note
The complete set of symbols that the ALib library accepts from the compiler (usually -D option) are documented here.

5.4.1 C++20 Module Version

Attention
The C++20 Module compilation is considered experimental. While it seems stable and very reliable under GNU/Linux with an actual Clang compiler, we did not succeed in getting it to work with the GCC or MSVC compiler yet.

All that is needed to be done is to place

    set( ALIB_C20_MODULES   "On" )

in your CMake script, before script ALib.cmake is invoked.
Everything described in the following sections remain the same. As discussed in chapter 2.4 Including/Importing ALib Modules of this manual, the using code also remains the same.

A sort of blog-text, describing some experience we made when transitioning the library, is given with Observations Made When Shifting ALib to C++20 Modules.

5.4.2 Selecting The Included ALib Modules

CMake list variable ALIB_BUILD is an input/output list which defines the particular ALib Modules that should be included in the build. The script will process the values given and will extend the list to include all necessary modules that the given selection depends on.

The values correspond to the module names in upper case letters, hence ALOX, BOXING, CHARACTERS, etc... (Lower-case or mixed case names are allowed as input, but will be internally converted to upper case.)

If the variable is not set or contains special name "ALL", all modules are chosen to be built and included.

5.4.3 Variables For Directory and Source Code Definitions

The following CMake variables are available after the invocation of the script:

  • ALIB_BASE_DIR
    Defines the directory where the ALib project is stored.
    This variable may also be specified before entering the script to specify a different directory than the default, which is inferred from the full path name of the script invoked.
  • ALIB_SOURCE_DIR
    Defines the directory where the ALib source files are stored. The header-files are located in the same directories.
    This variable may also be specified before entering the script to specify a different directory than the default, which is
          ${ALIB_BASE_DIR}/src
    

Besides the directory, the following CMake list variables are defined. Each list contains all names of source files of the corresponding type, which have to be present according to the ALib Build defined in the previous step.
The file names come with absolute path names.

CMake List File type description
ALIB_MPP C++20 Module files (respectively aggregation headers with legacy builds).
ALIB_INL C++ header files which are included by the .mpp-files.
ALIB_CPP Compilation units.
ALIB_HPP Legacy Header, containing mostly necessary preprocessor definitions.
ALIB_H Module access headers. The only header files to be included by using code. Usable in both modes, C++20 and legacy. See chapter 2.4 Including/Importing ALib Modules).

5.4.4 Cached CMake Variables

The script will create a set of cached boolean CMake variables (which are variables that can be edited with CMake GUI tools and various C++ IDEs).

The following variables correspond directly to preprocessor symbols used for code selection, and thus this list links to their corresponding documentation:

In addition, the following further cached variables are set:

  • ALIB_CLANG_USE_LIBCPP
    If the to true and the Clang compiler is used, then the LLVM libc++ library is used instead of the default libstdc++. The variable is evaluated in CMake function ALibSetCompilerAndLinker (see section below). Technically, this variable adds the option -stdlib=libc++ to the compiler command line as well as c++ to the list of link-libraries of the linker command line.
    Note
    In case precompiled 3rd-party libraries are used which are not compiled against the LLVM libc++ library, the build will fail. Hence the variable is set to false by default and should only be used if all libraries used by a project are known to be compiled against the LLVM libc++ library.

.

  • ALIB_VERSION
    Defines the ALib library version. This variable cannot be changed, respectively will be overwritten on CMake generation. It is specified as a cached CMake variable just for the reason of presenting the ALib version to tools that allow to modify CMake settings.

    Furthermore, non-cached version variables ALIB_VERSION_NO and ALIB_VERSION_REV are set which hold the version number and revision number as separate integral values.

  • ALIB_CMAKE_VERBOSE
    If set to true, extended information will be printed with running the CMake script. See 5.5.1 Other Build Systems for details.

  • ALIB_CMAKE_SKIP_THREAD_LIB_SEARCH
    If set to true, the provided script will not search for a thread library on the target platform. In debug-compilations of the library, if the compiler-symbol ALIB_SINGLE_THREADED is passed, still a multi-threading library is of use. This is because with such a build, the library assures single-threaded use: An ALib assertion is raised when multi-threading is detected! Therefore, the target system's thread library is still searched and linked, unless this symbol is set.
    If found, symbol ALIB_EXT_LIB_THREADS_AVAILABLE is passed to the C++ compiler.

    The way to prevent searching and adding a thread library is by setting special CMake cached variable ALIB_CMAKE_SKIP_THREAD_LIB_SEARCH to true.

  • ALIB_DEBUG_GLIB
    If true, symbols _GLIBCXX_DEBUG, _GLIBCXX_DEBUG_PEDANTIC and _GLIBCPP_CONCEPT_CHECKS are passed to the compiler.
  • ALIB_COVERAGE_COMPILE
    If true, option –coverage is added to CMake variables ALIB_COMPILER_OPTIONS and ALIB_LINKER_OPTIONS

5.4.5 CMake Build-Setting Variables

The script will create the following non-cached CMake variables, which can be used to define build-settings of custom projects:

  • ALIB_SYMBOLS
    Will contain the ALib preprocessor symbols, as defined by the cached variables. This variable can for example be used as a parameter to CMake function target_compile_definitions().
  • ALIB_COMPILER_WARNINGS
    Will contain compiler parameters to set the (high!) warning level used with compiling ALib. This variable can, for example, be used as a parameter to CMake function target_compile_options().
    Custom entries may be added to the list before invoking ALib.cmake. If the entry "ALIB_SUPPRESS_COMPILER_WARNINGS" is found, that entry is removed and no compiler-specific warning settings are added to this symbol.
  • ALIB_COMPILER_OPTIONS
    Will contain parameters to be passed to the compiler when linking ALib. This variable can, for example, be used as a parameter to CMake function target_compile_options().
  • ALIB_LINKER_OPTIONS
    Will contain parameters to be passed to the linker when linking ALib. This variable can, for example, be used as a parameter to CMake function set_target_properties().
  • ALIB_EXTERNAL_LIBS
    Will contain a list of external libraries needed to build ALib. This variable can, for example, be used as a parameter to CMake function target_link_libraries().

In addition, the following non-cached variable is an input variable which may be set before invoking the script:

  • ALIB_COMPILER_FEATURES
    May contain CMake feature parameters to be passed to the compiler. This variable can, for example, be used as a parameter to CMake function
    • target_compile_features().

5.4.6 CMake Functions Defined By The Script

The script will define the following CMake functions:

  • ALibAddStaticLibrary
    This parameterless function creates a static library target called ALib_StaticLib.
  • ALibAddSharedLibrary
    This parameterless function creates a shared library (under Windows OS a DLL) target called ALib_SharedLib.
  • ALibSetCompilerAndLinker( target )
    This function applies the settings of all variables listed in the previous section to the given target. In addition, the target's CMake property POSITION_INDEPENDENT_CODE is switched on. Furthermore, CMake variable ALIB_CLANG_USE_LIBCPP is evaluated and, if set to true and Clang compiler is used, the option -stdlib=libc++ is added to the compiler command line as well as c++ to the list of link-libraries of the linker command line.

5.4.6 Library Filename

The library's filename is determined by (non-cached) CMake variable ALIB_LIBRARY_FILENAME. If this variable is not set before invoking script ALib.cmake, then the name is determined automatically from the selected modules.

The name is used with functions ALibAddStaticLibrary and ALibAddSharedLibrary, introduced in the previous section.

If not provided, the name will be assembled according to the following set of rules:

  • The name starts with "alib_".
  • The library version is appended, for example "2510R0".
  • In the case of debug-builds, the term "_DBG" is appended.
  • In the case that variable ALIB_SINGLE_THREADED is set, the term "_ST" is appended.
  • In the case that all modules are selected in the ALib Build, no information about modules is appended. Otherwise, each selected module's name is added in capital letters, separated by an underscore character ('_') unless a module is not "superseded" by another module that has a mandatory dependency to it.
  • If one of the variables

    • ALIB_DEBUG_MEMORY,
    • ALIB_DEBUG_CONTAINERS,
    • ALIB_DEBUG_STRINGS, or
    • ALIB_DEBUG_RESOURCES

    is given, then the prefix "DBG"" is added to the module name appended. - In the case that \ref alib_mod_alox "ALox" is included, the suffixes _ALOX is extended by suffix NRL if release logging is switched off. In addition, suffix NDL is added with debug-builds that do not include debug logging. Note that both exceptions cover the non-default, unusual compilation cases in respect to the availability of debug- and release logging.

5.5 Further Information

5.5.1 Other Build Systems

As already explained, the CMake build process is viewed to be the reference process for ALib compilation.

For noneCMake users, the selection of the exact minimum set of source and header-files, is probably the most difficult (and annoying) task. In CMake we have separated the source selection into script:

        ALIB_BASE_DIR/build/cmake/ALibSources.cmake

which is invoked by the main script.

This script might be analysed to identify the source and header-file dependencies of the different ALib Modules.

Likewise, script

        ALIB_BASE_DIR/build/cmake/ALibModules.cmake

might be analyzed to get actual (and correct) information about module dependencies.

Furthermore, by setting CMake cache variable ALIB_CMAKE_VERBOSE to true, running CMake will write extended information that might be used and copied into the configuration files of other build systems or into IDE project files.

Among the data displayed is:

  • List of modules included in the build. (Modules that the user explicitly selected plus recursive dependencies)
  • List of modules excluded from the build.
  • List of source files that are needed for the build.
  • The resulting library filename.
  • The base folder of source files.
  • The compiler-symbols (definitions) passed.
  • The compiler warning flags.
  • The compiler features and other flags.
  • The linker flags
  • External libraries used.

5.5.2 Choosing C++ Language Version

As demonstrated in the chapter 5.3 A Step-By-Step CMake Sample (Step 3), CMake variable ALIB_COMPILER_FEATURES may be used to determine the C++ language standard for ALib targets and optionally for custom targets (i.e. if function ALibSetCompilerAndLinker is invoked for a custom target).

ALib requires C++ language level 20 as a minimum and is compatible with level 23.

CMake provides other (maybe even more preferable) mechanics to determine/set the C++ language level, which of course may be used alternatively.

5.5.3 Copying The Sources

If CMake variable ALIB_SOURCE_COPY_TARGET_DIR is set before invoking the CMake script ALib.cmake, then the source files are copied to the directory specified in the variable. If the variable does not point to a valid directory, an error is raised and the CMake script is stopped.

The files copied represent exactly the set of files which are needed for compiling the selected ALib Build (combination of ALib Modules), which is optionally specified with the variable ALIB_BUILD (see 5.4.2 Selecting The Included ALib Modules).

This feature therefore can be used to create a fresh, filtered copy of the ALib source tree tailored to an application. Nevertheless, it is usually not recommended to do so, because source files that are not used by a combination of modules are neither included as header-files, nor compiled. The feature is rather used by the ALib developers to verify module and source code dependencies.

5.5.4 External Library Dependencies

ALib has no mandatory dependencies to external (3rd-party) libraries. The following optional dependencies exist:

  1. Boost RegEx
    If enabled by the compiler-symbol ALIB_FEAT_BOOST_REGEX, a dependency to boost library component regex is made. As a result, the following ALib features become available:
  2. Standard C++ library
    Some support to make types of the C++ standard library compatible with various concepts of ALib are activated by including "compatibility header-files". Those are named "ALib.Compatibility.Std<NAME>" For more information, see the following namespace documentation:
  3. QT Library
    If ALib is used in combination with the QT Class Library , compatibility support for character array types (strings and character vectors) of the QT Library becomes available with modules ALib Boxing and ALib Strings.
    Such support is activated simply by including "compatibility header-files" named "ALib.Compatibility.QT<NAME>"

    For more information, see the following namespace documentations:

6. Using The Library

There is little to say here, so we note just some bullet points:

  • A shortest C++ program is given at the start of the Programmer's Manual of module ALib Bootstrap. From there, a user of the library should start directly with the topic and module of interest. Often, the dedicated Programmer's Manuals linked in this table provide some sample code to start with.
  • To access an ALib Module, one or a few header files have to be included. The complete list of available header files to include (respectively import) a module is given at the end of chapter 2.4 Including/Importing ALib Modules.
    For example, if a user wants to use ALib for scanning a directory and working with file trees, the single include of the header file ALib.Files.H is sufficient.
  • When compiling code against ALib, each compilation unit needs to receive the same compiler symbols as were given to the library itself. With debug-compilations, this will be checked with the invocation of function Bootstrap.
    If different compilation symbols are used, then, in debug-compilations, an assertion will be raised with bootstrapping. The mechanics behind this are quickly explained in chapter 9. Assuring Compilation Compatibility of the Programmer's Manual of the module ALib Bootstrap.
  • When using ALib with Microsoft Windows OS, it may be important to include windows.h before any ALib header-file. The reason is that ALib includes windows.h unless it was included already. When the ALib library includes windows.h, it uses some defines like WIN32_LEAN_AND_MEAN or NOGDI to minimize the impact of that huge header-file. If windows.h is included before including ALox (AWorx library), it is up to the embedding application which level of windows functionality is needed.

Appendices

A.1 Naming Conventions

The C++ language standard does not suggest naming rules. This is different to, for example, with the JAVA language. While some people could say, that the C++ standard library naming scheme suggests such rules, others say that it is especially valuable, that code outside namespace std differs from that - to be better distinguishable within source code.

ALib uses the following rules:

  • Preprocessor symbols are "UPPER_SNAKE_CASE" and mostly are prefixed "ALIB_".
  • Global variables are also UPPER_SNAKE_CASE.
  • Otherwise, underscores (snake_case) are almost never used.
  • Namespaces are in lower case letters consisting of single words (no camel casing).
  • All other entity names are either lowerCamelCase or UpperCamelCase.
  • Public namespace functions are UpperCamelCase.
  • Hidden (e.g., anonymous) namespace functions are lowerCamelCase.
  • Types on namespace scope are UpperCamelCase.
  • Members and inner types are UpperCamelCase if public, otherwise lowerCamelCase.
  • Types and members that are available only in debug-compilations, are prefixed "Dbg".
  • Method and function parameters are lowerCamelCase.
  • Templated types UpperCamelCase with a leading 'T', for example TString. Those template types which are not aliased (like TString is aliased to String, WString, NString etc.), often are missing the leading 'T'. This is for better readability of the end-user code and also to just indicate that the direct use of the templated class is the common use. This is why, for example, class SharedVal carries no leading 'T',
    while class TSharedMonoVal does.
  • Template parameters types are also UpperCamelCase with a leading 'T'. Only in seldom cases, a template parameter is just named "T". Usually the name should indicate what kind of templated type is expected or what is to be done with it.
  • Type traits that might specialized by users of this library are UpperCamelCase with the postfix of "Traits". For example, ArrayTraits or BoxTraits.
  • C++ Concepts usually are prefixed "Is", for example, with IsImplicitArraySource, IsAppendable or IsCustomized. In fewer cases, the prefix "Has" is used, for example, with HasResources or HasRecords.
  • Methods and field-members that are available only with debug-builds are prefixed "Dbg". Often, these members remain available in release-builds but are then empty and probably static and constexpr. This way they are optimized out by the compiler, while their use does not have to be pruned using the preprocessor (i.e., macro ALIB_DBG() or symbol ALIB_DEBUG).

As a final remark, when browsing the code it could be noticed that a lot of block-formatting is used. While a line width of 100 characters is usually not exceeded, for the sake of building code blocks, sometimes this line width barrier is ignored. From our perspective, the usefulness of tabbed code blocks is very much underestimated. Besides readability, a lot of annoying typos can be detected easily, before a next build run.
In addition, tabbed code blocks often largely improve the ability to quickly refactor a codebase manually. It should be allowed to mention, that the editor of C++ IDE CLion here has some unrivalled features: Instead of only supporting block selection and copy/paste of blocks, this editor creates a set of completely new "carets" at the moment block selection starts. We want to thank the team of JetBrains for supporting ALib development by providing a free Open Source License of their absolutely superb set of IDEs.

A.2 How To Use This Library

Most of the time, a 3rd party library like ALib is used to help solving a certain task. Such tasks are addressed with higher level modules, like ALox, ALib CLI, or ALib Expressions. In this case, the corresponding module documentation should be read directly, without loosing time on learning too much about the lower level modules and types.

If you find the lower level types useful for your own projects, those will be understood over time and by reading the reference documentation of types found in their corresponding namespaces.

Should you really just be interested in general and therefore absolutely not knowing where to start reading, we recommend to start with some of the module's programming manuals, for example ALib Boxing or ALib Strings. As debug log-output is something most projects needs, another recommendation might be the to check out the tutorial of module ALox.

The manuals mentioned above may provide you with a good grasp of the design principles and features of ALib in general and thus with the value that ALib might bring to your own software projects.

Warning
Experienced C++ programmers might complain that the Programmer's Manuals are to lengthy. Together they comprise almost 500 pages!
We could now say that the reason for often being quite "verbose" is that the manual address less experienced programmers. But this is not the truth. Explaining the details just helps us to understand a module's goals better and leads to a better code design. It happens that manual sections are written before the corresponding library feature is even implemented. Or, that while writing a manual chapter, the already written code gets heavily refactored.
While we excuse for verbosity, we think that often, the reference documentation of types and entities found in the library provide a good way to avoid reading the libraries manuals! :-)

A.3 Type Aliases in Namespace ::alib

A.3.1 Why Type Aliases?

Once a C++ library becomes more complex, their types get split into different namespaces and inner namespaces. This is also true for ALib, which is split into various modules. Consequently, the common need to conveniently address all types is to add several using statements to a compilation unit. With ALib this could, for example, look like this:

 using namespace alib::threads;
 using namespace alib::strings;
 using namespace alib::expressions;

To avoid the need of the right "permutation" of using statements at the top of a users' compilation unit, ALib exposes all important types into this outer namespace with type definition statements (C++ keyword using). For example, at the end of header-file expressions/compiler.inl, just before closing outer namespace alib, the following statement is made:

/// Type alias in namespace \b alib.
using Compiler= expressions::Compiler;
} // namespace [alib]

With that, a single:

 using namespace alib;

fits for all ALib types and in most cases, this single using statement is all that is needed. This approach does not generate conflicts: If, for example, the using code would have an own class named "Compiler", it can fall back to the standard schematic, by adding just:

 using namespace alib::threads;
 using namespace alib::strings;

and accessing class Compiler explicitly as alib::expressions::Compiler.

Note
The reason why all this is possible is due to (a next great) design decision of C++. While the CODE in the following snippet: namespace A { namespace B { CODE } }
"sees" all types in namespace A, with this snippet: using namespace A::B; CODE
the CODE does not "see" types of namespace A, but only those of B.

A.3.2 Aliases of Template Types

Most of the type aliases are as simple as sampled with class Compiler above. Also templated types are often just a 1:1 alias, for example:

/// Type alias in namespace \b alib.
template<typename T, typename TAllocator= lang::HeapAllocator, typename TLock= void>

But sometimes template parameters become predefined with the alias. Often (but not always) with that the name changes, i.e., the leading T is removed. As an example, take:

/// Type alias in namespace \b alib.
using SharedFTree= files::TSharedFTree<SharedLock>;

In some few situations, mostly due to technical reasons, template parameters are even changing their position. Of-course, some special care has to be taken if such types are used aliased or not. As an example, take:

/// Type alias in namespace \b alib.
template<int TEnd, int TBegin= 0, typename TInterface= int>

Finally, often several different aliases are defined in namespace alib, providing different permutations of template types. For example, type alib::strings::TAString is aliased as:

A.4 Type-Traits, Concepts and Adapters

A.4.1 Terminology

Understanding the interplay between "concepts", "type-traits", and "adapters" is essential for mastering modern C++ programming, especially when working with libraries like ALib. Here's a concise overview of these elements and their interactions:

Type-Traits:
Introduced in C++11, type-traits are templates that provide compile-time information about types. They enable metaprogramming by allowing developers to query and manipulate type properties. For instance, std::is_integral<T>::value checks if T is an integral type.
Concepts:
Added in C++20, concepts define a set of requirements (such as functions, types, or constants) that a type must satisfy. They act as compile-time predicates, ensuring that template parameters meet specific criteria, leading to clearer and more robust code. For example, a concept can require that a type offers certain member functions or provides a certain type alias.
Adapters:
In C++, adapters are software design-patterns that allow incompatible interfaces to work together. They "adapt" one interface to another, facilitating code reuse and flexibility. For instance, stream adapters can modify data streams to fit different input/output interfaces.

A.4.2 Use in ALib

Most of the concepts introduced by ALib are formulated as expressions that depend on the specialization of certain type trait structures. Then, the methods that use these concepts as requirements often leverage other mandatory members of the same type-trait structures for their implementation. The following scheme illustrates this relationship:

This design ensures:

  • Flexibility:
    Users can extend or modify type behaviors by specializing type-traits without altering the core library code.
  • Type Safety:
    Concepts enforce that only types meeting specific criteria are used in particular contexts, preventing misuse.
  • Least Intrusive:
    By specializing ALib type-traits, users define how their types interact with ALib's templates and functions. Such specialization can be performed in code units which are separated from the user's type definition. Concepts then validate these types at compile-time, ensuring they conform to the expected interfaces. This layered approach allows seamless interaction between user-defined types and ALib's functionalities without introducing unnecessary dependencies on ALib in the using code.
  • Easy Adaptability:
    By adhering to this design pattern across ALib, users are quickly directed towards what they need to do to adapt its custom types - without reading through our (lengthy) Programmer's Manuals.

The latter claim about "easiness" should be quickly sampled:

  • A user has an instance of type "MyType".
      MyType myInstance;
    
  • A user wants to append a string representation of that type to an ALib AString and writes:
      AString as;
      as << myInstance;  
    
  • This creates a compiler error-message. Among the different hints of the compiler, the user will see something like this:
      note: because 'alib::strings::IsAppendable<MyType, char, alib::lang::HeapAllocator>'
    
  • If the user clicks this line, the IDE will show the adapter method that is restricted by the concept IsAppendable.
  • From here, the user can navigate to the definition of this concept. There he will quickly see that this concept's constraint string exclusively relies on the type trait AppendableTraits.
  • The next click in the IDE will lead to the declaration of the type trait. Its reference documentation explains on how to specialize it for MyType - and that is all the user has to do.

A.4.3 Further Reading

For a deeper understanding, consider exploring the following resources:

These resources provide foundational knowledge that complements the design and usage patterns employed in ALib.

A.5 Collecting Caller Information

A core class included in any ALib Build is lang::CallerInfo. It stores the following information:

  • Filename, line number, and function name received with corresponding built-in preprocessor symbols __FILE__, __LINE__ and a compiler-dependent symbol which is fetched with ALIB_CALLER_FUNC.
  • The C++ type id, fetched with keyword typeid, usually passing argument *this.
  • The C++ thread id of type std::thread::id. This is only included if the compiler-symbol ALIB_EXT_LIB_THREADS_AVAILABLE evaluates to true.

It is used to collect information about the caller of a function or method member. ALib collects such information mainly on two occasions:

  • For collecting information in debug-builds of the library, and
  • with module ALox to make any log output 'clickable' in a developer's IDE and to provide log "scopes" which allow enabling or disabling log statements depending on their location.

To ease the type's use, several preprocessor macros are defined. To understand their difference, it is important to be aware that, depending on the use-case, caller information is either exclusively collected in debug-builds or collected in any build-type. In the second case, it still should be the user of the library to decide whether source-code information in a release executable or not.

With this background thought in mind, the reference documentation of the following macro list should be enough to fully understand how the data flow is managed and how to efficiently use the macros.

The macros directly relate to class CallerInfo:

The following macros make use of the caller-macros:

Attention
The macros fail to work and produce compiler errors if used within static methods or namespace functions.

This is because they use the keyword this to identify a caller's type. Unfortunately, the C++ language in combination with the preprocessor does not allow the automatic detection whether it is legal to use keyword this or not . For this reason the core macro ALIB_CALLER has to be redefined before a code unit defines functions and restored back afterward. To do this, header-files ALib.Lang.CIFunctions.H and ALib.Lang.CIMethods.H are provided. The following example demonstrates how they are to be used:

// by default, macros are activated that work within non-static methods.
// therefore this compiles
alib::Lock MyLock;
struct MyType
{
void doSomething()
{
//...do something
Log_Info("Also here, the macro ALIB_CALLER is indirectly used")
}
// but we must not define this method here
static void doSomethingStatic();
};
// now we switch to the function version:
// this way, the static method can use all derived macros:
void MyType::doSomethingStatic()
{
//...do something
Log_Info("Also here, the caller is used")
}
// Furthermore, non-members can be implemented now:
void ANamespaceFunc()
{
//...do something
Log_Info("Also here, the caller is used")
}
// Now we switch to the method version.
// This should always be done to avoid conflicts, i.e. when the order
// of header-file inclusion changes, etc.

A.6 T.HPP-Files And Custom Template Instantiations

A.6.1 The Challenge and ALib's Solution

In C++, templates offer a powerful mechanism for writing generic and reusable code. But using templates extensively in header-files leads to longer compilation times and potential code bloat, particularly when templates are instantiated multiple times across different translation units. The problem becomes significant in larger projects where a template may be used with many types or included in multiple source files, forcing the compiler to repeatedly generate code for the same template.

To mitigate these issues, with a few template types, ALib separates

  1. template declarations,
  2. a subset of that template's method definitions, and
  3. instantiations for combinations of template types,

into three different files. These files are:

  1. The internal INL-file (.inl) used for the template class declarations exported with the C++ module.
    This header is missing the definition of those methods that should not be compiled with a unit that does not instantiate a templated type with a specific combination of template parameters.
    In other words, the contents of this file is exposed by importing the module and is not relevant for a user.
  2. A T.INL-file (.t.inl) providing the missing (and and not inlined) method definitions.
  3. A CPP-file (.cpp) that includes both, the declaration and definition files, and explicitly instantiates the template for specific, commonly used types.

This approach results in improved compilation speed, code modularity, and manageable build times while still allowing users the flexibility of creating custom instantiations when necessary.

A.6.2 Instantiations of Custom Types

With this recipe in place, it is fairly straight forward to instantiate your custom version of an ALib-type of that sort. Let's quickly look at a sample.

For class alib::monomem::TMonoAllocator, provided with module ALib Monomem, only one built-in template instantiation is provided. This specifies the single template parameter TAllocator to be of type HeapAllocator. This instantiation is then aliased as alib::MonoAllocator.
The corresponding files are:

Let us look at the compilation unit (the CPP-file):

// #################################################################################################
// Instantiation of MonoAllocator, which is TMonoAllocator<lang::HeapAllocator>.
// #################################################################################################
#include "alib/monomem/monoallocator.t.inl"

That is basically all that the CPP file needs to contain. Of-course, with optional C++20 Module compilation of ALib, the file got a little more complex, so please refer to the sources for complete insights.

With this in mind, we can now instantiate a custom mono-allocator, one that uses a PoolAllocator as its memory source. This is done in the ALib unit-tests. Here is the excerpt:

// ================================================================================================#
// AWorx ALib Unit Tests
//
// Copyright 2013-2025 A-Worx GmbH, Germany
// Published under 'Boost Software License' (a free software license, see LICENSE.txt)
// ================================================================================================#
#if !defined(ALIB_C20_MODULES) || ((ALIB_C20_MODULES != 0) && (ALIB_C20_MODULES != 1))
# error "Symbol ALIB_C20_MODULES has to be given to the compiler as either 0 or 1"
#endif
#if ALIB_C20_MODULES
module;
#endif
// ====================================== Global Fragment ======================================
// =========================================== Module ==========================================
#if ALIB_C20_MODULES
module ALib.Monomem;
#else
# include "ALib.Monomem.H"
#endif
// =================================================================================================
// Instantiation of TMonoAllocator for PoolAllocatorHA and its inner type
// detail::MAFields<PoolAllocatorHA>.
// =================================================================================================
// Under windows, we cannot do the following within the unit tests project, which imports the
// ALib as a DLL. With Windows OS, it has to be done in an external lib.
#if !defined(_WIN32)
#endif // !defined(_WIN32)

Note that the compilation unit - although it is your own file and is located outside ALib - still needs to specify module ALib.Monomem;. Because any compilation unit must only specify one module, you have to place this code into a dedicated compilation unit specifically created for this purpose.

Next, create your custom header-file or place it anywhere in your existing headers. (In the case of C++20 Modules, do not use the export keyword.) There, copy the instantiation code and place keyword extern in front, to turn it into a declaration:

namespace alib::monomem {
}

This announces that the linker will find the instantiation.

With this in place, we can start using the instantiation:

// Create a pool allocator that uses heap allocation
{
// Create a mono allocator that receives its memory from the pool.
// Its growth factor is 100%, which keeps each next allocation at
// the initial size, which is a good strategy with an underlying pool!
monomem::TMonoAllocator<PoolAllocatorHA> myMono(ALIB_DBG("MyPaMa",) myPool, 1, 100);
// Allocate and construct an object.
MyType* myObject= myMono().New<MyType>(A_CHAR("Hello"));
///...
///...
// Destruct the object
myMono().Delete(myObject);
}// destruction of the mono allocator, which passes the memory buffer back to the pool

To summarize, the fast track approach when a specific instantiation of an ALib type that splits some method definitions from the type declaration is needed, is to have a look at the compilation unit (the CPP-file), copy its code to an own compilation unit, and replace the given type with your own type.

Attention
With WindowsOS and DLL-compilation, things may become more complicated. Here, the compiler-symbol ALIB_DLL has to be set rightfully for the code-unit that implements an instantiation. This manual cannot go into further details of this topic. Instead, general information on compiling and linking under WindowsOS has to be consulted.

A.7 Pretty Printers

For gdb (GNU Debugger), some "pretty printers" are available. Please consult page 1. Pretty Printers for GNU Debugger (gdb).