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>