2
\$\begingroup\$

I finally have a working trampoline mechanism. Would anyone care to critique it?

Ideone

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

This question follows from here.

\$\endgroup\$
1
\$\begingroup\$

I will maintain my list of improvements in this answer.

There is a more succinct way to expose the mechanism to the consumer:

class Final : Base<Final>
{
    register_method<&Final::Foo>( "foo" ); //, static_cast<PHandler>( &Handler<&Final::Foo> ) );
    register_method<&Final::Bar>( "bar" ); //, static_cast<PHandler>( &Handler<&Final::Bar> ) );
    :

class Base
{
    :
    template<PFunc func>
    static void register_method( string name ) {
        methods()[name] = &Handler<func>;
    }
    :

This has extra advantage in that it avoids the following nasty issue:

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)

It doubles the number of templated functions getting generated: now 2 are generated for each method instead of one. But at a small price, it is much cleaner to the consumer!

Also function pointer typedef-s could be rewritten:

//typedef void (T::*PFunc)(void);
//typedef void (*PHandler)(void*);

using PFunc = void (T::*)(void);
using PHandler = void(*)(void);

http://ideone.com/ZFQb1f

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.