reflect-cpp unifies seventeen formats through concepts
A C++20 library for fast serialization, deserialization and validation using reflection. Supports JSON, Avro, Boost Serialization, BSON, Cap'n Proto, CBOR, Cereal, Command line interfaces, CSV, Environment variables, flexbuffers, msgpack, parquet, TOML, UBJSON, XML, YAML, yas / msgpack.org[C++20]
At a glance
- What is it?
- reflect-cpp is a C++20 serialization, deserialization and validation library that unifies JSON, Avro, BSON, Cap'n Proto, CBOR, msgpack, parquet, TOML, YAML and more behind one reflection-based interface. The interface is uniform, but each format brings its own dependency, version floor and licence, which is the cost the abstraction does not hide.
- Who is it for?
- Adopt reflect-cpp if you are on C++20 and need struct-driven JSON in both directions with validation and error messages, since JSON works out of the box with yyjson bundled in the repository and no third-party wiring. Do not adopt it expecting one dependency: many non-JSON formats add a library with its own version floor, from capnproto at 1.0.2 up to Apache Arrow at 21.0.0 for CSV and parquet.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly C++, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Star history
- getml/reflect-cpp1,960 ★ · +6
| Repository | 2026-10-16 | 2026-10-23 |
|---|---|---|
| getml/reflect-cpp | 2k | 2k |
6 Stars in the last 7 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
Sixteen formats, and only one of them ships in the box
The README frames the project by analogy: reflect-cpp is to C++ what pydantic is to Python, what serde is to Rust, what the encoding package is to Go, what aeson is to Haskell. That is a high bar, and the format table is where you find out how much of it is the library's own work.
The design is modular by construction. The README says reflect-cpp provides a unified reflection-based interface across formats and is deliberately designed in a modular way using concepts to make it as easy as possible to interface various C or C++ libraries related to serialization. Each format is therefore a separate adapter over a different underlying library, with its own version floor and its own licence:
| Format | Library | Version | License | | --- | --- | --- | --- | | JSON | yyjson | >= 0.8.0 | MIT | | Avro | avro-c | >= 1.11.3 | Apache 2.0 | | Boost.Serialization | Boost.Serialization | >= 1.74.0 | BSL 1.0 | | BSON | libbson | >= 1.25.1 | Apache 2.0 | | Cap'n Proto | capnproto | >= 1.0.2 | MIT | | CBOR | jsoncons | >= 0.176.0 | BSL 1.0 | | Cereal | Cereal | >= 1.3.2 | BSD | | CSV | Apache Arrow | >= 21.0.0 | Apache 2.0 | | flexbuffers | flatbuffers | >= 23.5.26 | Apache 2.0 | | msgpack | msgpack-c | >= 6.0.0 | BSL 1.0 | | parquet | Apache Arrow | >= 21.0.0 | Apache 2.0 | | TOML | toml++ | >= 3.4.0 | MIT |
Two formats have no underlying library at all: `cli` for command line interfaces and `env` for environment variables, both under MIT. Those two are implemented entirely by reflect-cpp, which is why CLI argument parsing and environment variable parsing appear as feature rows in the same table as CBOR.
JSON is the exception in one more way: the remarks column marks it as out-of-the-box support, included in this repository. Everything else is your dependency to fetch.
Concepts are the extension seam, for formats and for your own classes
The library's stated design principles are worth reading as a list of commitments rather than marketing. Close integration with containers from the C++ standard library, close adherence to C++ idioms, out-of-the-box JSON support, simple installation, simple extendability to other serialization formats, and simple extendability to custom classes.
Those last two are the same mechanism serving two different purposes, and the concepts-based design is what makes that possible. A format adapter is a type satisfying a constraint, not a class you inherit from. Adding your own format is a matter of satisfying the concept, and the documentation has a page specifically on supporting your own format. Adding your own types is a separate page on custom classes, which is a different problem: those need to be reflectable, and that is where the C++26 story comes in.
The README is careful about the standard version. reflect-cpp is described as a C++-20/C++-26 library, it supports C++-26 reflection, and most of the functionality is also available in C++-20 except where explicitly noted otherwise. That last clause is doing real work: it means there is a documented set of features that need C++26, and you have to read the docs to find which.
The compiler floors are gcc 11+, clang 14+ and MSVC 17+, which is the practical gate before any of this matters. If your toolchain is older, the answer is a compiler upgrade rather than a library workaround.
Tabular data and CLI parsing come from the same reflection model
Two of the more interesting capabilities do not involve a wire format at all.
Tabular data goes through Apache Arrow at version 21.0.0 or later, for both CSV as a tabular textual format and parquet as a tabular binary format. Both share the same dependency, so if you need both you pay for one library, and if you need neither you skip a 21.0.0 Arrow dependency, which is not small.
CLI argument parsing is the `cli` row, and it has no underlying library. Environment variables are the `env` row, likewise. The reason these work off the same reflection machinery is the same reason JSON works out of the box: reflect-cpp already knows how to turn a struct's fields into named, typed values, and a command line flag or an environment variable is just another source of named, typed values. Once you have a struct marked up with metadata, all three sources become cheap.
That is also what makes the error messages feature in the README's table of contents a natural one. Validation errors are reported against the struct's field names, and the documentation has a dedicated section on them rather than leaving error formatting to the caller.
JSON schema generation is on that list too, which follows from the same design: if the struct is the schema, emitting it is a traversal.
sqlgen is the second project built on the same foundation
The README devotes a paragraph to a downstream library, and it tells you more about reflect-cpp's intended niche than the feature list does. reflect-cpp is the basis for sqlgen, described as a modern, type-safe ORM and SQL query generator for C++20, inspired by Python's SQLAlchemy and SQLModel and Rust's Diesel, providing a fluent, composable interface for database operations with compile-time type checking and SQL injection protection.
The logic of that dependency is worth following. An ORM needs to map struct fields to columns, and a serialization library needs to map struct fields to keys. Doing that mapping twice, in two libraries, guarantees they drift. Sharing the reflection layer means the database view of a struct and the JSON view of a struct cannot disagree about what the fields are.
The README states the combined goal as filling important gaps in C++ development by reducing boilerplate and increasing code safety, together enabling reliable and efficient ETL pipelines. That is the actual use case to judge against. If you are moving records between systems rather than serving request traffic, reflect-cpp's format breadth is the reason it exists.
Note that sqlgen is a separate repository with its own versioning. Adopting reflect-cpp does not bring it along.
Where the abstraction stops, and where it is the wrong choice
The uniform interface does not make the underlying libraries uniform, and that is where projects get surprised.
Schemaful formats are a different category from schema-less ones. Avro and Cap'n Proto are marked schemaful binary formats, which means a schema is part of the contract and the writer and reader must agree. JSON is schema-less in this taxonomy. flexbuffers is described specifically as the schema-less version of flatbuffers. Writing code against reflect-cpp's uniform struct and then switching from JSON to Avro is not a drop-in change, because the round-trip semantics differ even though the API does not.
Licences accumulate. reflect-cpp itself is MIT, but the table shows BSL 1.0 for Boost.Serialization, CBOR, UBJSON and msgpack, Apache 2.0 for Avro, BSON, CSV, flexbuffers and parquet, and BSD for Cereal. That is not a problem for internal use, but if you ship a binary that statically links these, the Apache 2.0 notice obligations and the Boost Software License terms travel with the artifact.
Version floors are the other sharp edge. Apache Arrow at 21.0.0 is a much heavier commitment than capnproto at 1.0.2, and jsoncons at 0.176.0 is a pre-1.0 version with the API instability that implies. Enabling CSV means you own Arrow.
And the compiler requirement rules some projects out entirely. gcc 11 or clang 14 as a floor is recent enough that an embedded toolchain or a legacy cross-compile setup may not reach it.
Build systems, distribution and release cadence
The project ships the full set of C++ packaging routes, which is a reasonable signal of who it is aimed at. Top-level files include `CMakeLists.txt`, `conanfile.py`, `vcpkg.json` with a `vcpkg/` directory, `package.xml`, `reflectcpp-config.cmake.in` and `pyproject.toml`. So there is CMake, Conan, vcpkg, ROS package XML and a Python project file, which covers CMake-based C++, Conan users, Windows vcpkg users and ROS 2 packages in one repository.
The `pyproject.toml` is only for the documentation site. It declares the project name reflect-cpp with a dynamic version, and a single hatch environment with `mkdocs~=1.6`, `mkdocs-material~=9.7` and `mkdocs-htmlproofer-plugin~=1.2`. Those are the docs toolchain, not a Python binding. That is worth stating because the presence of a pyproject.toml in a C++ project usually implies a Python API, and here it does not.
`.gitmodules` is present, which is consistent with the README's claim that yyjson is included in this repository, likely as a submodule. `.clang-format` and `.clang-tidy` are both committed, so formatting and linting are enforced by the project's own configuration rather than left to each contributor.
Release cadence is regular and the versions are pre-1.0: v0.25.0 on 2026-05-16, v0.24.0 on 2026-02-13 and v0.23.0 on 2026-01-01. The repository is not archived and the last push was on 2026-10-14, so work is ongoing, but a zero-major version is the honest signal that the API can still move. There is also a `benchmarks/` directory and a documentation page for benchmarks; the README claims to be one of the fastest serialization libraries in existence on that basis, which is a claim you should treat as the maintainer's own rather than as settled fact.
Editorial conclusion
Adopt reflect-cpp if you are on C++20 and need struct-driven JSON in both directions with validation and error messages, since JSON works out of the box with yyjson bundled in the repository and no third-party wiring. Do not adopt it expecting one dependency: many non-JSON formats add a library with its own version floor, from capnproto at 1.0.2 up to Apache Arrow at 21.0.0 for CSV and parquet. Before starting, pick the one or two formats you actually need rather than enabling all of them, and note that C++26 reflection is supported but most functionality exists in C++20 too, with the compiler floor stated as gcc 11+, clang 14+ and MSVC 17+.
Frequently asked questions
What is reflect-cpp and what is it comparable to?
It is a C++20 and C++26 library for fast serialization, deserialization and validation using reflection. The README positions it as the C++ counterpart to pydantic in Python, serde in Rust, the encoding package in Go and aeson in Haskell.
Which serialization formats does reflect-cpp support out of the box?
JSON, backed by yyjson at version 0.8.0 or later, which the README marks as included in the repository. Command line arguments and environment variables are also implemented natively with no underlying library. Every other format requires its own third-party library.
What compilers does reflect-cpp support?
The README badges state gcc 11+, clang 14+ and MSVC 17+. The library targets C++-20 and C++-26, with C++26 reflection supported and most functionality also available in C++-20 except where noted otherwise.
Do I need C++26 to use reflect-cpp?
No. The README states reflect-cpp supports C++-26 reflection but most of the functionality is also available in C++-20, except where explicitly noted otherwise in the documentation.
How do I add my own serialization format to reflect-cpp?
The library is deliberately modular and built on concepts, so a new format is a type satisfying the relevant constraint rather than a class to subclass. The README points to the documentation page on supporting your own format.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/getml-reflect-cpp)Star history chart for your README
[](https://hysenlabs.com/projects/getml-reflect-cpp)Was this useful?