The idea is a special Reflection template generates typelist about the
properties & methods of the given template parameter. This makes
adding reflection unobtrusive to the reflected class. The most
pressing issue is how to access properties (and invoke methods). I
accomplish this by specializing an Accessor template for the given
class and the (largely) arbitrary property index/id. These accessor
classes are implemented here using macros, and is what would need to
be automated. (The code presented is non-standard as it specializes a
nested class, but this implementation detail is unimportant as it
could be emulated any number of ways.)
The last points of interest are the new Loki typelist iterator
constructs (so new they aren't in the CVS repository quite yet).
These two templates form a binding between the compile-time world and
the run-time world. The first one, IterateTypes, works with an
insertion iterator to generate & store data about the types in the
typelist. The second, StreamTypes, streams data into a given stream
object. I am convinced StreamTypes would allow the complete
automation of (de)serialization IFF there were a mechanism to generate
typelist & accessors for the properties.
Special cases (e.g. raw pointers) would need some extra handling. By
default it should be assumed that they point to one item, but there
needs to be a mechanism to override this and bind the size to a
functor (which could be a reflected accessor) which can determine the
number of elements.
This is the output of the program. It will generate the corresponding
output for any class that has a well-defined Reflection<Class>
template.
[output]
class std::vector<int,class std::allocator<int> >, class
std::vector<double,class std::allocator<double> >
3: 42, 3012, 110
2: 3.14159, 2.71828
Press any key to continue . . .
[/output]
[source]
#include <cassert>
#include <vector>
#include <typeinfo>
#include <iostream>
#include <Loki\TypeList.h>
#include <Loki\DataGenerator.h>
#define ACCESSORS \
template<class Class, int Index> \
struct _Accessor;
#define ACCESSOR(CLASS, TYPE, PROP_NAME, INDEX) \
template<>\
struct _Accessor<CLASS, INDEX>\
{\
typedef TYPE type;\
static type& extract(CLASS* obj)\
{\
return obj->PROP_NAME;\
}\
};\
typedef _Accessor<CLASS, INDEX> _Accessor##INDEX;
template<typename T>
struct Reflection;
struct TestCase1
{
std::vector<int> integers;
std::vector<double> reals;
};
template<>
struct Reflection<TestCase1>
{
struct Public
{
ACCESSORS;
ACCESSOR(TestCase1, std::vector<int>, integers, 0);
ACCESSOR(TestCase1, std::vector<double>, reals, 1);
typedef Loki::TL::MakeTypeList<_Accessor0, _Accessor1>::Result
Properties;
};
};
//Return the name (char*) given a type
template<typename Property>
struct property_type_name
{
const char* operator()()
{
return typeid(Property::type).name();
}
};
//Extract & return the property from the class
template<typename Property>
struct extract_property
{
template<class Class>
Property::type& operator()(Class* obj)
{
return Property::extract(obj);
}
};
//I hope this is self-explanitory...
template<typename FI, typename Stream>
Stream& write_csv(FI begin, FI end, Stream& s)
{
std::copy(begin, end-1, std::ostream_iterator<typename
FI::value_type>(s, ", "));
s << *(end-1);
return s;
}
template<class OS, typename T>
OS& operator<<(OS& os, std::vector<T>& v)
{
os << (int)v.size() <<": ";
write_csv(v.begin(), v.end(), os);
os << std::endl;
return os;
}
int main(int argc, char* argv[])
{
//Create the unwitting object
TestCase1 testcase1;
//Magic happens here
typedef Reflection<TestCase1>::Public::Properties props;
std::vector<const char*> names;
//Iterate types in the 'props' Typelist and put their
// names in a vector
Loki::TL::IterateTypes<props, property_type_name> gen;
gen(std::back_inserter(names));
//Write that vector out cout
write_csv(names.begin(), names.end(), std::cout);
std::cout << std::endl;
std::cout << std::endl;
//Put some data into testcase1
testcase1.integers.push_back(42);
testcase1.integers.push_back(3012);
testcase1.integers.push_back(110);
testcase1.reals.push_back(3.14159265358979);
testcase1.reals.push_back(2.71828182818281828);
//Stream the types in the 'props' Typelist through
// extract_property and send the results to std::cout
Loki::TL::StreamTypes<props, extract_property> stream_gen;
stream_gen(std::cout, &testcase1);
std::cout << std::endl;
system("pause");
return 0;
}
[/source]
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
Did you have a look at open- c++?
http://www.csg.is.titech.ac.jp/~chiba/openc++.html
The project started with some type of runtime reflection long time ago. Currently
it offers a C++ preprocesor that allows compiletime reflection, which in turn
allows to add many extensions to the language, including runtime reflection. It
could at least solve the problem of automating the generation of reflection- data.
Ole
What do you think of this Reflection package for C++ (below) ?
http://www.garret.ru/~knizhnik/cppreflection/docs/reflect.html
Regards,
Andrew Marlow.
I have briefly looked at OpenC++ before - I think I will perfect the
technique, then look into using OpenC++ to generate the reflection
templates.
The various limitations of each technique are imposing, and I am
primarily interested in developing compile-time reflection - which
would relieve the imposing limitations for those various techniques.