📖 Full documentation on GitHub Pages
A header-only C++20 framework with a Python/Jinja2 code generator for building type-safe, zero-allocation data layers — targeting embedded systems (MCU / bare-metal) as well as host applications.
You describe your data model in JSON or YAML files. The generator produces a single datalayer.h
header that contains fully type-safe constinit datapoint globals, with access control enforced at
compile time, optional range checking, optional file persistence, optional fmt formatters, and
optional Python bindings.
| Feature | Description |
|---|---|
| C++20 | Requires C++20 — leverages Concepts, NTTPs, consteval, constinit, std::span |
| Header-only framework | src/include/ has no compiled artifacts and no external dependencies |
| Compile-time access control | READ_ONLY, WRITE_ONLY, READ_WRITE enforced via C++20 Concepts |
| Range checking | Alias types carry Minimum/Maximum; violations return RangeCheck::underflow/overflow |
| Versioning | Groups and individual datapoints carry Version{major, minor, build} for EEPROM upgrade paths |
| Persistence | Binary file serialization with group- and datapoint-level version validation (-DENABLE_FILE_PERSISTENCE=ON) |
| fmt support | Auto-generated fmt::formatter<> for all custom structs and enums (-DENABLE_FMT=ON) |
| Python bindings | Auto-generated pybind11 module (-DENABLE_PYBIND11=ON) |
| JSON & YAML models | Write model files in JSON or YAML; mixed directories supported |
| Cross-compilation | ARM Cortex-M4 GCC toolchain included |
| Conan package | Distributable as a Conan header-library package with consumer helper CMake function |
- CMake ≥ 3.19
- C++20 compiler (GCC 10+, Clang 15+, MSVC 2022)
- Python ≥ 3.9 with
pip install jinja2 jsonschema pyyaml - Conan 2.x
# Install dependencies via Conan and configure
conan install . --build=missing
cmake --preset gcc-14-release # see CMakePresets.json for available presets
# Build
cmake --build --preset gcc-14-release
# Run tests
ctest --preset gcc-14-releasecd docker
./build-dev-image.sh # developer image with all tools pre-installed
./build-ci-image.sh # CI imagesrc/
├── include/ ← Header-only C++ framework (ship this + generated header)
├── generator/ ← Python code generator (generator.py, validators, templates)
├── template/ ← Jinja2 templates (datalayer.h, formatter.h, pythonBinding.cpp)
├── model/ ← Example model files (JSON)
├── main.cpp ← Host demo executable
└── embedded.cpp ← Bare-metal demo executable
test/
├── catch2/ ← C++ unit tests (Catch2)
└── python/ ← Python unit tests for the generator
docs/ ← Sphinx documentation source
cmake/ ← CMake helpers, toolchains, find modules
Place your model files in any directory and pass it via --model_dir. Five file types are
recognised (JSON or YAML):
| File | Purpose |
|---|---|
groups.json |
Define groups — each group has a baseId, version, and persistence mode |
datapoints.json |
Define datapoints — type, access, default value, version, optional namespace |
structs.json |
Define custom struct types (nestable) |
enums.json |
Define enumerations — auto-ID or explicit values |
types.json |
Define alias types with optional compile-time range bounds |
{
"Groups": [
{ "name": "DefaultGroup", "persistence": "None", "baseId": "0x4000", "version": "1.0.1" },
{ "name": "CyclicGroup", "persistence": "Cyclic", "baseId": "0x5000", "version": "2.0.0" }
]
}{
"Datapoints": [
{
"name": "test", "group": "DefaultGroup", "id": 1,
"type": "int32_t", "default": 4211,
"access": "READ_WRITE", "namespace": "Testify", "version": "1.0.1"
}
]
}See the Model File Reference for the full specification.
python src/generator/generator.py \
--model_dir src/model \
--out_dir build/ \
--template_dir src/template \
--schema_dir src/generator \
--module_name my_module # optional pybind11 module nameGenerated output under build/generated/:
| File | Content |
|---|---|
include/datalayer.h |
All group infos, types, enums, structs, constinit datapoint globals |
include/formatter.h |
fmt::formatter<> specialisations for every generated struct and enum |
datalayer_example/pythonBinding.cpp |
pybind11 module source |
doc/*.puml |
PlantUML class diagrams |
doc/overview.csv |
Datapoint overview table |
| Option | Default | Description |
|---|---|---|
ENABLE_FMT |
OFF |
Enable fmt library support; compiles with -DUSE_FMT |
ENABLE_FILE_PERSISTENCE |
OFF |
Enable binary file persistence; compiles with -DUSE_FILE_PERSISTENCE |
ENABLE_PYBIND11 |
OFF |
Build pybind11 Python module |
ENABLE_TESTING |
ON |
Build Catch2 test suite |
ENABLE_DOCS |
OFF |
Build Sphinx/Doxygen documentation |
DOCS_ONLY |
OFF |
Skip all C++ targets and Conan — docs build only |
#include <include/datalayer.h> // generated
#include <include/formatter.h> // generated (requires ENABLE_FMT)
#include <fmt/format.h>
int main()
{
// Print group structure
DefaultGroup.printDatapoints();
// Read / write a typed datapoint
std::ignore = Testify::test.set(42); // returns RangeCheck::ok
int32_t val = Testify::test.get(); // val == 42
// Range-checked alias type (min=100, max=200)
auto rc = TestAlias.set(AliasTypeUint32t{42});
// rc == DataLayer::Detail::RangeCheck::underflow
// Struct datapoint
constexpr Temperature a{ .raw = 1234, .value = 42.2f };
std::ignore = test4.set(a);
// Runtime-ID dispatch across all groups
auto result = Dispatcher.setDatapoint(0x4001, 99);
// result.success == true, result.check == RangeCheck::ok
#ifdef USE_FILE_PERSISTENCE
// Persist and restore CyclicGroup
CyclicGroup.serializeGroup("sample.bin");
CyclicGroup.deserializeGroup("sample.bin");
#endif
}conan create . --build=missing # build & install into local cacheIn your project's conanfile.txt:
[requires]
datalayer-generator/0.0.1
[options]
datalayer-generator/*:with_fmt=True
[generators]
CMakeDeps
CMakeToolchainIn your CMakeLists.txt:
find_package(DataLayerGenerator REQUIRED)
add_executable(my_app main.cpp)
target_link_libraries(my_app PRIVATE DataLayerGenerator::DataLayerGenerator)
generate_datalayer(my_app
MODEL_DIR ${CMAKE_CURRENT_SOURCE_DIR}/model
OUT_DIR ${CMAKE_CURRENT_BINARY_DIR}
)The generate_datalayer() helper is automatically available after find_package — it wires up the
Python generator as a stamp-file-based custom command so the generator only reruns when model files
change.
Full documentation — including API reference, model file specification, and integration guide — is
published automatically to GitHub Pages on every push to main:
➡ https://StephanKa.github.io/DataLayerGenerator/
To build the docs locally:
pip install sphinx sphinx-rtd-theme breathe sphinx-copybutton
mkdir -p build-docs/doxygen
sed \
-e "s|@DOXYGEN_INPUT_DIR@|$(pwd)/src/include|g" \
-e "s|@DOXYGEN_OUTPUT_DIR@|$(pwd)/build-docs/doxygen|g" \
docs/Doxyfile.in > build-docs/Doxyfile
doxygen build-docs/Doxyfile
sphinx-build -b html \
-Dbreathe_projects.DataLayerGenerator="$(pwd)/build-docs/doxygen/xml" \
docs/ build-docs/sphinx
# Output: build-docs/sphinx/index.htmlThis project is released into the public domain under the Unlicense.