Кодирование статических функций экземпляра метода батут с шаблонами

Я пытаюсь перекодировать довольно некрасивое шаблонное кодирование.

Для справки, оригинал здесь: 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();
}
}

Мой код взаимодействует с CPython (библиотека C).
Среда выполнения Python видит «myInst.foo», ищет «foo» в таблице и вызывает:

Final::FooHandler( pointer_to_myInst );

(Обратите внимание, что можно типизировать статический метод к указателю на функцию C)

FooHandler батуты к методу Foo правильного экземпляра Final.

На самом деле дескриптор не такой чистый, и есть много методов, каждый из которых требует идентичного обработчика (но с отдельным адресом функции).

Я пытаюсь абстрагировать механизм обработчика в базовый класс, что-то вроде этого:

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

В настоящее время я застреваю в ошибках компилятора (http://ideone.com/vOtbcD), так что я даже не уверен, что метод действителен.

Есть ли способ сделать это, или это один из тех случаев, когда вам действительно нужны макросы?

Для справки, оригинал здесь: https://codereview.stackexchange.com/questions/69545/recode-c-c-trampoline-function-macros-using-templates

Как видно, в оригинале используются довольно некрасивые макросы.

0

Решение

register_method должен быть статическим, если вы собираетесь вызывать его из статического init функция.

Для вызова функции-члена через указатель на функцию-член требуется дополнительный набор скобок: (f->*sig)();

После этого ваш тестовый пример компилируется в режиме C ++ 11. Тем не менее, вы рассматривали возможность использования std::function а также std::bind сделать это вместо этого? Трудно сказать, как это будет выглядеть на самом деле, не зная, что register_method делает, но это может закончиться немного чище.

2

Другие решения

Следующий код работает на 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;
}

Примечание: (Eelis на IRC) известно, что gcc не может разрешать адреса специализаций шаблонов функций, передаваемых функциям. Вы можете обойти это, либо объявив отдельные переменные для указателей на функции, либо используя грязное приведение (или хорошее приведение типа boost :: implicit_cast)

Я поместил этот код на рассмотрение по адресу: https://codereview.stackexchange.com/questions/69876/static-to-instance-method-trampolining-with-templates

0

По вопросам рекламы [email protected]