19

Most mature C++ projects seem to have an own reflection and attribute system, i.e for defining attributes which can be accessed by string and are automatically serializable. At least many C++ projects I participated in seemed to reinvent the wheel.

Do you know any good open source libraries for C++ which support reflection and attribute containers, specifically:

  • Defining RTTI and attributes via macros
  • Accessing RTTI and attributes via code
  • Automatic serialisation of attributes
  • Listening to attribute modifications (e.g. OnValueChanged)
Fabian
  • 1,824
  • 3
  • 19
  • 35

8 Answers8

13

There is a new project providing reflection in C++ using a totally different approach: CAMP. https://github.com/tegesoft/camp

CAMP doesn't use a precompiler, the classes/properties/functions/... are declared manually using a syntax similar to boost.python or luabind. Of course, people can use a precompiler like gccxml or open-c++ to generate this declaration if they prefer.

It's based on pure C++ and boost headers only, and thanks to the power of template meta-programming it supports any kind of bindable entity (inheritance and strange constructors are not a problem, for example).

It is distributed under the MIT licence (previously LGPL).

Nick
  • 27,566
  • 12
  • 60
  • 72
Laurent
  • 131
  • 1
  • 2
  • 3
    This has been forked and [continued as Ponder](http://billyquith.github.io/ponder/) – Nick May 18 '16 at 18:41
11

This is what you get when C++ meets Reflection:

C++ meets Reflection

Whatever you choose, it'll probably have horrible macros, hard to debug code or weird build steps. I've seen one system automatically generate the serialisation code from DevStudio's PDB file.

Seriously though, for small projects, it'll be easier to write save/load functions (or use streaming operators). In fact, that might hold for big projects too - it's obvious what's going on and you'd usually need to change code anyway if the structure changes.

Skizz
  • 69,698
  • 10
  • 71
  • 108
  • 1
    Unless my app already uses Qt, I create streaming operators for each of my classes that need to be saved. – Branan Sep 17 '08 at 23:17
  • +1 : for the photo ;) kidding. I agree with the conclusion. Use handcraft serialisers or use another langage :) – neuro Jun 15 '10 at 08:25
  • 7
    The wagon lettering has a typo, should be l337! – sehe Sep 06 '12 at 09:29
6

You could have a look at the two tools below. I've never used either of them, so I can't tell you how (im)practical they are.

XRTTI:

Xrtti is a tool and accompanying C++ library which extends the standard runtime type system of C++ to provide a much richer set of reflection information about classes and methods to manipulate these classes and their members.

OpenC++:

OpenC++ is C++ frontend library (lexer+parser+DOM/MOP) and source-to-source translator. OpenC++ enables development of C++ language tools, extensions, domain specific compiler optimizations and runtime metaobject protocols.

Carl Seleborg
  • 13,125
  • 11
  • 58
  • 70
4

I looked at these things for quite a while but they tend to be very heavy-handed. They might prevent you from using inheritance, or having strange constructors etc etc. In the end they ended up being too much of a burden instead of a convenience.

This approach for exposing members that I now use is quite lightweight and lets you explore a class for serialization or setting all fields called "x" to 0, for example. It's also statically determined so is very very fast. No layers of library code or code-gen to worry about messing with the build process. It generalises to hierarchies of nested types.

Set your editor up with some macros to automate writing some of these things.

struct point
{
     int x;
     int y;

     // add this to your classes
     template <typename Visitor>
     void visit(Visitor v)
     {
         v->visit(x, "x"); 
         v->visit(y, "y");
     }
};


/** Outputs any type to standard output in key=value format */
struct stdout_visitor
{
     template <typename T>
     void visit(const T& rhs)
     {
         rhs.visit(this);
     }

     template <typename Scalar>
     void visit (const Scalar& s, const char* name)
     {
          std::cout << name << " = " << s << " ";
     }
}
  • While i like it, it should be noted that it isn't a general solution - e.g. it breaks as soon as the actual static type isn't known. – Georg Fritzsche Mar 21 '10 at 12:50
  • That is a good approach. I once made a preprocessor that generates `visit()` function templates for you. And then a few functors to serialize and deserialize JSON, binary, text, etc... No ugly macros. https://groups.google.com/d/msg/comp.lang.c++/Ila1Tn09mm4/nJVxl3SzpFUJ – Maxim Egorushkin Sep 06 '12 at 08:25
3

Looked at this for a while too. The current easiest solution seems to be BOOST_FUSION_ADAPT_STRUCT. Practically once you have a library/header you only need to add your struct fields into the BOOST_FUSION_ADAPT_STRUCT() macro, as the last segment of the code shows. Yes it has restrictions many other people have mentioned. And it does not support listeners directly.

The other promising solutions I looked into are

  • CAMP and XRTTI/gccxml, however both seem to be a hurdle to bring external tools dependency into your project.
  • Years ago I used perl c2ph/pstruct to dump the meta info from the output of gcc -gstabs, that is less intrusive but needs more work though it worked perfectly for me.

Regarding the boost/__cxa approach, once you figure out all the small details, adding/changing structs or fields is simple to maintain. we currently use it to build a custom types binding layer on top of dbus, to serialize the API and hide the transport/RPC details for a managed object service subsystem.

Community
  • 1
  • 1
minghua
  • 5,981
  • 6
  • 45
  • 71
  • Does this enable /reflection/? I can see how this adapts a struct to model the Sequence concept of Boost MPL/Fusion, so you can iterate over it's fields. However, getting the names of fields or using the names to access fields is out of the question, IIRC. That's not even speaking about the struct type itself – sehe Sep 06 '12 at 09:37
  • @sehe: As you see the field types are printed by __cxa_demangle(typeid(t).name()). The same function can be applied to struct decoder and array decoder to print all struct types and array types, including the very top level. If you think java vm keeps a list of all classes/objects in an internal binary list, the meta data this example dumps is roughly at that level. From that low level information (plus runtime address information), you need to write code to access fields (by names in string or char[] format). That would be the other half of the whole reflection picture. – minghua Sep 07 '12 at 07:13
  • Ah. So, the most important portion of the answer isn't actually there. Hmmm that was surprising. Anyways, I'll **+1 for `c2ph/pstruct`**. Oldschool, but will work in most places, really. __cxa* isn't exactly portable. And ADAPT_STRUCT is both not very relevant, easy to work with, and not easily automated. If you're using macros, you might as well use macros the full way (and not depend on Boost or GCC ABI) – sehe Sep 07 '12 at 07:23
  • Actually the most important part for me is least runtime cost and least intrusive process. c2ph/pstruct is not very portable as it may appear to be. I had to spend a large effort to fix pstruct for the specific gcc stabs dump. After dumping the type info and retrieve the address info from the built binary, only a memory access interface is needed to be built into the runtime. Everything else can be done out of the box (I do embedded programming, and use reflection for mainly testing). Thus this ADAPT_STRUCT is the easiest solution so far I can find. – minghua Sep 07 '12 at 07:45
  • Just re-red sehe's last comment: You do two rounds. The first round compile/run on a host machine to use __cxa*/boost to dump the meta info. The 2nd round to build the meta info in the format you define into the target. Then in the target build there is no dependency on boost or g++. – minghua Dec 31 '14 at 17:26
3

This is a notorious weakness of the C++ language in general because the things that would need to be standardized to make reflection implementations portable and worthwhile aren't standard. Calling conventions, object layouts, and symbol mangling come to mind, but there are others as well.

The lack of direction from the standard means that compiler implementers will do some things differently, which means that very few people have the motivation to write a portable reflection library, which means that people who need reflection re-invent the wheel, but only just enough for what they need. This happens ad infinitum, and here we are.

Ben Collins
  • 20,538
  • 18
  • 127
  • 187
2

Not a general one but QT supports this via a meta compiler, and is GPL. My understanding from talking to the QT people was that this isn't possible with pure C++, hence the need for the moc.

Martin Beckett
  • 94,801
  • 28
  • 188
  • 263
0

Automatic introspection/reflection toolkit. Use meta compiler like Qt's and adding meta information directly into object files. Intuitive easy to use. No external dependencies. Even allow automatically reflect std::string and then use it in scripts. Please visit IDK

Eugene G
  • 11
  • 3