The Crefl API provides runtime access to reflection metadata for C structure declarations with support for arbitrarily nested combinations of: intrinsic, set, enum, struct, union, field (member), array, constant, variable. The Crefl reflection graph database format for portable reflection metadata.
Reflection provides objects (of type Type) that describe assemblies, modules, and types. You can use reflection to dynamically create an instance of a type, bind the type to an existing object, or get the type from an existing object and invoke its methods or access its fields and properties.
The get/set functions appear in this list as regular methods. Reflection is supported through the common language runtime, not by the Microsoft C++ compiler. Although you used this code to inspect an assembly that you defined, you can also use this code to inspect .
Reflection is the process of describing the metadata of types, methods and fields in a code. The namespace System.Reflection enables you to obtain data about the loaded assemblies, the elements within them like classes, methods and value types. Some of the commonly used classes of System.Reflection are: Class.
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.
There are two kinds of reflection
swimming around.
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
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.
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.
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:
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:
http://www.donw.org/rfl/
https://bitbucket.org/dwilliamson/clreflect
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:
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:
http://cppnow.org/history/2019/talks/
https://www.youtube.com/watch?v=VMuML6vLSus&feature=youtu.be
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:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With