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);
}