A Month of Writing Reflections-based Code: What have I learned?
## Preface
I have been trying to automate writing my own pybind11 binding code with the help of C++26 reflections, as implemented by [clang-p2996](https://github.com/bloomberg/clang-p2996).
There were moments where things went smoothly, but also moments where I missed a feature or two from the world of reflections. Then there is also accidental complexity caused by pybind11 having features which are, at the very least, not friendly for generic binding generation.
Before I begin, a massive thanks to Barry Revzin, Daveed Vandevoorde, Dan Katz, Adam Lach and whoever else worked on bringing Reflections to C++.
## Smooth sailing
What we got from the set of reflections papers is awesome. Here's an example of what can be achieved quite easily:
https://godbolt.org/z/jaxT8Ebjf
With some 20 lines of reflections, we can generate bindings that cover:
- free functions (though not overload sets of free functions - more on that later)
- structs/classes with
- a default constructor
- member functions
- data members, though always writable from python
You can also see how this easily generalizes to all other kinds of `py_class.def_meow(...)`. Almost...
Since C++ does not have "properties" in the python sense, `def_property_meow` will need [special care](https://godbolt.org/z/MT5ojzG4n).
As the `def_property` example shows, customizing the generated bindings is possible with `[[=annotations]]`.
So far... this is AWESOME. Looks like we can make bindings for whatever C++ entity we fine.
Well, let's talk about the not so awesome parts of this adventure. In order from least troublesome to most troublesome
## Splicing ranges
Pybind11 likes to work with template parameter packs, but C++26 often leaves us with `std::vector<std::meta::info>`.
We can deal with this in multiple ways:
Options are:
- good old [`index_sequece`](https://godbolt.org/z/5vEM7dWhr)
- [structured bindings can introduce a pack](https://godbolt.org/z/GooGTsWdc)
- But constexpr structured bindings have not been implemented yet in clang-p2996
And one thing that didn't end up in [P2996](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2996r13.html#range-splicers) are [range splicers](https://godbolt.org/z/TPWqv3jss).
So this can be done. Depending on the context, it can even look elegant, but I often missed costexpr structured bindings and ended up reaching for `index_sequence` a lot.
Range splicers would have been nice, but I can live without them.
## Code duplication due to pybind11 design
Pybind11 has a lot of similar functions with different names:
`def` vs `def_static` vs `def_property` vs `def_property_readonly` vs ...
Then there are also things whose mere presence alters what pybind11 is doing, without a no-op state:
`is_final` for classes, `arithmetic` for enums and so on.
These can be handled with an `if constexpr` that branches on existence of annotation, however, this leads to a lot of code duplication.
Here, token sequences as described in https://wg21.link/P3294 would remove most of repetition. For the `def_meow` stuff, an approximate reduction in amount of code is ~10x.
## Pure virtual bases
To use these with pybind11, users need to write ["trampolines"](https://godbolt.org/z/ffPKE7Gxb), because it needs to be able to instantiate a python object representing the base class object.
C++26 still can't generate types that have member function, but this will be solved with https://wg21.link/P3294
## Templates can't be annotated
It would be useful to annotate member function templates with something like
template_inputs({
{.name = "T1Func", .args = {^^T1}},
{.name = "T2T3Func", args = {^^T2, ^^T3}}
})
And then bind the same template multiple times, under different names and with different template arguments. However [that's not possible right now](https://godbolt.org/z/qx5c1v417). Can templates even have attributes and annotations?
## Function parameter missing features
**Parameter annotations can
Post #23960
15