2017-10-02 1 views
7

Sind zwei oder mehr Beispielfunktionen gegeben, ist es möglich, einen Templates-Code zu schreiben, der in der Lage wäre, die Argumente einer als Template-Parameter bereitgestellten Funktion abzuleiten?Wie man Argumentliste vom Funktionszeiger ableitet?

Dies ist das motivierende Beispiel:

void do_something(int value, double amount) { 
    std::cout << (value * amount) << std::endl; 
} 

void do_something_else(std::string const& first, double & second, int third) { 
    for(char c : first) 
     if(third/c == 0) 
      second += 13.7; 
} 

template<void(*Func)(/*???*/)> 
struct wrapper { 
    using Args = /*???*/; 
    void operator()(Args&& ... args) const { 
     Func(std::forward<Args>(args)...); 
    } 
}; 

int main() { 
    wrapper<do_something> obj; //Should be able to deduce Args to be [int, double] 
    obj(5, 17.4); //Would call do_something(5, 17.4); 
    wrapper<do_something_else> obj2; //Should be able to deduce Args to be [std::string const&, double&, int] 
    double value = 5; 
    obj2("Hello there!", value, 70); //Would call do_something_else("Hello there!", value, 70); 
} 

In beiden Verwendungen von /*???*/, ich versuche, herauszufinden, was ich konnte dort setzen, dass diese Art von Code ermöglichen würde.

Das folgende scheint nicht zu funktionieren, aufgrund Args nicht vor seiner ersten Verwendung definiert (zusammen mit dem, was ich annehmen muss, sind zahlreiche Syntaxfehler neben), und selbst wenn es tat, suche ich noch eine Version, die sich nicht ausdrücklich schriftlich der Typen erfordern:

template<void(*Func)(Args ...), typename ... Args) 
struct wrapper { 
    void operator()(Args ...args) const { 
     Func(std::forward<Args>(args)...); 
    } 
}; 

wrapper<do_something, int, double> obj; 
+0

Sie bedeuten [std :: function] (http://en.cppreference.com/w/ cpp/Dienstprogramm/Funktion/Funktion)? – bolov

+0

@bolov Was ich speziell suche, ist eine Möglichkeit, 'Args' aus der' Funktion' abzuleiten. 'std :: function' passt Funktionszeiger an, indem diese Typen explizit in der Typparameterliste angegeben werden. – Xirema

+0

Akzeptieren Sie C++ 17? wink wink – bolov

Antwort

5

Mit C 17 ++ wir auto-Vorlage nicht-Typ-Parameter haben kann, die die Wrapper<do_something> w{} Syntax 1) möglich zu machen.

Zum Ableiten Args... können Sie das mit einem specialization tun.

template <auto* F> 
struct Wrapper {}; 

template <class Ret, class... Args, auto (*F)(Args...) -> Ret> 
struct Wrapper<F> 
{ 
    auto operator()(Args... args) const 
    { 
     return F(args...); 
    } 
}; 
Wrapper<do_something> w{}; 
w(10, 11.11); 

1) Ohne 17 C++ ist es unmöglich, die schön Wrapper<do_something> w{} Syntax zu haben.

Das Beste, was Sie tun können, ist:

template <class F, F* func> 
struct Wrapper {}; 

template <class Ret, class... Args, auto (*F)(Args...) -> Ret> 
struct Wrapper<Ret (Args...), F> 
{ 
    auto operator()(Args... args) const 
    { 
     return F(args...); 
    } 
}; 
Wrapper<declype(do_something), do_something> w{}; 
+0

Ihr Beispielcode wird nicht in GCC kompiliert: https://godbolt.org/g/3WEksw – Xirema

+1

@Xirema C++ 17 befindet sich in den frühen Phasen der Implementierung. clang kann es ohne Probleme kompilieren: https://godbolt.org/g/DyArxa – bolov

4

Mit 17 C++, können Sie dies tun:

template <auto FUNC, typename = decltype(FUNC)> 
struct wrapper; 

template <auto FUNC, typename RETURN, typename ...ARGS> 
struct wrapper<FUNC, RETURN (*)(ARGS...)> { 
    RETURN operator()(ARGS ...args) { 
     return FUNC(args...); 
    } 
}; 

ich diese Technik aus WF gelernt habe‘ s answer

+0

Bolov hat seine Lösung repariert, und eigentlich mag ich diese Lösung jetzt besser. – geza

2

Weitere Verbesserung von C++ 17 Version: weniger Vorlage pa metern und richtige noexcept Anmerkung:

template<auto VFnPtr> struct 
wrapper; 

template<typename TResult, typename... TArgs, TResult (* VFnPtr)(TArgs...)> struct 
wrapper<VFnPtr> 
{ 
    TResult 
    operator()(TArgs... args) const noexcept(noexcept((*VFnPtr)(::std::forward<TArgs>(args)...))) 
    { 
     return (*VFnPtr)(::std::forward<TArgs>(args)...); 
    } 
}; 
0

Mit 11 C++ können Sie eine Templat-make_wrapper Helferfunktion betrachten. Bei diesem Ansatz ist der Funktionszeiger jedoch kein Vorlagenparameter. Stattdessen wird der Funktionszeiger „getragen“ durch das nicht-statische Datenelement namens f_ im folgende Beispiel:

#include <iostream> 

void do_something(int value, double amount) { 
    std::cout << (value * amount) << std::endl; 
} 

void do_something_else(std::string const& first, double & second, int third) { 
    for(char c : first) 
    if(third/c == 0) 
     second += 13.7; 
} 

template<class Ret, class... Args> 
using function_pointer = Ret(*)(Args...); 

template<class Ret, class... Args> 
struct wrapper { 
    using F = function_pointer<Ret, Args...>; 

    F f_; 

    explicit constexpr wrapper(F f) noexcept : f_{f} {} 

    template<class... PreciseArgs>// not sure if this is required 
    Ret operator()(PreciseArgs&&... precise_args) const { 
    return f_(std::forward<PreciseArgs>(precise_args)...); 
    } 
}; 

template<class Ret, class... Args> 
constexpr auto make_wrapper(
    function_pointer<Ret, Args...> f 
) -> wrapper<Ret, Args...> { 
    return wrapper<Ret, Args...>(f); 
} 

int main() { 
    constexpr auto obj = make_wrapper(do_something); 
    obj(5, 17.4); 
    constexpr auto obj2 = make_wrapper(do_something_else); 
    double value = 5; 
    obj2("Hello there!", value, 70); 

    return 0; 
} 
Verwandte Themen