etl/docs/generators.md
Roland Reichwein 866c8a315e
Extensions for testing (#1380)
* Extensions for testing

Generalize run-tests.sh

Test all C++ versions at once

Fix combination of big endian and -Wsign-conversion

Failed on s390x (as reference for big endian)

Add github workflow for s390x

Add armhf container files

Devcontainers for i386 and riscv

Add github workflows for armhf, i386 and riscv64

Add run-tests.sh for foreign architectures

Document testing in doc/testing.md

Adjustments from clang-format run

Fix .devcontainer/s390x/Dockerfile for linebreak syntax

Fix exit code of run-test.sh

Previously, "exit $?" was used, actually the return value of
FailedCompilation and FailedTest which are always 0.

Now just using 1.

In run-tests.sh at ctest, use -V for printing number of tests unconditionally

While ctest suppresses individual test list by default, it didn't even
print the number of tests anymore, as run_tests.sh does because
it suppresses it output completely.

Now, by default print number of tests, and in verbose mode, print test list
in addition.

* Support powerpc as foreign architecture

* Add SFINAE constraints to etl::begin/end and reverse iterator free functions

The unconstrained etl::begin(), etl::end(), etl::cbegin(), etl::cend(),
etl::rbegin(), etl::rend(), etl::crbegin(), and etl::crend() templates
in the no-STL code path were matching iterator types during ADL, causing
a hard error with GCC 15's std::ranges::begin. When std::ranges performed
ADL on an etl::*::iterator, it found etl::begin() as a candidate; since
the iterator type has a nested iterator typedef, the return type TContainer::iterator
was valid, but calling .begin() on the iterator failed.

Fix: add etl::void_t<decltype(...)> SFINAE guards to each template,
ensuring they only participate in overload resolution when TContainer
actually has the corresponding member function (.begin(), .end(), etc.).

* - Fix red unit tests on 32 bits big-endian platform.

* Document powerpc architecture for testing

* Use Dockerfiles in cross testing github workflows

Synchronizes environment setup for github workflows to what is
defined in the development Dockerfiles. So they don't need to
be maintained separately.

---------

Co-authored-by: John Wellbelove <john.wellbelove@etlcpp.com>
Co-authored-by: Sergei Shirokov <sergej.shirokov@gmail.com>
Co-authored-by: John Wellbelove <jwellbelove@users.noreply.github.com>
2026-04-15 10:47:52 +02:00

191 lines
5.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Code Generation for Pre-C++11 Support
ETL supports C++03 (also referred to as C++98) environments where variadic
templates, `constexpr`, and other modern features are unavailable. To
provide equivalent functionality, certain headers are **generated** using
[Cog](https://nedbatchelder.com/code/cog/), a Python-based code generation
tool that embeds Python snippets inside source files.
This document explains how the code generation system works and how to
regenerate the headers if you modify a generator template.
---
## Overview
| Directory | Contents |
|---|---|
| `include/etl/generators/` | Generator templates (`*_generator.h`) and batch scripts |
| `include/etl/private/` | Generated output (`*_cpp03.h`) committed to the repository |
| `scripts/generator_test.py` | CI script that verifies generators match committed files |
The generator templates contain embedded Python code (delimited by `[[[cog`
and `]]]`) that produces the repetitive C++03 boilerplate. Cog processes
these templates and writes the expanded output to `include/etl/private/`.
---
## Generated Headers
The following C++03 compatibility headers are generated:
| Generator | Output | Purpose |
|---|---|---|
| `fsm_fwd_decl_cpp03_generator.h` | `fsm_fwd_decl_cpp03.h` | FSM forward declarations |
| `fsm_friend_decl_cpp03_generator.h` | `fsm_friend_decl_cpp03.h` | FSM friend declarations |
| `fsm_cpp03_generator.h` | `fsm_cpp03.h` | Finite state machine implementation |
| `message_router_cpp03_generator.h` | `message_router_cpp03.h` | Message router |
| `message_packet_cpp03_generator.h` | `message_packet_cpp03.h` | Message packet |
| `largest_type_cpp03_generator.h` | `largest_type_cpp03.h` | Largest type metafunction |
| `largest_alignment_cpp03_generator.h` | `largest_alignment_cpp03.h` | Largest alignment metafunction |
| `largest_cpp03_generator.h` | `largest_cpp03.h` | Largest type/size utilities |
| `smallest_cpp03_generator.h` | `smallest_cpp03.h` | Smallest type/size utilities |
| `type_traits_cpp03_generator.h` | `type_traits_cpp03.h` | Type traits (`is_one_of`, etc.) |
| `type_lookup_cpp03_generator.h` | `type_lookup_cpp03.h` | Type lookup metafunction |
| `type_select_cpp03_generator.h` | `type_select_cpp03.h` | Type selection metafunction |
| `variant_pool_cpp03_generator.h` | `variant_pool_cpp03.h` | Variant pool |
---
## Generator Parameters
Cog variables control how many template parameter overloads are generated:
| Variable | Default | Used by |
|---|---|---|
| `Handlers` | 16 | FSM and message router generators |
| `NTypes` | 16 | Type utility generators (largest, smallest, lookup, select, variant pool) |
| `IsOneOf` | 16 | Type traits generator (`is_one_of`) |
These defaults produce overloads supporting up to 16 types or handlers,
which is sufficient for most embedded applications while keeping compile
times reasonable.
---
## Prerequisites
* **Python 3**
* **cogapp** install via:
```bash
pip install cogapp
```
---
## Regenerating Headers
### Using the batch scripts (Windows)
Each generator has a corresponding `.bat` file in `include/etl/generators/`:
```bat
cd include/etl/generators
generate.bat # Regenerate all headers
generate_fsm.bat # Regenerate FSM headers only
generate_smallest.bat # Regenerate smallest_cpp03.h only
# etc.
```
### Manual invocation
Run Cog directly from the `include/etl/generators/` directory:
```bash
cd include/etl/generators
# Example: regenerate smallest_cpp03.h
python3 -m cogapp -d -e -o../private/smallest_cpp03.h -DNTypes=16 smallest_cpp03_generator.h
# Example: regenerate fsm_cpp03.h
python3 -m cogapp -d -e -o../private/fsm_cpp03.h -DHandlers=16 fsm_cpp03_generator.h
```
Cog options used:
| Option | Meaning |
|---|---|
| `-d` | Delete the generator markers from output |
| `-e` | Warn if the input file has no generator markers |
| `-o<file>` | Write output to the specified file |
| `-D<var>=<value>` | Define a Cog variable |
### Regenerating all headers
The `generate.bat` script regenerates every header:
```bash
cd include/etl/generators
./generate.bat # Windows
# or run the commands manually on Linux/macOS
```
On Linux/macOS you can run the commands from `generate.bat` directly in
your shell (they are standard `python3 -m cogapp` invocations).
---
## Verifying Generators
After modifying a generator template, verify the output matches the
committed file:
```bash
python3 scripts/generator_test.py
```
This script:
1. Runs Cog on every `*_generator.h` file.
2. Compares each output against the corresponding file in
`include/etl/private/`.
3. Reports success or failure.
The `generator.yml` GitHub Actions workflow runs this automatically on
every push and pull request.
---
## How Generators Work
A generator template contains standard C++ code interspersed with Cog
directives. For example, from `smallest_cpp03_generator.h`:
```cpp
/*[[[cog
import cog
cog.outl("template <typename T1, ")
for n in range(2, int(NTypes)):
cog.out("typename T%s = void, " % n)
cog.outl("typename T%s = void>" % int(NTypes))
]]]*/
// Generated code appears here after running Cog
/*[[[end]]]*/
```
When Cog processes this file with `-DNTypes=16`, the Python code executes
and outputs the expanded template parameter list supporting 16 types.
---
## Adding a New Generator
1. Create `include/etl/generators/<name>_cpp03_generator.h` with Cog
directives.
2. Add a corresponding entry to `generate.bat`.
3. Run `generate.bat` (or the equivalent Cog command) to produce
`include/etl/private/<name>_cpp03.h`.
4. Commit both the generator and the generated output.
5. Verify with `python3 scripts/generator_test.py`.
---
## Troubleshooting
| Problem | Solution |
|---|---|
| `ModuleNotFoundError: No module named 'cogapp'` | Install Cog: `pip install cogapp` |
| Generator output differs from committed file | Regenerate and commit the updated output |
| Need more than 16 types/handlers | Change `-DNTypes=` or `-DHandlers=` and regenerate |