Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
070d51e
Isolated resources that use C++20 hacks
liuzicheng1987 Aug 10, 2026
94f0913
Implemented bind_to_tuple and num_fields
liuzicheng1987 Aug 11, 2026
bd1ca57
Adapted surrounding headers
liuzicheng1987 Aug 11, 2026
ec50d13
Added concat_literals
liuzicheng1987 Aug 12, 2026
3ac42d5
Fixed two bugs
liuzicheng1987 Aug 15, 2026
43981f6
Made everything consteval
liuzicheng1987 Aug 15, 2026
02037cf
Make sure rename is applied
liuzicheng1987 Aug 15, 2026
36fba5c
Started working on inheritance
liuzicheng1987 Aug 15, 2026
8bffdff
Made inheritance work
liuzicheng1987 Aug 16, 2026
9c619dd
Minor code beautification
liuzicheng1987 Aug 16, 2026
5e6489a
Fixed typo in test
liuzicheng1987 Aug 16, 2026
02d8535
Began developing enums
liuzicheng1987 Aug 16, 2026
dd996f5
Added support for enums
liuzicheng1987 Aug 16, 2026
cd09619
Updated range_defined
liuzicheng1987 Aug 16, 2026
e64f181
Started adapting the README
liuzicheng1987 Aug 18, 2026
c3da851
Renamed the flag
liuzicheng1987 Aug 18, 2026
5b5b17f
Write workaround to avoid false positive warning
liuzicheng1987 Aug 18, 2026
18f0a95
Fixed minor issues in the tests
liuzicheng1987 Aug 18, 2026
40d64f2
Added documentation
liuzicheng1987 Aug 18, 2026
aaca276
Fixed issues in the C++-20 build
liuzicheng1987 Aug 20, 2026
ed74536
Fixed first test failure: Make sure that enum ranges work
liuzicheng1987 Aug 22, 2026
5d30c0b
Fixed error in test
liuzicheng1987 Aug 22, 2026
2096a3b
Don't automatically set CPP26 to true
liuzicheng1987 Aug 22, 2026
bedcbe3
Removed remaining references to enchantum
liuzicheng1987 Aug 22, 2026
4fdb50f
Added C++ 26 reflection to the pipeline
liuzicheng1987 Aug 22, 2026
ff05444
Get type name using C++ 26
liuzicheng1987 Aug 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 89 additions & 5 deletions .github/workflows/linux.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,82 @@ jobs:
- compiler: llvm
compiler-version: 17
cxx: 23
include:
- format: JSON
compiler: gcc
compiler-version: 16
cxx: 26
reflection: "on"
- format: AVRO
compiler: gcc
compiler-version: 16
cxx: 26
reflection: "on"
- format: BOOST_SERIALIZATION
compiler: gcc
compiler-version: 16
cxx: 26
reflection: "on"
- format: CAPNPROTO
compiler: gcc
compiler-version: 16
cxx: 26
reflection: "on"
- format: CBOR
compiler: gcc
compiler-version: 16
cxx: 26
reflection: "on"
- format: CEREAL
compiler: gcc
compiler-version: 16
cxx: 26
reflection: "on"
- format: FLEXBUFFERS
compiler: gcc
compiler-version: 16
cxx: 26
reflection: "on"
- format: MSGPACK
compiler: gcc
compiler-version: 16
cxx: 26
reflection: "on"
- format: PARQUET
compiler: gcc
compiler-version: 16
cxx: 26
reflection: "on"
- format: TOML
compiler: gcc
compiler-version: 16
cxx: 26
reflection: "on"
- format: UBJSON
compiler: gcc
compiler-version: 16
cxx: 26
reflection: "on"
- format: XML
compiler: gcc
compiler-version: 16
cxx: 26
reflection: "on"
- format: YAML
compiler: gcc
compiler-version: 16
cxx: 26
reflection: "on"
- format: benchmarks
compiler: gcc
compiler-version: 16
cxx: 26
reflection: "on"
- format: headers
compiler: gcc
compiler-version: 16
cxx: 26
reflection: "on"
name: "${{ github.job }} (${{ matrix.format }}-C++${{ matrix.cxx }}-${{ matrix.compiler }}-${{ matrix.compiler-version }})"
concurrency:
group: "linux-${{ github.ref }}-${{ github.job }}-${{ matrix.format }}-C++${{ matrix.cxx }}-${{ matrix.compiler }}-${{ matrix.compiler-version }}"
Expand All @@ -64,7 +140,11 @@ jobs:
run: |
sudo apt update
if [[ "${{ matrix.compiler-version }}" == 11 ]]; then
sudo apt install -y ninja-build g++-11
sudo apt install -y ninja-build g++-11
elif [[ "${{ matrix.compiler }}" == "gcc" && "${{ matrix.compiler-version }}" == 16 ]]; then
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
sudo apt update
sudo apt install -y ninja-build g++-16
else
sudo apt install -y ninja-build
fi
Expand All @@ -78,14 +158,18 @@ jobs:
export CXX=g++-${{ matrix.compiler-version }}
fi
$CXX --version
EXTRA_CMAKE_FLAGS=""
if [[ "${{ matrix.reflection }}" == "on" ]]; then
EXTRA_CMAKE_FLAGS="-DREFLECTCPP_USE_CPP26_REFLECTION=ON -DCMAKE_CXX_FLAGS=-freflection"
fi
if [[ "${{ matrix.format }}" == "JSON" ]]; then
cmake -S . -B build -G Ninja -DCMAKE_CXX_STANDARD=${{ matrix.cxx }} -DCMAKE_CXX_STANDARD=20 -DREFLECTCPP_BUILD_TESTS=ON -DCMAKE_BUILD_TYPE=Release
cmake -S . -B build -G Ninja -DCMAKE_CXX_STANDARD=${{ matrix.cxx }} -DREFLECTCPP_BUILD_TESTS=ON -DCMAKE_BUILD_TYPE=Release $EXTRA_CMAKE_FLAGS
elif [[ "${{ matrix.format }}" == "benchmarks" ]]; then
cmake -S . -B build -G Ninja -DCMAKE_CXX_STANDARD=${{ matrix.cxx }} -DCMAKE_CXX_STANDARD=20 -DREFLECTCPP_BUILD_BENCHMARKS=ON -DREFLECTCPP_ALL_FORMATS=ON -DCMAKE_BUILD_TYPE=Release
cmake -S . -B build -G Ninja -DCMAKE_CXX_STANDARD=${{ matrix.cxx }} -DREFLECTCPP_BUILD_BENCHMARKS=ON -DREFLECTCPP_ALL_FORMATS=ON -DCMAKE_BUILD_TYPE=Release $EXTRA_CMAKE_FLAGS
elif [[ "${{ matrix.format }}" == "headers" ]]; then
cmake -S . -B build -G Ninja -DCMAKE_CXX_STANDARD=${{ matrix.cxx }} -DCMAKE_CXX_STANDARD=20 -DREFLECTCPP_CHECK_HEADERS=ON -DCMAKE_BUILD_TYPE=Release
cmake -S . -B build -G Ninja -DCMAKE_CXX_STANDARD=${{ matrix.cxx }} -DREFLECTCPP_CHECK_HEADERS=ON -DCMAKE_BUILD_TYPE=Release $EXTRA_CMAKE_FLAGS
else
cmake -S . -B build -G Ninja -DCMAKE_CXX_STANDARD=${{ matrix.cxx }} -DCMAKE_CXX_STANDARD=20 -DREFLECTCPP_BUILD_TESTS=ON -DREFLECTCPP_JSON=OFF -DREFLECTCPP_${{ matrix.format }}=ON -DCMAKE_BUILD_TYPE=Release
cmake -S . -B build -G Ninja -DCMAKE_CXX_STANDARD=${{ matrix.cxx }} -DREFLECTCPP_BUILD_TESTS=ON -DREFLECTCPP_JSON=OFF -DREFLECTCPP_${{ matrix.format }}=ON -DCMAKE_BUILD_TYPE=Release $EXTRA_CMAKE_FLAGS
fi
cmake --build build -j4
- name: Run tests
Expand Down
11 changes: 10 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,14 @@ option(REFLECTCPP_CHECK_HEADERS "Make sure that all headers are self-contained"

option(REFLECTCPP_USE_BUNDLED_DEPENDENCIES "Use the bundled dependencies" ON)

option(REFLECTCPP_USE_CPP26_REFLECTION "Use standard C++ reflection (requires C++-26)" OFF)
option(REFLECTCPP_USE_STD_EXPECTED "Use std::expected instead of the built-in Result type (requires C++-23)" OFF)

if(REFLECTCPP_USE_STD_EXPECTED)
if(REFLECTCPP_USE_CPP26_REFLECTION)
if(NOT DEFINED CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 26)
endif()
elseif(REFLECTCPP_USE_STD_EXPECTED)
if(NOT DEFINED CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 23)
endif()
Expand Down Expand Up @@ -229,6 +234,10 @@ if(REFLECTCPP_BUILD_SHARED)
target_compile_definitions(reflectcpp PUBLIC RFL_BUILD_SHARED)
endif()

if(REFLECTCPP_USE_CPP26_REFLECTION)
target_compile_definitions(reflectcpp PUBLIC REFLECTCPP_USE_CPP26_REFLECTION)
endif()

if(REFLECTCPP_USE_STD_EXPECTED)
target_compile_definitions(reflectcpp PUBLIC REFLECTCPP_USE_STD_EXPECTED)
endif()
Expand Down
45 changes: 33 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@

![image](banner1.png)

**reflect-cpp** is a C++-20 library for **fast serialization, deserialization and validation** using reflection, similar to [pydantic](https://github.com/pydantic/pydantic) in Python, [serde](https://github.com/serde-rs) in Rust, [encoding](https://github.com/golang/go/tree/master/src/encoding) in Go or [aeson](https://github.com/haskell/aeson/tree/master) in Haskell.
**reflect-cpp** is a C++-20/C++-26 library for **fast serialization, deserialization and validation** using reflection, similar to [pydantic](https://github.com/pydantic/pydantic) in Python, [serde](https://github.com/serde-rs) in Rust, [encoding](https://github.com/golang/go/tree/master/src/encoding) in Go or [aeson](https://github.com/haskell/aeson/tree/master) in Haskell.

reflect-cpp supports C++-26 reflection, but most of the functionality is also available in C++-20, except where explicitly noted otherwise.

Moreover, reflect-cpp is the basis for [sqlgen](https://github.com/getml/sqlgen), a **modern, type-safe ORM and SQL query generator** for C++20, inspired by Python's SQLAlchemy/SQLModel and Rust's Diesel. It provides a fluent, composable interface for database operations with compile-time type checking and SQL injection protection.

Expand Down Expand Up @@ -51,6 +53,7 @@ reflect-cpp and sqlgen fill important gaps in C++ development. They reduce boile

### More in our [documentation](https://rfl.getml.com):
- [Installation ↗](https://rfl.getml.com/install/#option-2-compilation-using-cmake)
- [C++26 reflection ↗](https://rfl.getml.com/cpp26_reflection)
- [Benchmarks ↗](https://rfl.getml.com/benchmarks)
- [How to contribute ↗](https://rfl.getml.com/contributing)
- [Compiling and running the tests ↗](https://rfl.getml.com/contributing/#compiling-and-running-the-tests)
Expand All @@ -62,17 +65,17 @@ reflect-cpp provides a unified reflection-based interface across different seria

The following table lists the serialization formats currently supported by reflect-cpp and the underlying libraries used:

| Format | Library | Version | License | Remarks |
|--------------|------------------------------------------------------|--------------|------------| -----------------------------------------------------|
| JSON | [yyjson](https://github.com/ibireme/yyjson) | >= 0.8.0 | MIT | out-of-the-box support, included in this repository |
| Avro | [avro-c](https://avro.apache.org/docs/1.11.1/api/c/) | >= 1.11.3 | Apache 2.0 | Schemaful binary format |
| Format | Library | Version | License | Remarks |
|---------------------|------------------------------------------------------|--------------|------------| -----------------------------------------------------|
| JSON | [yyjson](https://github.com/ibireme/yyjson) | >= 0.8.0 | MIT | out-of-the-box support, included in this repository |
| Avro | [avro-c](https://avro.apache.org/docs/1.11.1/api/c/) | >= 1.11.3 | Apache 2.0 | Schemaful binary format |
| Boost.Serialization | [Boost.Serialization](https://www.boost.org/doc/libs/release/libs/serialization/) | >= 1.74.0 | BSL 1.0 | Streaming binary format with archive interop |
| BSON | [libbson](https://github.com/mongodb/mongo-c-driver) | >= 1.25.1 | Apache 2.0 | JSON-like binary format |
| Cap'n Proto | [capnproto](https://capnproto.org) | >= 1.0.2 | MIT | Schemaful binary format |
| CBOR | [jsoncons](https://github.com/danielaparker/jsoncons)| >= 0.176.0 | BSL 1.0 | JSON-like binary format |
| cli | *(none)* | *(none)* | MIT | Command line interface |
| env | *(none)* | *(none)* | MIT | Environment variables |
| Cereal | [Cereal](https://uscilab.github.io/cereal/) | >= 1.3.2 | BSD | C++ serialization library with multiple formats |
| BSON | [libbson](https://github.com/mongodb/mongo-c-driver) | >= 1.25.1 | Apache 2.0 | JSON-like binary format |
| Cap'n Proto | [capnproto](https://capnproto.org) | >= 1.0.2 | MIT | Schemaful binary format |
| CBOR | [jsoncons](https://github.com/danielaparker/jsoncons)| >= 0.176.0 | BSL 1.0 | JSON-like binary format |
| cli | *(none)* | *(none)* | MIT | Command line interface |
| env | *(none)* | *(none)* | MIT | Environment variables |
| Cereal | [Cereal](https://uscilab.github.io/cereal/) | >= 1.3.2 | BSD | C++ serialization library with multiple formats |
| CSV | [Apache Arrow](https://arrow.apache.org/) | >= 21.0.0 | Apache 2.0 | Tabular textual format |
| flexbuffers | [flatbuffers](https://github.com/google/flatbuffers) | >= 23.5.26 | Apache 2.0 | Schema-less version of flatbuffers, binary format |
| msgpack | [msgpack-c](https://github.com/msgpack/msgpack-c) | >= 6.0.0 | BSL 1.0 | JSON-like binary format |
Expand Down Expand Up @@ -666,11 +669,29 @@ Finally, it is very easy to extend full support to your own classes, refer to th

## Installation

The following compilers are supported:
The following compilers are supported for C++-20:
- GCC 11.4 or higher
- Clang 14.0 or higher
- MSVC 17.8 (19.38) or higher

The following compilers are supported for C++-26:
- GCC 16.2 or higher

### Compiling with C++-26 reflection

To compile reflect-cpp using the standard C++ reflection facilities, pass the CMake option
`REFLECTCPP_USE_CPP26_REFLECTION` together with the compiler flag that activates reflection
support in your compiler (`-freflection` for GCC, `-freflection-latest` for Clang):

```bash
cmake -S . -B build -DCMAKE_CXX_STANDARD=26 -DCMAKE_BUILD_TYPE=Release -DREFLECTCPP_USE_CPP26_REFLECTION=ON -DCMAKE_CXX_FLAGS="-freflection"
cmake --build build -j 4
```

With C++-26 reflection, fixed-size C arrays and inheritance are supported out of the box (no
`-DREFLECT_CPP_C_ARRAYS_OR_INHERITANCE` flag needed), and there are no range restrictions for
enums. Refer to the [documentation](https://rfl.getml.com/cpp26_reflection) for details.

### Using vcpkg

https://vcpkg.io/en/package/reflectcpp
Expand Down
11 changes: 11 additions & 0 deletions docs/c_arrays_and_inheritance.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ inheritance altogether.
Note that C arrays are not the same thing as `std::array`. `std::array` is always
supported and is the recommended alternative.

!!! note "C++-26 reflection"

If you compile reflect-cpp with C++-26 reflection (see [C++26 reflection](cpp26_reflection.md)),
then none of the restrictions in this section apply: fixed-size C arrays and inheritance are
supported out of the box, no flag is required, and the fields may be spread out over
multiple structs.

If you want support for these, you will have to pass the flag `-D REFLECT_CPP_C_ARRAYS_OR_INHERITANCE`
during compilation.

Expand Down Expand Up @@ -85,6 +92,10 @@ struct Derived : Base {
};
```

Note that this restriction does not apply when compiling with C++-26 reflection: with C++-26,
the fields of the base class and the fields of the derived class are combined automatically,
so the example above works as well.

The recommended alternative is to simply use `rfl::Flatten`, which
has no such limitation:

Expand Down
126 changes: 126 additions & 0 deletions docs/cpp26_reflection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# C++26 reflection

reflect-cpp can be compiled in two modes, which use two different reflection implementations:

* **C++-20/23 mode (the default):** reflection is implemented using metaprogramming
techniques (structured bindings, aggregate-initialization analysis, and
[enchantum](https://github.com/ZXShady/enchantum)). This works on a wide range of
compilers, but it has some limitations and comes with a higher compile-time cost.
* **C++-26 mode:** reflection uses the standard C++ reflection facilities
(`<meta>`, [P2996](https://wg21.link/P2996)). This is more powerful, because members
and enumerators are queried directly from the compiler instead of being inferred.

C++-26 mode is opt-in. It requires a compiler that supports the C++ reflection proposal
and a compiler flag to activate it. The rest of this documentation applies to both modes,
except where explicitly noted otherwise.

## Compiling with C++-26 reflection

The following compilers are supported for C++-26:

* GCC 16.2 or higher
* Clang: experimental, only available in Clang builds that implement
[P2996](https://wg21.link/P2996) (such as Bloomberg's
[clang-p2996](https://github.com/bloomberg/clang-p2996) fork)

To compile reflect-cpp with C++-26 reflection, pass the CMake option
`REFLECTCPP_USE_CPP26_REFLECTION` and the compiler flag that activates reflection
support in your compiler:

* GCC: `-freflection`
* Clang: `-freflection-latest`

For example, using cmake:

```bash
cmake -S . -B build -DCMAKE_CXX_STANDARD=26 -DCMAKE_BUILD_TYPE=Release -DREFLECTCPP_USE_CPP26_REFLECTION=ON -DCMAKE_CXX_FLAGS="-freflection"
cmake --build build -j 4
```

The individual parts:

* `-DREFLECTCPP_USE_CPP26_REFLECTION=ON` switches reflect-cpp to the C++-26 reflection
implementation and defines the macro `REFLECTCPP_USE_CPP26_REFLECTION`. If
`CMAKE_CXX_STANDARD` is not set, it will be set to 26 automatically.
* `-DCMAKE_CXX_STANDARD=26` sets the C++ standard to 26.
* `-DCMAKE_CXX_FLAGS="-freflection"` activates the C++ reflection facilities in the
compiler itself. Use `-freflection-latest` on Clang instead.

When compiling with C++-26 reflection, the option `REFLECTCPP_USE_STD_EXPECTED` is
enabled by default as well, which means that `rfl::Result<T>` is an alias for
`std::expected<T, rfl::Error>`. If you want to keep using the built-in `rfl::Result`
type, pass `-DREFLECTCPP_USE_STD_EXPECTED=OFF`.

If you include the source files directly into your own build (see
[Installation](install.md#option-4-include-source-files-into-your-own-build)), add the
compile definition `-DREFLECTCPP_USE_CPP26_REFLECTION` and the appropriate compiler flag
to all translation units that include reflect-cpp.

## What C++-26 reflection changes

### C arrays and inheritance are supported out of the box

In C++-20/23 mode, fixed-size C arrays and inheritance are only supported when you pass
the flag `-DREFLECT_CPP_C_ARRAYS_OR_INHERITANCE`, and inheritance only works when all of
the fields are inside the same struct. Refer to
[C arrays and inheritance](c_arrays_and_inheritance.md) for details.

In C++-26 mode, neither restriction applies:

* Fixed-size C arrays are regular fields as far as the compiler's reflection is concerned,
so they work without any flag.
* The fields of base classes are combined with the fields of the derived class, so the
fields may be spread out over multiple structs.

For example, this is not supported in C++-20/23 mode, but works as-is in C++-26 mode:

```cpp
struct Base {
int x;
};

struct Derived : Base {
int y;
};

const auto derived = Derived{1, 2};

rfl::json::write(derived);
```

This results in the following JSON string:

```json
{"x":1,"y":2}
```

### No range restrictions for enums

In C++-20/23 mode, enum values must be in the range `[RFL_ENUM_RANGE_MIN,
RFL_ENUM_RANGE_MAX]`, where the default range is `[-256, 256]`. Refer to
[Enums](enums.md) for details.

In C++-26 mode, the enumerators are read directly from the compiler, so there is no
restriction on the range of enum values, and `RFL_ENUM_RANGE_MIN` and `RFL_ENUM_RANGE_MAX`
are not needed.

### Lower compile-time cost

In C++-20/23 mode, reflect-cpp infers the fields of a struct by figuring out how the
struct can be constructed. This requires a lot of compile-time work, especially in the
presence of C arrays and inheritance. In C++-26 mode, the fields and enumerators are
queried directly from the compiler's reflection information, which is considerably
cheaper.

## Summary

| Feature | C++-20/23 | C++-26 |
|---------|-----------|--------|
| Supported compilers | GCC 11.4+, Clang 14.0+, MSVC 17.8+ | GCC 16.2+ (Clang: experimental) |
| Compiler flag | *(none)* | `-freflection` (GCC), `-freflection-latest` (Clang) |
| CMake option | *(none)* | `-DREFLECTCPP_USE_CPP26_REFLECTION=ON` |
| Fixed-size C arrays | requires `-DREFLECT_CPP_C_ARRAYS_OR_INHERITANCE` | supported out of the box |
| Inheritance | requires `-DREFLECT_CPP_C_ARRAYS_OR_INHERITANCE`, fields must be in a single struct | supported out of the box, fields may be spread over multiple structs |
| Enum value range | restricted to `[RFL_ENUM_RANGE_MIN, RFL_ENUM_RANGE_MAX]` (default `[-256, 256]`) | no restriction |
| `rfl::Result` | built-in type (or `std::expected` via `-DREFLECTCPP_USE_STD_EXPECTED`) | `std::expected` by default |
| Compile time | higher | lower |
Loading
Loading