// Andy Friesen --

Ghetto Closures in C++ III: Templates and Traits and Interfaces, oh my!

2009-02-10 00:00:00 +0000

Last time, we went over generating a tiny ASM thunk that could wrap a C++ method pointer (instance plus function) up in a non-method __stdcall function pointer, suitable for use as, say, a win32 WndProc. Next, I’m going to talk about wrapping it all up in a convenient API. To do this, we’re going to need some template-fu.

Since I am using a modern compiler, I am going to see how close I can get to boost.function‘s interface:

boost::function<void ()> fn = &someFunction;

I consider this interface to be pretty rad.

boost::function works with only a single template argument, so we could go that route too. We could also accept that we’re doing something a bit different, and add a second parameter:

Thunk<LRESULT (Window::*)(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)> wndProcThunk;

or

Thunk<Window, LRESULT (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)> wndProcThunk;

I picked the first one, mostly because it was the first thing that popped into my head. Here is my test harness:

#include <cstdio>
using std::printf;

struct I {
    virtual void print() = 0;
    virtual void printSum(double y) = 0;
};

struct C : I {
    C(int x)
        : x(x)
    { }

    void print() {
        printf("My x is %in", x);
    }

    void printSum(double y) {
        printf("%i + %f = %fn", x, y, x + y);
    }

    int x;
};

void main() {
    C instance(4);

    Thunk<void (I::*)()> thunk(&instance, &I::print);
    printf("n");
    thunk.get()();

    Thunk<void (I::*)(double)> thunk2(&instance, &I::printSum);
    printf("n");
    thunk2.get()(3.14);
}

First, I extracted the part of the code that actually constructs and cleans up the generated code. Everything else I’ll outline will just be scaffolding so that the interface is prettier.

template <typename D, typename S>
D really_reinterpret_cast(S s) {
    char __static_assert_that_types_have_same_size[sizeof(S) == sizeof(D)];

    union {
        S s;
        D d;
    } u;

    u.s = s;
    return u.d;
}

template <typename C, typename M>
void* createThunk(C* instance, M method) {
    char code[] = {
        0xB9, 0, 0, 0, 0,   // mov ecx, 0
        0xB8, 0, 0, 0, 0,   // mov eax, 0
        0xFF, 0xE0          // jmp eax
    };

    // YEEHAW
    *((I**)(code + 1)) = instance;
    *((void**)(code + 6)) = really_reinterpret_cast<void*>(method);

    void* thunk = VirtualAlloc(0, sizeof(code), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    memcpy(thunk, &code, sizeof(code));
    FlushInstructionCache(GetCurrentProcess(), thunk, sizeof(code));

    return thunk;
}

void releaseThunk(void* thunk) {
    VirtualFree(thunk, 0, MEM_RELEASE);
}

To be honest, this interface isn’t all that bad: all you have to do is remember to manage the lifetime of the generated code and cast the void* you get to the right type. That’s kind of boring, though, so let’s instead see if we can make something kickass and typesafe.

I like objects, so let’s start with one of those.

template <typename M>
struct Thunk {
    typedef M Method;
    typedef typename methodptr_traits<M>::class_type Class;
    typedef typename methodptr_traits<M>::function_type Function;

    Thunk(Class* instance, Method method)
        : ptr(createThunk(instance, method))
    { }

    ~Thunk() {
        releaseThunk(ptr);
    }

    Function get() const {
        return reinterpret_cast<Function>(ptr);
    }

private:
    Thunk(const Thunk&);

    void* ptr;
};

This is simple enough to be boring, except for this methodptr_traits thing.

methodptr_traits is an instance of something called a traits class. Basically, it is a fancy template type that defines various other types. You can think of it as a way to code ad-hoc, compile-time type introspection.

If you’ve never used templates this way, the implementation is pretty intimidating:

template <typename F>
struct methodptr_traits;

template <typename ReturnType, typename T>
struct methodptr_traits<ReturnType (T::*)()> {
    typedef T class_type;
    typedef ReturnType (__stdcall *function_type)();
};

This is one of the more convoluted things one can do with templates, and I’ll be the first to admit that I think it’s a bit scary. Let’s rewind a bit and look at this in simpler terms. Say we want a boolean variable that’s true if a particular template type is a number. We can use template specialization to accomplish this pretty easily:

template <typename T> struct is_int { enum {value = false}; };
template <> struct is_int<int> { enum {value = true}; };
template <> struct is_int<short> { enum {value = true}; };
template <> struct is_int<char> { enum {value = true}; };
// and so on

I might leverage this code with something like the following:

if (is_int<T>::value) { /* stuff */ }

and be off to the races.

Cool, right? Now what if we instead wanted to know whether something is a std::vector, whatever the element type? The same principle applies, but now we have a template specialization that is itself a template:

template <typename T> struct is_vector { enum {value=false}; };
template <typename E> struct is_vector<std::vector<E> > { enum {value=true}; };
How to use this should be obvious:

if (is_vector<T>::value) { /* do something that only works on std::vector */ }

methodptr_traits is just a tiny jump further. Most of the terror that this sort of thing inspires is really the fault of C++’s ridiculous function pointer syntax.

It is kind of a drag that C++ templates cannot express functions without specifying exactly how many arguments the function has. Because of this, a new specialization must be written for each argument count you want to support. I only did 0 and 1 arguments because this is just a small example. boost tends to support a minimum of 10 arguments by default, which is good enough for almost everyone.

For this example, I only need 0 and 1 argument, so here’s the specialization for a one-argument function:

template <typename ReturnType, typename T, typename Arg1>
struct methodptr_traits<ReturnType (T::*)(Arg1)> {
    typedef T class_type;
    typedef ReturnType (__stdcall *function_type)(Arg1);
};

And that’s it! With a single templatized class, we can dynamically generate an assembly thunk that works as a perfectly usable __stdcall function. We can pass this function pointer on to Win32, GLU, or whatever other C library we might need a callback function for.