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.
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:
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.
Within this manual, within the distinct manuals of the ALib Modules and within the reference documentation, the term "ALib Build" is frequently used.
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.
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:
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.
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. |
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:
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"
error: declaration of 'CalendarDateTime' must be imported from module 'ALib.Strings.Calendar' before it is required
".H"
and use this as your inclusion header to add: #include "ALib.Strings.Calendar.H"
The following table lists all header files available.
Besides the headers which either import or include portions of an ALib Module, a few further public headers are present.
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. 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".
To close this section, we quickly want to explain the term ALib Camp.
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.
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.
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.
The current release was compiled and tested under the following platforms and toolchain combinations:
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.
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.
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.
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.
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:
When this manual section talks about "building the ALib library", one of the three options is meant.
An extensive set of unit tests is included in the distribution, along with a corresponding CMake script to build those.
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:
ALIB_BASE_DIR/build/cmake/unittests
cmake ..
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 ..
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:
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.
Step 3: Set Other Feature Variables
Our project should be compiled using C++20. This is set with:
Step 4: Include "ALib.cmake"
Now we are ready to include the main ALib CMake script:
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:
Step 6: Define The Custom Project
Now we are good to define our custom project in a usual way:
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
:
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:
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!
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:
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.
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.
The following CMake variables are available after the invocation of the script:
ALIB_BASE_DIR
ALIB_SOURCE_DIR
${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). |
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_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.
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
.
true
, symbols _GLIBCXX_DEBUG, _GLIBCXX_DEBUG_PEDANTIC and _GLIBCPP_CONCEPT_CHECKS are passed to the compiler.true
, option –coverage is added to CMake variables ALIB_COMPILER_OPTIONS and ALIB_LINKER_OPTIONS The script will create the following non-cached CMake variables, which can be used to define build-settings of custom projects:
target_compile_definitions()
. target_compile_options()
.target_compile_options()
. set_target_properties()
. target_link_libraries()
.In addition, the following non-cached variable is an input variable which may be set before invoking the script:
target_compile_features()
.The script will define the following CMake functions:
ALib_StaticLib
. ALib_SharedLib
. 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. 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:
"alib_"
."2510R0"
."_DBG"
is appended."_ST"
is appended.'_'
) unless a module is not "superseded" by another module that has a mandatory dependency to it.If one of the variables
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.
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:
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.
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.
ALib has no mandatory dependencies to external (3rd-party) libraries. The following optional dependencies exist:
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:
There is little to say here, so we note just some bullet points:
import
) a module is given at the end of chapter 2.4 Including/Importing ALib Modules. 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:
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.
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.
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:
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
.
A
, with this snippet: using namespace A::B; CODE A
, but only those of B
.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:
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:
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:
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:
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:
std::is_integral<T>::value
checks if T is an integral type.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:
The latter claim about "easiness" should be quickly sampled:
MyType myInstance;
AString as; as << myInstance;
note: because 'alib::strings::IsAppendable<MyType, char, alib::lang::HeapAllocator>'
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 core class included in any ALib Build is lang::CallerInfo. It stores the following information:
typeid
, usually passing argument *this
.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:
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:
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:
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
into three different files. These files are:
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.
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):
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:
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:
This announces that the linker will find the instantiation.
With this in place, we can start using the instantiation:
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.
For gdb (GNU Debugger), some "pretty printers" are available. Please consult page 1. Pretty Printers for GNU Debugger (gdb).