Object Reflection

The basic concept behind this technology is the ability to know rather arbitrary aspects of an object's type at runtime, long after the compiler has thrown that information away. Other object oriented systems (for example Objective C) use dynamic type binding in the form of an isa pointer that points to a compiler generated object representing the class of that object. This technology can also referred to as class description, as one only needs to generate a description of the object's class, then ensure the object is bound to that description, hence the name classdesc[3].

In C++, it is not necessary to incur the overhead of an isa pointer, as one can bind an object's type to an overloaded instance of a function call at compile time.

A descriptor is a template function D

template <class T>
void D(D_t&  t, const classdesc::string& d, T& a);

The D t argument allows for state to be maintained while the descriptor is recursively applied to the data structure. If state is not needed for the descriptor, a “null” object should be provided for passing through.

Users can specialise the descriptor to handle different types. The classdesc descriptor, however does not specialise the descriptor directly, but rather specialises a functor template:

template <class T> struct access_D
{
   void operator()(D_t&, const string&, T&);
};
There are two advantages of using functional classes:
  1. Partial specialisation is available for template classes, but not template functions
  2. It is simpler to specify friend access to a functional class for those descriptors needing to access private or protected members
The generic descriptor then calls operator() of the appropriate access class, eg:
template <class T>
void pack(classdesc::pack_t& t, const classdesc::string& d, T& a)
{classdesc::access_pack<T>()(t,d,a);}
but may optionally perform some additional pre/post-processing if sensible for the particular descriptor.

The classdesc package comes with several descriptors already implemented:

pack/unpack
, which implements serialisation
xml_pack/xml_unpack
, which serialises to/from an XML description of the object.
json_pack/json_unpack
, which serialises to/from a JSON description of the object.
dump
, which writes an ascii representation of the object to a std::ostream object
javaClass
, which generates a Java interface to C++ objects using JNI.
pack will be documented in more detail later, but a simple overview is that the pack_t object is a simple reference to some binary data:
struct pack_t
{
  char *data();
  const char *data() const;
  size_t size();
} buf;
A call to pack(buf,""",foo) pushes a binary representation of the object foo (regardless of its type) into buf. The inverse operation is called unpack. Syntactically, we may also use the << operator for the same purpose:
buf << foo << bar;
fwrite(buf.data(),buf.size(),1,f);
pack_t b1(buf.size());
fread(b1.data(),b1.size(),1,f);
b1 >> foo1 >> bar1;
This code has made a copy of foo and bar, but with the data going via a disk file.



Subsections