Game Development by Sean

C++ Method Type Deduction Tricks

Working on the next part of the C++ Metadata series, I decided that I didn’t like the way I was handling setters and getters for object properties. I wanted something even faster and with even less overhead while retaining the extreme simplicity of my API. I discovered some tricks (only tested in Visual Studio 2010 so far) that do exactly what I need. I’m afraid I’m a little too swamped to write anything about how these tricks work just now, but I wanted to get the code out there.

I may update the article with explanations of what’s going on and why this all works later if time permits.

#include

// debugging macro #define assert(expr) do{ if (!(expr)) __debugbreak(); }while(0)

// getter/setter typedefs typedef void(Getter)(const void object, void* out_value); typedef void(Setter)(void object, const void* value);

// deduce if a getter is const or not template <typename ObjectType, typename ReturnType> std::true_type deduce_constness(ReturnType(ObjectType::*getter)() const);

template <typename ObjectType, typename ReturnType> std::false_type deduce_constness(ReturnType(ObjectType::*getter)());

// deduce the object type of a getter/setter pointer-to-method template <typename ObjectType, typename ReturnType> ObjectType deduce_object(ReturnType(ObjectType::*getter)() const);

template <typename ObjectType, typename ReturnType> ObjectType deduce_object(ReturnType(ObjectType::*getter)());

template <typename ObjectType, typename ReturnType, typename SetType> ObjectType deduce_object(ReturnType(ObjectType::*getter)(SetType));

// deduce the return type of a getter/setter pointer-to-method template <typename ObjectType, typename ReturnType> ReturnType deduce_return_val(ReturnType(ObjectType::*getter)() const);

template <typename ObjectType, typename ReturnType> ReturnType deduce_return_val(ReturnType(ObjectType::*getter)());

template <typename ObjectType, typename ReturnType, typename SetType> ReturnType deduce_return_val(ReturnType(ObjectType::*getter)(SetType));

// deduce the parameter type of a setter pointer-to-method template <typename ObjectType, typename ReturnType, typename SetType> SetType deduce_first_param(ReturnType(ObjectType::*setter)(SetType value));

// wrapper for a const getter template <bool Const, typename ObjectType, typename ReturnType> struct wrap_getter { template <ReturnType(ObjectType::Getter)() const> static void thunk(const void object, void* out_value) { const ObjectType* typed_object = reinterpret_cast<const ObjectType>(object); ReturnType typed_value = reinterpret_cast<ReturnType>(out_value); *typed_value = (typed_object->Getter)(); } };

// wrapper for a non-const getter template <typename ObjectType, typename ReturnType> struct wrap_getter<false, ObjectType, ReturnType> { template <ReturnType(ObjectType::Getter)()> static void thunk(const void object, void* out_value) { ObjectType* typed_object = reinterpret_cast<ObjectType>(const_cast<void>(object)); ReturnType* typed_value = reinterpret_cast<ReturnType>(out_value); *typed_value = (typed_object->Getter)(); } };

// wrapper for a setter template <typename ObjectType, typename ReturnType, typename SetType> struct wrap_setter { template <ReturnType(ObjectType::Setter)(SetType)> static void thunk(void object, const void* value) { ObjectType* typed_object = reinterpret_cast<ObjectType>(object); const SetType typed_value = reinterpret_cast<const SetType>(value); (typed_object->Setter)(*typed_value); } };

// construction macros #define GETTER(method) \ (&wrap_getter<std::is_same<decltype(deduce_constness(method)), std::true_type>::value, decltype(deduce_object(method)), decltype(deduce_return_val(method))>::thunk)

#define SETTER(method) \ (&wrap_setter<decltype(deduce_object(method)), decltype(deduce_return_val(method)), decltype(deduce_first_param(method))>::thunk)

// test object class TestObject { public: TestObject(): i(5), f(2.f) {}

int getInt() const { return i; } float getFloat() { return f; }

void setInt(int i_) { i = i_; } float setFloat(float f_) { return f = f_; }

int i; float f; };

// test code int main() { Getter call_i = GETTER(&TestObject::getInt); Getter call_f = GETTER(&TestObject::getFloat);

Setter set_i = SETTER(&TestObject::setInt); Setter set_f = SETTER(&TestObject::setFloat);

TestObject o; int i; float f;

call_i(&o, &i); call_f(&o, &f);

assert(i == o.i); assert(f == o.f);

i = 10; f = 7.f;

set_i(&o, &i); set_f(&o, &f);

assert(o.i == i); assert(o.f == f); }</code></div>