2016-04-08 28 views
7

Sto tentando di creare una funzione generica wrapper che accetta una funzione come argomento modello e accetta gli stessi argomenti di quella funzione dei suoi argomenti. Ad esempio:Call wrapper con funzione C++ con funzione come argomento modello

template <typename F, F func> 
/* return type of F */ wrapper(Ts... Args /* not sure how to get Ts*/) 
{ 
    // do stuff 
    auto ret = F(std::forward<Ts>(args)...); 
    // do some other stuff 
    return ret; 
} 

La soluzione deve essere colabile a un puntatore funzione con lo stesso tipo func modo che possa passare ad un'api C. In altre parole, la soluzione deve essere una funzione e non un oggetto funzione. Ancora più importante, Devo essere in grado di lavorare nella funzione wrapper.

Se i commenti inline non sono chiare, mi piacerebbe essere in grado di fare qualcosa di simile al seguente:

struct c_api_interface { 
    int (*func_a)(int, int); 
    int (*func_b)(char, char, char); 
}; 

int foo(int a, int b) 
{ 
    return a + b; 
} 

int bar(char a, char b, char c) 
{ 
    return a + b * c; 
} 

c_api_interface my_interface; 
my_interface.func_a = wrapper<foo>; 
my_interface.func_b = wrapper<bar>; 

ho cercato related posts e trovato questi, ma nessuno di loro sono abbastanza quello Sto cercando di fare. La maggior parte di questi post riguarda oggetti funzionali. Quello che sto cercando di fare è anche possibile?

Function passed as template argument

Function wrapper via (function object) class (variadic) template

How does wrapping a function pointer and function object work in generic code?

How do I get the argument types of a function pointer in a variadic template class?

Generic functor for functions with any argument list

C++ Functors - and their uses

In risposta alle prime 2 risposte, ho modificato la domanda per chiarire che devo essere in grado di lavorare nella funzione wrapper (ad es. modificare alcune stato globale prima e dopo la chiamata alla funzione avvolto)

risposta

3
#include <utility> 
#include <iostream> 

struct c_api_interface { int (*func_a)(int, int); int (*func_b)(char, char, char); }; 
int foo(int a, int b) { return a + b; } 
int bar(char a, char b, char c) { return a + b * c; } 


template<typename Fn, Fn fn, typename... Args> 
typename std::result_of<Fn(Args...)>::type 
wrapper(Args... args) { 
    std::cout << "and ....it's a wrap "; 
    return fn(std::forward<Args>(args)...); 
} 
#define WRAPIT(FUNC) wrapper<decltype(&FUNC), &FUNC> 

int main() { 
    c_api_interface my_interface; 
    my_interface.func_a = WRAPIT(foo); 
    my_interface.func_b = WRAPIT(bar); 

    std:: cout << my_interface.func_a(1,1) << std::endl; 
    std:: cout << my_interface.func_b('a','b', 1) << std::endl; 

    return 0; 
} 

vedere http://rextester.com/ZZD18334

+0

Grazie! Sto cambiando questo alla risposta accettata perché è più breve di @TC, usa solo una funzione e non una struct, usa 'std :: forward', e (per una questione di opinioni) è più facile da capire perché non lo hai fatto utilizzare la specializzazione degli argomenti del modello. – Eric

+0

Anche se devo ammettere che std :: forward è solo lì perché ho provato qualcosa che sarebbe compilato sotto MSVC e poi ho pensato "ah fanculo, non succederà ... ma hey, mantieni l'avanti" ;-) – VolkerK

+0

Cosa è il modo corretto per dichiarare un alias per il wrapper che sembrerà una normale funzione da chiamare? Qualcosa come "usando a_wrapped = wrapper ;" (ma questo non funziona). – eudoxos

0

probabilmente bisogno di qualcosa come

template <typename F> 
class Wrapper { 
public: 
    Wrapper(F *func) : function(func) {} 
    operator F*() { return function; } 
    F *function; 
}; 

che si può usare come void (*funcPtr)(int) = Wrapper<void(int)>(&someFunction);

+0

Apprezzo la risposta, ma questo non fa quello che ho chiesto nel post originale . Nella tua soluzione, se chiamo 'funcPtr()' chiama 'someFunction'. Mi piacerebbe che richiamasse una funzione che funziona un po 'prima e dopo aver chiamato 'someFunction'. – Eric

+1

@Eric, ora capisco cosa vuoi e non capisco perché stai cercando un modo così difficile.Le funzioni Lambda senza acquisizione sono valide per i puntatori di funzioni c: 'void (* funcPtr) (int) = [] (int a) {/ * smth * /};' - chiamata funzione di avvolgimento e routine prima/dopo all'interno di lambda –

+0

I l'ho provato all'inizio. Credo che i lambda siano utilizzabile solo per i puntatori di funzione se non catturano e per rendere questo generico ho bisogno di scrivere un lambda che cattura la funzione che chiamerà, ma forse c'è un modo per fare quel lavoro che mi manca . – Eric

0

penso che sarà il modo conciso di fare ciò che si vuole:

template <typename F> 
F* wrapper(F* pFunc) 
{ 
    return pFunc; 
} 

e usarlo in questo modo:

my_interface.func_a = wrapper(foo); 
my_interface.func_a(1, 3); 
+0

Grazie per la risposta, ma questo non fa quello che ho chiesto. Devo essere in grado di fare un po 'di lavoro nella funzione wrapper prima e dopo chiamo la funzione wrapped (in questo caso 'foo'). Quindi la funzione wrapper deve avere la ** stessa firma della funzione della funzione wrapping **. – Eric

+0

Non sono sicuro che sia possibile recuperare la firma dagli argomenti del modello (ovvero 'Ts ... Args') ma è possibile salvarli per le chiamate posticipate come mostrato [qui] (http://stackoverflow.com/questions/15537817/c -how-to-store-a-parameter-pack-as-a-variable) e poi in 'Wrapper :: operator()' fa cose prima e dopo la chiamata al func sottostante con argomenti salvati –

7
template<class F, F f> struct wrapper_impl; 
template<class R, class... Args, R(*f)(Args...)> 
struct wrapper_impl<R(*)(Args...), f> { 
    static R wrap(Args... args) { 
     // stuff 
     return f(args...); 
    } 
}; 

template<class F, F f> 
constexpr auto wrapper = wrapper_impl<F, f>::wrap; 

Utilizzare come wrapper<decltype(&foo), foo>.

+0

Grazie mille, questo ha funzionato perfettamente! :) Per gli altri: la variabile template è una caratteristica di C++ 14, quindi potrebbe non funzionare su tutti i compilatori. Ho definito una macro '#define wrapper (func) wrapper_impl :: wrap' che esegue approssimativamente la stessa cosa. – Eric

+0

Potrebbe essere adattato per contenere argomenti opzionali? Ciò significa che gli argomenti opzionali nella funzione wrapper sono opzionali anche nel wrapper. In questa variante, tutti gli argomenti devono essere specificati. – eudoxos

0

si può provare qualcosa di simile (brutto, ma funziona)

#include <iostream> 
#include <functional> 

struct wrapper_ctx 
{ 
    wrapper_ctx() 
    { 
    std::cout << "Before" << std::endl; 
    } 
    ~wrapper_ctx() 
    { 
    std::cout << "after" << std::endl; 
    } 
}; 

template <typename F, typename... Args> 
auto executor (F&& f, Args&&... args) -> typename std::result_of<F(Args...)>::type 
{ 
    wrapper_ctx ctx; 
    return std::forward<F>(f)(std::forward<Args>(args)...); 

} 

template <typename F> 
class wrapper_helper; 


template<typename Ret, typename... Args> 
class wrapper_helper <std::function<Ret(Args...)>> 
{ 
    std::function<Ret(Args...)> m_f; 
public: 
    wrapper_helper(std::function<Ret(Args...)> f) 
     : m_f(f) {} 
    Ret operator()(Args... args) const 
    { 
    return executor (m_f, args...); 
    } 
}; 

template <typename T> 
wrapper_helper<T> wrapper (T f) 
{ 
    return wrapper_helper <T>(f); 
} 


int sum(int x, int y) 
{ 
    return x + y; 
} 


int main (int argc, char* argv []) 
{ 

    std::function<int(int, int)> f = sum; 
    auto w = wrapper (f); 

    std::cout << "Executing the wrapper" << std::endl; 
    int z = w(3, 4); 

    std::cout << "z = " << z << std::endl; 
}