2015-02-14 2 views
2

Я пытаюсь придумать шаблонную функцию, которая обобщает процедуру отказов при работе с C API, использующими обратные вызовы функций.C++ templated callback bounce function

Я в основном выяснил это и имею рабочую систему, но мне интересно, есть ли способ очистить последний шаг.

Представьте, что у вас есть API, который принимает указатель на функцию и указатель данных пользователя. Вы хотите использовать метод экземпляра в качестве цели обратного вызова. Для этого требуется функция «отскока», которая переинтерпретирует указатель пользовательских данных как указатель экземпляра и вызывает метод с остальными аргументами.

В следующем примере кода работы:

#include <cstdio> 

class Foo { 
public: 
    Foo(int val) : val_(val) { } 

    void baz(int v) const 
    { 
     printf("baz %d\n", v + val_); 
    } 

private: 
    int val_; 
}; 

// Templated bounce function 
template<class T, class Method, Method m, class Ret, class ...Args> 
static Ret bounce(void *priv, Args... args) 
{ 
    return ((*reinterpret_cast<T *>(priv)).*m)(args...); 
} 

#define BOUNCE(c, m) bounce<c, decltype(&c::m), &c::m> 

// Callback simulator 
void call_callback(void (*func)(void *, int), void *priv, int v) 
{ 
    if (func) { 
     func(priv, v); 
    } 
} 

// Main Entry 
int main() 
{ 
    Foo bar(13); 

    call_callback(&bounce<Foo, decltype(&Foo::baz), &Foo::baz>, &bar, 10); 
    call_callback(&BOUNCE(Foo, baz), &bar, 11); 

    return 0; 
} 

В основном я ищу способ, чтобы очистить использование. Макрос работает, но я пытаюсь вместо этого найти какую-то вспомогательную функцию, которая может просто принять параметр указателя метода, например &Foo::baz, и вывести все параметры. Что-то вроде bounce_gen(&Foo::baz), которое вернет указатель на фактическую функцию отскока.

Это было веселое упражнение, но я не могу получить последний кусок.

ответ

0

Тип указателя функции-члена содержит тип класса и сигнатуру функции. Таким образом, вы можете позволить шаблон функции аргумент вычет ручки это для вас:

template<class T, class Method, class ...Args> 
static auto bounce(Method T::*func, T* priv, Args... args) -> decltype((priv->*m)(args...)) 
{ 
    return (priv->*m)(args...); 
} 

Более удобным может быть либо использовать std::bind или лямбда, чтобы полностью скрыть тот факт, что она является функцией член вызова:

template<class Func, class ...Args> 
static auto bounceCallable(Func func, Args... args) -> decltype(func(args...)) 
{ 
    return func(args...); 
} 

И вы могли бы назвать это так:

call_callback([&bar](int v){bar.baz(v);}, 11); 

с лямбда, у вас есть синтаксис лучше, чем с std::bind, но он приходит на соз t необходимости повторять подпись.

+0

Проблема в том, что отскок должен принимать только указатель и аргументы пользовательских данных 'void *'. Он не может принимать указатель на функцию-член. (Я не контролирую API обратного вызова). В вашем случае я понимаю, как работает дедукция параметров. Вместо этого мне нужно что-то сделать для вывода параметров и сгенерировать указатель функции bounce для перехода в API. –

+0

А я вижу. Тот же принцип применяется. Сравнивая ваш макрос 'BOUNCE' и определение' bounce' в моем ответе, это сопоставление 'c == T',' decltype (& c :: m) == Метод T :: * 'и' & c :: m = = func'. – Pradhan