Introspectable properties in C++

I'm working on a C++ project on my free time, and there's one thing I miss about this language : introspection.

The case is quite common : I want to access my object's properties dynamically, for example when I need to serialize or deserialize them. Being a great fan of Objective-C, I basically wanted to access my attributes as Objective-C Key Value Coding compliant properties :

object.getValueForKey("key");
object.setValueForKey("key", value);

I also wanted to keep this access method independent from the internal representation of the property (which basically means that I just want to provide a getter and setter method).

Obviously no such mecanism exists in C++, and the goal was to build a generic solution that could be applied to all my objects.

 I started to look at some code online, and kept the basic idea :

  • One class used as a properties container, that will be the superclass of all the classes that I want to be introspectable.
  • One class that will represent the property itself, containing the getter and setter method and with the ability to be cast to and affected from the type of the property.
  • Pass the introspectable class as a template parameter to be able to call the getter and setter methods from the property object.

So I started to write my own implementation, trying to simplify a bit the code. After a while I ended up with basically the same code, with the same problem : you couldn't have a property of a type you didn't handled specifically. You had to add a new value to represent the type to an enum

So I added the type of the property as a template parameter on all the concrete propety classes, and on the valueForKey and setValueForKey methods.

 

 /*

 *  PropertiesUtils.h

 *  chateau

 *

 *  Created by Christian Corsano on 18/10/08.

 *  Copyright 2008 conceptoire.com. All rights reserved.

 *

 */

 

#include <typeinfo>

#include <vector>

#include <map>

#include <string>

 

#ifndef _PROPERTIESUTILS_H_

#define _PROPERTIESUTILS_H_

 

using namespace std;

 

/**

 * \def CallMethod Macro to make increase legibility of method pointer calls

 */

#define CallMethod(object,funcPointer)  ((object)->*(funcPointer))

 

template<class TargetClass> class Introspectable;

 

/**

 * Property

 * An object property.

 * This is meant to be an abstract class that will be subclassed into different kind of properties

 * (Read-Write permission, collection operations)

 * This abstract property does not hold any type (no access to the property).

 * \param TargetClass the class for which the property is  declared

 */

template<class TargetClass> class Property {

public:

/**

* Constructor

* \param name The name used to access this property on the target class

*/

Property(string name){

m_name = name;

}

/**

* "Is a" operator

* \param type A type to compare to the type of this property

* \return True if the type matches, false otherwise

*/

virtual bool is_a(type_info const &type) { return false; };

/**

* Type getter

* \return A pointer to the type_info object of the type of this property

*/

virtual const type_info* type() { return NULL; };

/**

* Name getter

* \return The name of this property

*/

string name(){return m_name;};

/**

* Object setter

* Called when adding the property in the Introspectable object

* \param The actual object holding this property

*/

void object(TargetClass* object){m_object = object;};

protected:

TargetClass* m_object;

string m_name;

};

 

/**

 * \class ReadableProperty

 * A property with read access

 * It virtually inherit from Property so we can combine read and write

 * behaviors in a multiple inherited subclass.

 * \param TargetClass the class for which the property is  declared

 * \param T The type contained in the property

 */

template<class TargetClass, typename T>

class ReadableProperty : virtual public Property<TargetClass> {

public:

/**

* \typedef GetFuncPointer Pointer to a getter method of the Target Class

*/

typedef T (TargetClass::*GetFuncPointer)(void);

/**

* \param name Name of the property

* \param getter Pointer to the getter method for this property

*/

ReadableProperty(string name, GetFuncPointer getter) : Property<TargetClass>(name){

m_getter = getter;

}

/**

* Cast operator to the type of the property

* This operator calls the getter method on the target object

* \return The value of the property

*/

operator const T (){

return CallMethod(this->m_object, m_getter)();

}

/**

* "Is a" operator

* \param type A type to compare to the type of this property

* \return True if the type matches, false otherwise

*/

virtual bool is_a(type_info const &type) {

return (*m_type) == type;

}

/**

* Type getter

* \return A pointer to the type_info object of the type of this property

*/

virtual const type_info* type(){

return m_type;

};

 

private:

GetFuncPointer m_getter;

const type_info* m_type;

};

 

 

/**

 * \class WritableProperty

 * A property with write access

 * It virtually inherit from Property so we can combine read and write

 * behaviors in a multiple inherited subclass.

 * \param TargetClass the class for which the property is  declared

 * \param T The type contained in the property

 */

template<class TargetClass, typename T>

class WritableProperty : virtual public Property<TargetClass> {

public:

/**

* \typedef GetFuncPointer Pointer to a getter method of the Target Class

*/

typedef void (TargetClass::*SetFuncPointer)(T);

/**

* \param name Name of the property

* \param getter Pointer to the getter method for this property

*/

WritableProperty(string name, SetFuncPointer setter) : Property<TargetClass>(name) {

m_setter = setter;

T privateType;

m_type = &typeid(privateType);

}

/**

* Assignement operator for the property type

* This operator calls the getter method on the target object

* \param value The target value for the property

*/

WritableProperty operator = (const T value){

CallMethod(this->m_object, m_setter)(value);

return *this;

}

/**

* "Is a" operator

* \param type A type to compare to the type of this property

* \return True if the type matches, false otherwise

*/

virtual bool is_a(type_info const &type) {

return (*m_type) == type;

}

/**

* Type getter

* \return A pointer to the type_info object of the type of this property

*/

virtual const type_info* type(){

return m_type;

};

private:

SetFuncPointer m_setter;

const type_info* m_type;

};

 

 

/**

 * \class SimpleProperty

 * A property with read and write access

 * \param TargetClass the class for which the property is  declared

 * \param T The type contained in the property

 */

template<class TargetClass, typename T>

class SimpleProperty : public ReadableProperty<TargetClass, T>, public WritableProperty<TargetClass, T> {

public:

typedef T (TargetClass::*GetFuncPointer)(void);

typedef void (TargetClass::*SetFuncPointer)(T);

/**

* \param name Name of the property

* \param getter Pointer to the getter method for this property

* \param setter Pointer to the setter method for this property

*/

SimpleProperty(string name,

  GetFuncPointer getter,

  SetFuncPointer setter) :

ReadableProperty<TargetClass, T>(name, getter),

WritableProperty<TargetClass, T>(name, setter),

Property<TargetClass>(name) {}

/**

* "Is a" operator

* \param type A type to compare to the type of this property

* \return True if the type matches, false otherwise

*/

virtual bool is_a(const type_info &type) { return ReadableProperty<TargetClass,T>::is_a(type); }

 

/**

* Type getter

* \return A pointer to the type_info object of the type of this property

*/

virtual const type_info* type() { return ReadableProperty<TargetClass,T>::type(); }

};

 

/**

 * \class CollectionProperty

 * A property containing several values of a same type.

 * Allow to add values to the property and get the collection

 * \param TargetClass the class for which the property is  declared

 * \param T The type contained in the property

 */

template<class TargetClass, typename T>

class CollectionProperty : public Property<TargetClass> {

public:

/**

* \typedef Pointer to a getter function

*/

typedef vector<T> (TargetClass::*GetFuncPointer)(void);

/**

* \typedef Pointer to a function to add a value to the property

*/

typedef void (TargetClass::*AddFuncPointer)(T);

/**

* \typedef Pointer to a function to remove a given value from the property

*/

typedef void (TargetClass::*RemoveFuncPointer)(T);

/**

* \param name Name of the property

* \param getter Pointer to the getter method for this property

* \param adder Pointer to the add method for this property

*/

CollectionProperty(string name,

GetFuncPointer getter,

AddFuncPointer adder,

RemoveFuncPointer remover) : Property<TargetClass>(name) {

m_getter = getter;

m_adder = adder;

m_remover = remover;

T privateType;

m_type = &typeid(privateType);

}

/**

* "Is a" operator

* \param type A type to compare to the type of this property

* \return True if the type matches, false otherwise

*/

virtual bool is_a(type_info const &type) {

return (*m_type) == type;

}

 

/**

* Type getter

* \return A pointer to the type_info object of the type of this property

*/

virtual const type_info* type(){

return m_type;

};

/**

* Adds a value to the collection

* \param value Value to add to the collection

*/

void add(T value){

CallMethod(this->m_object, m_adder)(value);

}

 

/**

* Removes a value from the collection

* \param value Value to remove from the collection

*/

void remove(T value){

CallMethod(this->m_object, m_remover)(value);

}

/**

* \return The collection of value of this property

*/

vector<T> get(){

return CallMethod(this->m_object, m_getter)();

}

private:

AddFuncPointer m_adder;

RemoveFuncPointer m_remover;

GetFuncPointer m_getter;

const type_info* m_type;

};

 

/**

 * \class Introspectable

 * Super-class for an introspectable object

 */

template<class TargetClass>

class Introspectable {

public:

/**

* Get the type_info for a property

* \param key name of the property

* \return A type_info pointer for the property

*/

type_info* typeForKey(string key) {

Property<TargetClass>* p = m_properties[key];

if (p != NULL){

return p->type();

}

return NULL;

};

/**

* Adds a property to this object

* \param prop The new property

*/

void addProperty(Property<TargetClass>* prop) {

prop->object((TargetClass*) this);

m_properties[prop->name()] = prop;

};

/**

* Get the value of a readable property

* This method is templated and takes the type of the property as a parameter.

* \param key Name of the property

 

Back