62++C (C++26 Reflection)
Preamble
I've always been better at the 'doing' rather than the explaining, so as part of my “what can we do with C++26” exercise I thought this would be a good opportunity to attempt to practice the 'explain it' bit. (In other words this might not be great! let me know if I can improve or change this)
C++26 has now introduced reflection capabilities (https://learnmoderncpp.com/2025/07/31/reflection-in-c26-p2996/) as a standard language feature, previously this is something you'd need to build yourself using various template and macro trickery.
I find the only way I can learn how to use new features is to actually try to use them to do something useful. So my plan was - can I use the new C++26 reflection features to create a dynamic language binding system, and if so, can I do so step by step to make it easy/obvious how it could be improved or extended rather than just write a completed final "ta da" here's a C++ reflection system.
Since this is my first attempt to do something practical with these new features then undoubtably there are probably better ways to write some of this or refactor parts to be clearer or simpler. Assume that I did so intentionally to provide opporunities to learn and make improvements. 🤣 Similarly, again for simplicity, very little attention has been given to thread safety or error handling in this code. at all.
Given the above, this may not be the best way to do any of this, caveat developer etc. Feel free to let me know about improvements, problems or better solutions.
TL;DR
So the plan is to start using C++26 to add library reflection, introspection and invocation to a C++ library for simple basic pod types, with a dynamic python binding, then extend it with more complex types before enhancing it to work on C++20 before adding complex features such as multi process IPC calling, and finally a Java binding.
Problem
Enough preamble, what is this, and what's the idea? When writing C++ libraries it's frequently desirable to be able to use those from other languages - usually the solution is to wire up individual specific implementations per language, e.g. compiling your library with a SWIG wrapper or a Pybind hook. For one off connections this is, and can be fine, but when you start building a large library that is integrated into many systems in multiple languages it gets to become a burden. (e.g. with multiple python versions needing separate pybind builds) Another option is to instead provide a "lowest common denominator" API that abstracts away the library complexity into simpler helper routines (often as just a raw C API) then the bindings are simple one to one calls to the API. The compromise then is needing to redesign a rich C++ API and object model down to single calls that pass state and references via a handle mechanism.
The idea
Whilst it is possible to build up an introspection system using C++, which has become easier as newer template techniques have come along, C++26 has now at last added reflection abilities as native language features allowing the interrogation of types, functions and structures at runtime (well compile time).
So the plan is/was, can we use these new features to construct a metadata layer that will allow us to build native language bindings dynamically. If we have a rich enough metadata interface we should be able to extract and generate bindings to python from the python side with no changes or python dependencies in our C++ libraries at all - and similarly for java, albeit from a java built jar using JNI.
Step by step
This guide takes 9* steps: (*it was 3 then 7 then 9 as I 'simplified' it, pray I don't simplify if further)
Each step adds or improves a small feature, the text for each is just highlighting a few details, the best way to work through is to build and run each one, reading through the code with each StepX.md file alongside. Similarly reading the text alongside the diff for each step will also mean more than either just reading the text or just reading the code. That's what works for me anyway, YMMV.
- Step 1: Open the PoD bay doors
- Step 2: Ready for complexity
- Step 3: How about some
structure - Step 4: Add a new type (std::chrono date)
- Step 5: Containment (vectors/maps)
- Step 6: Turtles all the way (recursive types)
- Step 7: gnioG kcaB nI emiT (C++20 compatibility)
- Step 8: Not remotely funny (IPC calling)
- Step 9: But what about Java?
All the code steps are in the codeberg repo: xplat-tutorial
Each folder is self contained, within each:
"libdemo" this is an example/demo library to show usage,
"libxplat" this is the library code for the metadata dictionary,
"python\example" - demo usage
"python\xplat" - the python binding module
If you download all steps you can diff the directories to see the changes between each, or look at the git commits on each step which will show the incremental additions.
Conclusion
On the whole I'm pleased with how this has turned out, this isn't perfect or finished, but it was done to exercise the features to see what was possible, how easy, and how powerful they are. If I have time/motivation/reason I might see about extending and tidying up the final state of the code and move it from being purely just an example for how to do this into more of a finished state (error checking, sane assertions, additional types and so on) in it's own codeberb repo: xplat
