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
Christian Corsano
