2016-04-08 12 views
7

Ich versuche, eine generische Wrapperfunktion zu erstellen, die eine Funktion als Vorlageargument übernimmt und dieselben Argumente wie diese Funktion als Argumente verwendet. Zum Beispiel:C++ - Funktionsaufruf-Wrapper mit Funktion als Vorlageargument

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

Die Lösung muss gießbaren auf einen Funktionszeiger mit dem gleichen Typ wie func sein, so dass ich es an einen C api passieren kann. Mit anderen Worten, die Lösung muss eine Funktion und kein Funktionsobjekt sein. Am wichtigsten ist, Ich muss in der Lage sein, Arbeit in der Wrapper-Funktion zu tun.

Wenn die Inline-Kommentare nicht klar sind, würde ich das so etwas wie die Lage sein, gerne tun folgende:

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

ich für verwandte Beiträge gesucht und gefunden diese, aber keiner von ihnen ist ganz das, was Ich versuche es zu tun. Die meisten dieser Posts betreffen Funktionsobjekte. Ist das, was ich versuche, überhaupt möglich?

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

Als Antwort auf die ersten beiden Antworten habe ich die Frage bearbeitet, um klarzustellen, dass ich in der Lage sein muss, in der Wrapper-Funktion zu arbeiten (d. H. ändern einige globalen Zustand vor und nach dem Aufruf der eingewickelt Funktion)

Antwort

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

siehe http://rextester.com/ZZD18334

+0

Danke! Ich ändere das zur akzeptierten Antwort, weil es kürzer ist als das von @TC, benutzt nur eine Funktion und nicht eine Struktur, benutzt 'std :: forward' und ist (meiner Meinung nach) leichter zu verstehen, weil du es nicht getan hast Verwenden Sie Template Argument Spezialisierung. – Eric

+0

Obwohl ich zugeben muss, dass der std :: forward nur drin ist, weil ich etwas versucht habe, das unter MSVC kompilieren würde und dachte dann "ah schraube es, werde nicht passieren ... aber hey, behalte den Forward" ;-) – VolkerK

+0

Was? ist der richtige Weg, um einen Alias ​​für den Wrapper zu deklarieren, der wie eine normale Funktion aussehen wird? Etwas wie 'using a_wrapped = wrapper ;' (aber dieses funktioniert nicht). – eudoxos

0

Sie wahrscheinlich so etwas wie

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

benötigen, die Sie mögen void (*funcPtr)(int) = Wrapper<void(int)>(&someFunction);

+0

ich die Antwort zu schätzen wissen, aber dies nicht tut, was ich in der ursprünglichen Post gefragt . Wenn ich 'funcPtr()' in Ihrer Lösung anrufe, ruft 'someFunction' auf. Ich möchte, dass es eine Funktion aufruft, die vor und nach dem Aufruf von "someFunction" etwas Arbeit leistet. – Eric

+1

@Eric, jetzt verstehe ich was du willst und verstehe nicht warum suchst du so einen harten Weg.Lambda-Funktionen ohne Capturing können in c-Funktionszeiger umgewandelt werden: 'void (* funcPtr) (int) = [] (int a) {/ * smth * /};' - umbrochene Funktion und Vorher/Nachher-Routinen direkt in Lambda aufrufen –

+0

I versuchte das zuerst. Ich glaube, dass lambdas nur für Zeiger funktionierbar sind, wenn sie nicht erfassen, und um dies generisch zu machen, musste ich ein lambda schreiben, das die Funktion erfasst, die es aufrufen wird, aber vielleicht gibt es eine Möglichkeit, die Arbeit zu machen, die ich vermisse . – Eric

0

denken verwenden kann ich, dass wird die prägnante Art und Weise zu tun, was Sie wollen:

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

und es wie folgt verwendet werden:

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

Danke für die Antwort, aber das tut nicht, was ich gefragt habe. Ich muss in der Wrapper-Funktion vor und nach dem Aufruf der Wrapped-Funktion (in diesem Fall 'foo') arbeiten können. Daher muss die Wrapper-Funktion die gleiche Funktionssignatur wie die Wrapped-Funktion haben **. – Eric

+0

Ich bin mir nicht sicher, ob Sie Signatur aus Vorlagenargumenten abrufen können (nämlich 'Ts ... Args'), aber Sie können sie für einen verzögerten Aufruf speichern, wie [hier] (http://stackoverflow.com/questions/15537817/c) -how-to-store-a-parameter-pack-as-a-variable) und dann in 'Wrapper :: operator()' stopfen vor und nach dem Aufruf der zugrunde liegenden Funktion mit gespeicherten Argumente –

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; 

Verwendung als wrapper<decltype(&foo), foo>.

+0

Vielen Dank, das hat funktioniert perfekt! :) Für andere: die Variable Vorlage ist eine C++ 14 Funktion, die ich glaube, so dass möglicherweise nicht auf allen Compilern funktioniert. Ich habe ein Makro '#define wrapper (func) wrapper_impl :: wrap' definiert, das ungefähr die gleiche Sache erreicht. – Eric

+0

Könnte dies angepasst werden, um optionale Argumente zu berücksichtigen? Das bedeutet, dass optionale Argumente in der Wrapped-Funktion auch im Wrapper optional sind. In dieser Variante müssen alle Argumente angegeben werden. – eudoxos

0

man so etwas versuchen kann (hässlich, aber funktioniert)

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