Coding static to instance method trampoline function with templates

610 Views Asked by At

I'm trying to recode some rather ugly template coding.

For reference, the original is here: https://codereview.stackexchange.com/questions/69545/recode-c-c-trampoline-function-macros-using-templates

class Final : Base
{
    void Foo(){...}
    void Bar(){...}

    static void init(){
        // register_method populates a table of "extern C" function pointers.
        register_method( "foo", & FooHandler );
        register_method( "bar", & BarHandler );
    }
    :
    // trampolines
    static void FooHandler( void* pInstance ) {
        Final* f = reinterpret_cast<Final*>(pInstance);
        f->Foo();
    }
    static void BarHandler( void* pInstance ) {
        Final* f = reinterpret_cast<Final*>(pInstance);
        f->Bar();
    }
}

My code interfaces with CPython (C library). Python runtime sees "myInst.foo" , looks up "foo" in table and invokes:

Final::FooHandler( pointer_to_myInst );

(Note it is possible to typecast a static method to a C function pointer)

FooHandler trampolines to the Foo method of the correct instance of Final.

In reality, the handle is not so clean, and there are many methods each of which requires an identical handler (but with a distinct function address).

I am attempting to abstract the handler mechanism into a base class, something like this:

class Final : Base<Final>
{
    void Foo(){...}
    void Bar(){...}

    static void init(){
        // register_method populates a table of "extern C" function pointers.
        register_method( "foo", & Foo, Handler< &Foo> );
        register_method( "bar", & Bar, Handler< &Bar> );
    }
    :
}

class Base<Final>
{
    typedef void (Final::*FuncSig)(void);
    typedef void (Final::*HandlerSig)(void*); // takes 1 pvoid param

    void register_method( std::string name, FuncSig meth, HandlerSig handler ) {
        ...        
    }

    // generic trampoline
    template< Sig sig>
    static void Handler( void* pInstance ) {
        Final* f = reinterpret_cast<Final*>(pInstance);
        f ->* sig();
    }
}

I'm currently getting stuck in compiler errors (http://ideone.com/vOtbcD), so I'm not even sure whether the technique is valid.

Is there some way to do this, or is this just one of those times where you really need macros?

For reference, the original is here: https://codereview.stackexchange.com/questions/69545/recode-c-c-trampoline-function-macros-using-templates

As can be seen, the original uses rather ugly macros.

2

There are 2 best solutions below

1
On BEST ANSWER

register_method needs to be static if you're going to call it from the static init function.

Calling a member function through a member function pointer requires an additional set of parenthesis: (f->*sig)();

With those in place, your test case compiles in C++11 mode. That said, have you considered using std::function and std::bind to do this instead? Hard to tell what that would actually look like without knowing what register_method does, but it might wind up a bit cleaner.

0
On

The following code works on ideone (http://ideone.com/vOtbcD):

#include <iostream>
using namespace std;

#include <map>

template<typename T>
class Base
{
public:    
    typedef void (T::*PFunc)(void);
    typedef void (*PHandler)(void*);

    using method_map_t = map< string, PHandler >;
    static method_map_t& methods( ) {
        static method_map_t* map_of_methods{};
        if( ! map_of_methods ) map_of_methods = new method_map_t;
        return *map_of_methods;
    }

    static void register_method( string name, PHandler handler ) {
        methods()[name] = handler;
    }

    // generic trampoline
    template<PFunc sig>
    static void Handler( void* pInstance ) {
        T* f = reinterpret_cast<T*>(pInstance);
        (f ->* sig)();
    }
};

...

class Final : Base<Final>
{
public:
    void Foo(){cout<<"got foo";}
    void Bar(){cout<<"got bar";}

    static void init(){
        // register_method populates a table of "extern C" function pointers.
        register_method( "foo", static_cast<PHandler>( &Handler<&Final::Foo> ) );
        register_method( "bar", static_cast<PHandler>( &Handler<&Final::Bar> ) );
    }
};

void InvokeFromC(void* inst, string name) {
    Base<Final>::PHandler h = Base<Final>::methods()[name];
    (*h)(inst);
}

int main() {
    Final* f = new Final{};
    f->init();
    // simulate invoking from C 
    InvokeFromC( f, "foo" );

    // your code goes here
    return 0;
}

Note: (Eelis on IRC) gcc is known not to be able to resolve addresses of function template specializations passed to functions. You can work around it either by declaring separate variables for the function pointers, or using a dirty cast (or a nice cast like boost::implicit_cast)

I have put this code up for review at: https://codereview.stackexchange.com/questions/69876/static-to-instance-method-trampolining-with-templates