How can I add reflection to a C++ application?

244,067

Solution 1

Ponder is a C++ reflection library, in answer to this question. I considered the options and decided to make my own since I couldn't find one that ticked all my boxes.

Although there are great answers to this question, I don't want to use tonnes of macros, or rely on Boost. Boost is a great library, but there are lots of small bespoke C++0x projects out that are simpler and have faster compile times. There are also advantages to being able to decorate a class externally, like wrapping a C++ library that doesn't (yet?) support C++11. It is fork of CAMP, using C++11, that no longer requires Boost.

Solution 2

What you need to do is have the preprocessor generate reflection data about the fields. This data can be stored as nested classes.

First, to make it easier and cleaner to write it in the preprocessor we will use typed expression. A typed expression is just an expression that puts the type in parenthesis. So instead of writing int x you will write (int) x. Here are some handy macros to help with typed expressions:

#define REM(...) __VA_ARGS__
#define EAT(...)
// Retrieve the type
#define TYPEOF(x) DETAIL_TYPEOF(DETAIL_TYPEOF_PROBE x,)
#define DETAIL_TYPEOF(...) DETAIL_TYPEOF_HEAD(__VA_ARGS__)
#define DETAIL_TYPEOF_HEAD(x, ...) REM x
#define DETAIL_TYPEOF_PROBE(...) (__VA_ARGS__),
// Strip off the type
#define STRIP(x) EAT x
// Show the type without parenthesis
#define PAIR(x) REM x

Next, we define a REFLECTABLE macro to generate the data about each field(plus the field itself). This macro will be called like this:

REFLECTABLE
(
    (const char *) name,
    (int) age
)

So using Boost.PP we iterate over each argument and generate the data like this:

// A helper metafunction for adding const to a type
template<class M, class T>
struct make_const
{
    typedef T type;
};
template<class M, class T>
struct make_const<const M, T>
{
    typedef typename boost::add_const<T>::type type;
};
#define REFLECTABLE(...) \
static const int fields_n = BOOST_PP_VARIADIC_SIZE(__VA_ARGS__); \
friend struct reflector; \
template<int N, class Self> \
struct field_data {}; \
BOOST_PP_SEQ_FOR_EACH_I(REFLECT_EACH, data, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))
#define REFLECT_EACH(r, data, i, x) \
PAIR(x); \
template<class Self> \
struct field_data<i, Self> \
{ \
    Self & self; \
    field_data(Self & self) : self(self) {} \
    \
    typename make_const<Self, TYPEOF(x)>::type & get() \
    { \
        return self.STRIP(x); \
    }\
    typename boost::add_const<TYPEOF(x)>::type & get() const \
    { \
        return self.STRIP(x); \
    }\
    const char * name() const \
    {\
        return BOOST_PP_STRINGIZE(STRIP(x)); \
    } \
}; \

What this does is generate a constant fields_n that is number of reflectable fields in the class. Then it specializes the field_data for each field. It also friends the reflector class, this is so it can access the fields even when they are private:

struct reflector
{
    //Get field_data at index N
    template<int N, class T>
    static typename T::template field_data<N, T> get_field_data(T& x)
    {
        return typename T::template field_data<N, T>(x);
    }
    // Get the number of fields
    template<class T>
    struct fields
    {
        static const int n = T::fields_n;
    };
};

Now to iterate over the fields we use the visitor pattern. We create an MPL range from 0 to the number of fields, and access the field data at that index. Then it passes the field data on to the user-provided visitor:

struct field_visitor
{
    template<class C, class Visitor, class I>
    void operator()(C& c, Visitor v, I)
    {
        v(reflector::get_field_data<I::value>(c));
    }
};
template<class C, class Visitor>
void visit_each(C & c, Visitor v)
{
    typedef boost::mpl::range_c<int,0,reflector::fields<C>::n> range;
    boost::mpl::for_each<range>(boost::bind<void>(field_visitor(), boost::ref(c), v, _1));
}

Now for the moment of truth we put it all together. Here is how we can define a Person class that is reflectable:

struct Person
{
    Person(const char *name, int age)
        :
        name(name),
        age(age)
    {
    }
private:
    REFLECTABLE
    (
        (const char *) name,
        (int) age
    )
};

Here is a generalized print_fields function using the reflection data to iterate over the fields:

struct print_visitor
{
    template<class FieldData>
    void operator()(FieldData f)
    {
        std::cout << f.name() << "=" << f.get() << std::endl;
    }
};
template<class T>
void print_fields(T & x)
{
    visit_each(x, print_visitor());
}

An example of using the print_fields with the reflectable Person class:

int main()
{
    Person p("Tom", 82);
    print_fields(p);
    return 0;
}

Which outputs:

name=Tom
age=82

And voila, we have just implemented reflection in C++, in under 100 lines of code.

Solution 3

There are two kinds of reflection swimming around.

  1. Inspection by iterating over members of a type, enumerating its methods and so on.

    This is not possible with C++.
  2. Inspection by checking whether a class-type (class, struct, union) has a method or nested type, is derived from another particular type.

    This kind of thing is possible with C++ using template-tricks. Use boost::type_traits for many things (like checking whether a type is integral). For checking for the existance of a member function, use Is it possible to write a template to check for a function's existence? . For checking whether a certain nested type exists, use plain SFINAE .

If you are rather looking for ways to accomplish 1), like looking how many methods a class has, or like getting the string representation of a class id, then i'm afraid there is no Standard C++ way of doing this. You have to use either

  • A Meta Compiler like the Qt Meta Object Compiler which translates your code adding additional meta informations.
  • A Framework constisting of macros that allow you to add the required meta-informations. You would need to tell the framework all methods, the class-names, base-classes and everything it needs.

C++ is made with speed in mind. If you want high-level inspection, like C# or Java has, then I'm afraid i have to tell you there is no way without some effort.

Solution 4

And I would love a pony, but ponies aren't free. :-p

http://en.wikibooks.org/wiki/C%2B%2B_Programming/RTTI is what you're going to get. Reflection like you're thinking about -- fully descriptive metadata available at runtime -- just doesn't exist for C++ by default.

Solution 5

Reflection is not supported by C++ out of the box. This is sad because it makes defensive testing a pain.

There are several approaches to doing reflection:

  1. use the debug information (non portable).
  2. Sprinkle your code with macro's/templates or some other source approach (looks ugly)
  3. Modify a compiler such as clang/gcc to produce a database.
  4. Use Qt moc approach
  5. Boost Reflect
  6. Precise and Flat Reflection

The first link looks the most promising (uses mod's to clang), the second discusses a number of techniques, the third is a different approach using gcc:

  1. http://www.donw.org/rfl/

  2. https://bitbucket.org/dwilliamson/clreflect

  3. https://root.cern.ch/how/how-use-reflex

There is now a working group for C++ reflection. See the news for C++14 @ CERN:

Edit 13/08/17:

Since the original post there have been a number of potential advancements on the reflection. The following provides more detail and a discussion on the various techniques and status:

  1. Static Reflection in a Nutshell
  2. Static Reflection
  3. A design for static reflection

However it does not look promising on a standardised reflections approach in C++ in the near future unless there is a lot more interest from the community in support for reflection in C++.

The following details the current status based on feedback from the last C++ standards meeting:

Edit 13/12/2017

Reflection looks to be moving towards C++ 20 or more probably a TSR. Movement is however slow.

Edit 15/09/2018

A draft TS has been sent out to the national bodies for ballot.

The text can be found here: https://github.com/cplusplus/reflection-ts

Edit 11/07/2019

The reflection TS is feature complete and is out for comment and vote over the summer (2019).

The meta-template programing approach is to be replaced with a simplier compile time code approach (not reflected in the TS).

Edit 10/02/2020

There is a request to support the reflection TS in Visual Studio here:

Talk on the TS by the author David Sankel:

Edit 17 March 2020

Progress on reflection is being made. A report from '2020-02 Prague ISO C++ Committee Trip Report' can be found here:

Details on what is being considered for C++23 can be found here (includes short section on Reflection):

Edit 4th June 2020

A new framework has been released by Jeff Preshing called 'Plywood' that contains a mechanism for runtime reflection. More details can be found here:

The tools and approach look to be the most polished and easiest to use so far.

Edit July 12 2020

Clang experimental reflection fork : https://github.com/lock3/meta/wiki

Interesting reflection library that uses clang tooling library to extract information for simple reflection with no need to add macro's: https://github.com/chakaz/reflang

Edit Feb 24 2021

Some additional clang tooling approaches:

Edit Aug 25 2021

An ACCU talk online at youtube https://www.youtube.com/watch?v=60ECEc-URP8 is well worth a listen too it talks about current proposals to the standard and an implementation based on clang.

See:

Share:
244,067

Related videos on Youtube

Nick
Author by

Nick

I'm a video game maker. I like shrubberies.

Updated on November 29, 2021

Comments

  • Nick
    Nick 11 months

    I'd like to be able to introspect a C++ class for its name, contents (i.e. members and their types) etc. I'm talking native C++ here, not managed C++, which has reflection. I realise C++ supplies some limited information using RTTI. Which additional libraries (or other techniques) could supply this information?

    • jalf
      jalf almost 14 years
      Tough luck, you can't do it without macros and other preprocessing, because the required metadata does not exist unless you manually create it through some macro preprocessing magic.
    • Joseph Garvin
      Joseph Garvin over 13 years
      The information you can get back from RTTI isn't enough to do most of the things you'd actually want reflection for though. You can't iterate over the member functions of a class for example.
  • Aaron
    Aaron about 14 years
    I second Brad. C++ templates can be rather powerful, and there is a wealth of experience around various 'reflection' type behaviors, such at boost 'any' library, type traits, C++ RTTI etc. that can solve many of the problems reflection is solved for. So Nick, what's your goal here?
  • Franci Penov
    Franci Penov almost 14 years
    Upvote for the ponies remark! I'd upvote twice, as your answer also deserves it, but sadly I get only one, so ponies win. :-)
  • Nick
    Nick over 13 years
    I don't really get why this is a clever response. I've already said I'd like references to libraries etc to implement this. The reflection/introspection is for various system to allow script access, serialisation etc.
  • Joseph Garvin
    Joseph Garvin over 13 years
    C++ is made with speed in mind, but the philosophy isn't "as fast as possible," instead it's, "you don't pay for it if you don't use it." I believe it's possible for a language to implement introspection in a way that fits with that philosophy, C++ just lacks it.
  • jalf
    jalf about 13 years
    @Nick: He already answered that. It can't be done, the data does not exist, and therefore, no library is able to implement it for you.
  • jalf
    jalf about 13 years
    @Joseph: How should that be done? It'd require all that metadata to be stored. Which means you have to pay for it, even if you don't use it. (Unless you could mark individual types as "supporting reflection", but then we're almost down where we might as well use the existing macro trickery.
  • MSalters
    MSalters about 13 years
    @jalf: Only the metadata which might be needed. If we consider only compile-time reflection, this is trivial. E.g. a compile-time function members<T> which returns a list of all members of T. If we wanted have runtime reflection (ie RTTI mixed with reflection), the compiler would still know all reflected base types. It's quite likely members<T>(T&) would never be instantiated for T=std::string, so the RTTI for std::string or its derived classes need not be included.
  • Joseph Lisee
    Joseph Lisee over 11 years
    The reflex library (mentioned below) adds reflection to C++ without slowing down existing code at: root.cern.ch/drupal/content/reflex
  • mmmmmmmm
    mmmmmmmm over 10 years
    @Joe: Reflection never slows down existing code. It just makes the delivered stuff bigger (since you have to deliver a type info database...).
  • Sqeaky
    Sqeaky over 10 years
    That is crafty and a giant hack, with the DIA SDK thing you suggested there.
  • Matthieu M.
    Matthieu M. about 10 years
    by minghua (who originally edited the post): I dug into this BOOST_FUSION_ADAPT_STRUCT solution and eventually came up with an example. See this newer SO question - C++ iterate into nested struct field with boost fusion adapt_struct.
  • minghua
    minghua about 10 years
    Great, Matthieu! Just realized having seen your hints here and there over the course of the past year. Did not notice they are related till now. Those were very inspiring.
  • fearless_fool
    fearless_fool over 8 years
    Kudos for showing how to implement reflection, rather than saying it can't be done. It's answers like this that make S.O. a great resource.
  • Phenglei Kai
    Phenglei Kai almost 8 years
    Note that if you try to compile this under Visual Studio you will get an error because VS doesn't handle the variadic macro expansion properly. For VS, try adding: #define DETAIL_TYPEOF_INT2(tuple) DETAIL_TYPEOF_HEAD tuple and #define DETAIL_TYPEOF_INT(...) DETAIL_TYPEOF_INT2((__VA_ARGS__)) and changing the definition of TYPEOF(x) to: #define TYPEOF(x) DETAIL_TYPEOF_INT(DETAIL_TYPEOF_PROBE x,)
  • Ira Baxter
    Ira Baxter over 7 years
    Everything has a cost. Even if you don't access metadata, you pay runtime memory space for it. Some worlds don't have that luxury.
  • JPHarford
    JPHarford over 7 years
    Iterating over members is not impossible in C++. It's just inconvenient, and not typically worth the effort. Consider when a table is built with type_traits data and sample objects together in a heterogeneous container, to enable lookups used to reverse type erasure. It's a LOT of boilerplate, and most things that can be done with it are better accomplished in other ways.
  • paulm
    paulm over 7 years
    gotta love these functions that always return true ;) I assume this is immune from static init ordering issues?
  • Ankit Zalani
    Ankit Zalani almost 7 years
    I m getting the error 'BOOST_PP_IIF_0' does not name a type. Can you please help.
  • Admin
    Admin almost 7 years
    The cern link is broken.
  • Orwellophile
    Orwellophile about 5 years
    I think you mean __declspec(dllexport) and you can retrieve the information from a the .map file if you enable creation of such during build.
  • Freddx L.
    Freddx L. over 2 years
    @jalf Still strange for me read people in the programming world saying thinks like 'its not possible' and not 'I don't know how'. Sure the metadata don't exists but can be inserted with macros
  • TarmoPikaro
    TarmoPikaro over 1 year
    See my own answer - stackoverflow.com/a/55364085/2338477 I've extracted and repacked all defines, and boost library is not needed. As demo code i'm providing serialization to xml and restore from xml. (Thanks for correction @stackprotector)

Related