Skip to content

Commit 4d99e54

Browse files
Add support for C++-26 reflection (#713)
1 parent b132dcc commit 4d99e54

103 files changed

Lines changed: 7017 additions & 5907 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/linux.yaml

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: linux
22

33
on: [ push, pull_request ]
44

5-
env:
5+
env:
66
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
77

88
jobs:
@@ -15,32 +15,45 @@ jobs:
1515
compiler-version: [11, 12, 13, 14, 16, 17, 18]
1616
cxx: [20, 23]
1717
exclude:
18-
- compiler: gcc
18+
- compiler: gcc
1919
compiler-version: 16
20-
- compiler: gcc
20+
- compiler: gcc
2121
compiler-version: 17
22-
- compiler: gcc
22+
- compiler: gcc
2323
compiler-version: 18
2424
- compiler: llvm
2525
compiler-version: 11
26-
- compiler: llvm
26+
- compiler: llvm
2727
compiler-version: 12
28-
- compiler: llvm
28+
- compiler: llvm
2929
compiler-version: 13
30-
- compiler: llvm
30+
- compiler: llvm
3131
compiler-version: 14
3232
- compiler: gcc
3333
compiler-version: 11
34-
cxx: 23
34+
cxx: 23
3535
- compiler: gcc
3636
compiler-version: 12
37-
cxx: 23
38-
- compiler: llvm
37+
cxx: 23
38+
- compiler: llvm
3939
compiler-version: 16
40-
cxx: 23
40+
cxx: 23
4141
- compiler: llvm
4242
compiler-version: 17
43-
cxx: 23
43+
cxx: 23
44+
- compiler: llvm
45+
cxx: 23
46+
format: "CBOR"
47+
- compiler: llvm
48+
cxx: 23
49+
format: "UBJSON"
50+
- compiler: llvm
51+
cxx: 23
52+
format: "benchmarks"
53+
- compiler: llvm
54+
cxx: 23
55+
format: "headers"
56+
# TODO: Include reflection runners, once gcc-16 is in the repo.
4457
name: "${{ github.job }} (${{ matrix.format }}-C++${{ matrix.cxx }}-${{ matrix.compiler }}-${{ matrix.compiler-version }})"
4558
concurrency:
4659
group: "linux-${{ github.ref }}-${{ github.job }}-${{ matrix.format }}-C++${{ matrix.cxx }}-${{ matrix.compiler }}-${{ matrix.compiler-version }}"
@@ -64,7 +77,11 @@ jobs:
6477
run: |
6578
sudo apt update
6679
if [[ "${{ matrix.compiler-version }}" == 11 ]]; then
67-
sudo apt install -y ninja-build g++-11
80+
sudo apt install -y ninja-build g++-11
81+
elif [[ "${{ matrix.compiler }}" == "gcc" && "${{ matrix.compiler-version }}" == 16 ]]; then
82+
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
83+
sudo apt update
84+
sudo apt install -y ninja-build g++-16
6885
else
6986
sudo apt install -y ninja-build
7087
fi
@@ -78,25 +95,29 @@ jobs:
7895
export CXX=g++-${{ matrix.compiler-version }}
7996
fi
8097
$CXX --version
98+
EXTRA_CMAKE_FLAGS=""
99+
if [[ "${{ matrix.reflection }}" == "on" ]]; then
100+
EXTRA_CMAKE_FLAGS="-DREFLECTCPP_USE_CPP26_REFLECTION=ON -DCMAKE_CXX_FLAGS=-freflection"
101+
fi
81102
if [[ "${{ matrix.format }}" == "JSON" ]]; then
82-
cmake -S . -B build -G Ninja -DCMAKE_CXX_STANDARD=${{ matrix.cxx }} -DCMAKE_CXX_STANDARD=20 -DREFLECTCPP_BUILD_TESTS=ON -DCMAKE_BUILD_TYPE=Release
103+
cmake -S . -B build -G Ninja -DCMAKE_CXX_STANDARD=${{ matrix.cxx }} -DREFLECTCPP_BUILD_TESTS=ON -DCMAKE_BUILD_TYPE=Release $EXTRA_CMAKE_FLAGS
83104
elif [[ "${{ matrix.format }}" == "benchmarks" ]]; then
84-
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
105+
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
85106
elif [[ "${{ matrix.format }}" == "headers" ]]; then
86-
cmake -S . -B build -G Ninja -DCMAKE_CXX_STANDARD=${{ matrix.cxx }} -DCMAKE_CXX_STANDARD=20 -DREFLECTCPP_CHECK_HEADERS=ON -DCMAKE_BUILD_TYPE=Release
107+
cmake -S . -B build -G Ninja -DCMAKE_CXX_STANDARD=${{ matrix.cxx }} -DREFLECTCPP_CHECK_HEADERS=ON -DCMAKE_BUILD_TYPE=Release $EXTRA_CMAKE_FLAGS
87108
else
88-
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
109+
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
89110
fi
90111
cmake --build build -j4
91112
- name: Run tests
92113
if: (matrix.format != 'benchmarks') && (matrix.format != 'headers')
93114
run: |
94115
ctest --test-dir build --output-on-failure
95-
- name: Run benchmarks
96-
if: matrix.format == 'benchmarks'
116+
- name: Run benchmarks
117+
if: matrix.format == 'benchmarks'
97118
run: |
98-
echo '# Benchmarks' >> $GITHUB_STEP_SUMMARY
99-
echo '## Benchmarks across different formats' >> $GITHUB_STEP_SUMMARY
119+
echo '# Benchmarks' >> $GITHUB_STEP_SUMMARY
120+
echo '## Benchmarks across different formats' >> $GITHUB_STEP_SUMMARY
100121
echo '```' >> $GITHUB_STEP_SUMMARY
101122
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=canada_read >> $GITHUB_STEP_SUMMARY
102123
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=canada_write >> $GITHUB_STEP_SUMMARY
@@ -105,7 +126,7 @@ jobs:
105126
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=person_read >> $GITHUB_STEP_SUMMARY
106127
./build/benchmarks/all/reflect-cpp-all-format-benchmarks --benchmark_filter=person_write >> $GITHUB_STEP_SUMMARY
107128
echo '```' >> $GITHUB_STEP_SUMMARY
108-
echo '## Benchmarks for JSON' >> $GITHUB_STEP_SUMMARY
129+
echo '## Benchmarks for JSON' >> $GITHUB_STEP_SUMMARY
109130
echo '```' >> $GITHUB_STEP_SUMMARY
110131
./build/benchmarks/json/reflect-cpp-json-benchmarks --benchmark_filter=person_read >> $GITHUB_STEP_SUMMARY
111132
./build/benchmarks/json/reflect-cpp-json-benchmarks --benchmark_filter=canada >> $GITHUB_STEP_SUMMARY

CMakeLists.txt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,14 @@ option(REFLECTCPP_CHECK_HEADERS "Make sure that all headers are self-contained"
3030

3131
option(REFLECTCPP_USE_BUNDLED_DEPENDENCIES "Use the bundled dependencies" ON)
3232

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

35-
if(REFLECTCPP_USE_STD_EXPECTED)
36+
if(REFLECTCPP_USE_CPP26_REFLECTION)
37+
if(NOT DEFINED CMAKE_CXX_STANDARD)
38+
set(CMAKE_CXX_STANDARD 26)
39+
endif()
40+
elseif(REFLECTCPP_USE_STD_EXPECTED)
3641
if(NOT DEFINED CMAKE_CXX_STANDARD)
3742
set(CMAKE_CXX_STANDARD 23)
3843
endif()
@@ -229,6 +234,10 @@ if(REFLECTCPP_BUILD_SHARED)
229234
target_compile_definitions(reflectcpp PUBLIC RFL_BUILD_SHARED)
230235
endif()
231236

237+
if(REFLECTCPP_USE_CPP26_REFLECTION)
238+
target_compile_definitions(reflectcpp PUBLIC REFLECTCPP_USE_CPP26_REFLECTION)
239+
endif()
240+
232241
if(REFLECTCPP_USE_STD_EXPECTED)
233242
target_compile_definitions(reflectcpp PUBLIC REFLECTCPP_USE_STD_EXPECTED)
234243
endif()

README.md

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212

1313
![image](banner1.png)
1414

15-
**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.
15+
**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.
16+
17+
reflect-cpp supports C++-26 reflection, but most of the functionality is also available in C++-20, except where explicitly noted otherwise.
1618

1719
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.
1820

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

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

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

65-
| Format | Library | Version | License | Remarks |
66-
|--------------|------------------------------------------------------|--------------|------------| -----------------------------------------------------|
67-
| JSON | [yyjson](https://github.com/ibireme/yyjson) | >= 0.8.0 | MIT | out-of-the-box support, included in this repository |
68-
| Avro | [avro-c](https://avro.apache.org/docs/1.11.1/api/c/) | >= 1.11.3 | Apache 2.0 | Schemaful binary format |
68+
| Format | Library | Version | License | Remarks |
69+
|---------------------|------------------------------------------------------|--------------|------------| -----------------------------------------------------|
70+
| JSON | [yyjson](https://github.com/ibireme/yyjson) | >= 0.8.0 | MIT | out-of-the-box support, included in this repository |
71+
| Avro | [avro-c](https://avro.apache.org/docs/1.11.1/api/c/) | >= 1.11.3 | Apache 2.0 | Schemaful binary format |
6972
| 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 |
70-
| BSON | [libbson](https://github.com/mongodb/mongo-c-driver) | >= 1.25.1 | Apache 2.0 | JSON-like binary format |
71-
| Cap'n Proto | [capnproto](https://capnproto.org) | >= 1.0.2 | MIT | Schemaful binary format |
72-
| CBOR | [jsoncons](https://github.com/danielaparker/jsoncons)| >= 0.176.0 | BSL 1.0 | JSON-like binary format |
73-
| cli | *(none)* | *(none)* | MIT | Command line interface |
74-
| env | *(none)* | *(none)* | MIT | Environment variables |
75-
| Cereal | [Cereal](https://uscilab.github.io/cereal/) | >= 1.3.2 | BSD | C++ serialization library with multiple formats |
73+
| BSON | [libbson](https://github.com/mongodb/mongo-c-driver) | >= 1.25.1 | Apache 2.0 | JSON-like binary format |
74+
| Cap'n Proto | [capnproto](https://capnproto.org) | >= 1.0.2 | MIT | Schemaful binary format |
75+
| CBOR | [jsoncons](https://github.com/danielaparker/jsoncons)| >= 0.176.0 | BSL 1.0 | JSON-like binary format |
76+
| cli | *(none)* | *(none)* | MIT | Command line interface |
77+
| env | *(none)* | *(none)* | MIT | Environment variables |
78+
| Cereal | [Cereal](https://uscilab.github.io/cereal/) | >= 1.3.2 | BSD | C++ serialization library with multiple formats |
7679
| CSV | [Apache Arrow](https://arrow.apache.org/) | >= 21.0.0 | Apache 2.0 | Tabular textual format |
7780
| flexbuffers | [flatbuffers](https://github.com/google/flatbuffers) | >= 23.5.26 | Apache 2.0 | Schema-less version of flatbuffers, binary format |
7881
| msgpack | [msgpack-c](https://github.com/msgpack/msgpack-c) | >= 6.0.0 | BSL 1.0 | JSON-like binary format |
@@ -666,11 +669,29 @@ Finally, it is very easy to extend full support to your own classes, refer to th
666669
667670
## Installation
668671
669-
The following compilers are supported:
672+
The following compilers are supported for C++-20:
670673
- GCC 11.4 or higher
671674
- Clang 14.0 or higher
672675
- MSVC 17.8 (19.38) or higher
673676
677+
The following compilers are supported for C++-26:
678+
- GCC 16.2 or higher
679+
680+
### Compiling with C++-26 reflection
681+
682+
To compile reflect-cpp using the standard C++ reflection facilities, pass the CMake option
683+
`REFLECTCPP_USE_CPP26_REFLECTION` together with the compiler flag that activates reflection
684+
support in your compiler (`-freflection` for GCC, `-freflection-latest` for Clang):
685+
686+
```bash
687+
cmake -S . -B build -DCMAKE_CXX_STANDARD=26 -DCMAKE_BUILD_TYPE=Release -DREFLECTCPP_USE_CPP26_REFLECTION=ON -DCMAKE_CXX_FLAGS="-freflection"
688+
cmake --build build -j 4
689+
```
690+
691+
With C++-26 reflection, fixed-size C arrays and inheritance are supported out of the box (no
692+
`-DREFLECT_CPP_C_ARRAYS_OR_INHERITANCE` flag needed), and there are no range restrictions for
693+
enums. Refer to the [documentation](https://rfl.getml.com/cpp26_reflection) for details.
694+
674695
### Using vcpkg
675696

676697
https://vcpkg.io/en/package/reflectcpp

benchmarks/json/person.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ rfl::Result<std::vector<Person>> yyjson_to_children(yyjson_val *_val) {
157157
while ((val = yyjson_arr_iter_next(&iter))) {
158158
auto r = yyjson_to_person(val);
159159
if (!r) {
160-
return error(r.error());
160+
return rfl::error(r.error());
161161
}
162162
children.emplace_back(std::move(*r));
163163
}
@@ -220,7 +220,7 @@ rfl::Result<Person> yyjson_to_person(yyjson_val *_val) {
220220
for (const auto &err : errors) {
221221
std::cout << err.what() << std::endl;
222222
}
223-
return error(errors[0]);
223+
return rfl::error(errors[0]);
224224
}
225225
return person;
226226
}

docs/c_arrays_and_inheritance.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ inheritance altogether.
1212
Note that C arrays are not the same thing as `std::array`. `std::array` is always
1313
supported and is the recommended alternative.
1414

15+
!!! note "C++-26 reflection"
16+
17+
If you compile reflect-cpp with C++-26 reflection (see [C++26 reflection](cpp26_reflection.md)),
18+
then none of the restrictions in this section apply: fixed-size C arrays and inheritance are
19+
supported out of the box, no flag is required, and the fields may be spread out over
20+
multiple structs.
21+
1522
If you want support for these, you will have to pass the flag `-D REFLECT_CPP_C_ARRAYS_OR_INHERITANCE`
1623
during compilation.
1724

@@ -85,6 +92,10 @@ struct Derived : Base {
8592
};
8693
```
8794
95+
Note that this restriction does not apply when compiling with C++-26 reflection: with C++-26,
96+
the fields of the base class and the fields of the derived class are combined automatically,
97+
so the example above works as well.
98+
8899
The recommended alternative is to simply use `rfl::Flatten`, which
89100
has no such limitation:
90101

0 commit comments

Comments
 (0)