2016-04-10 2 views
1

Ich möchte einen Wrapper um eine allgemeine Funktion, T0 f (T1, T2, T3 ...) automatisch, die Argumente der Funktion aus Strings auf einem Stapel (konvertiert zu den entsprechenden Typen) und tut etwas mit dem Rückgabewert. Mein ideales Ergebnis wird realisiert werden, wenn ich so etwas wie der folgenden Pseudo-ish Code tun können:Wickeln Sie eine allgemeine Funktion, um ihre Argumente aus einer Zeichenfolge

#include <stack> 
std::stack<char*> argStack; 
std::stack<char*> retStack; 


int add(float a, float b){ 
    return a+b; 
} 
int main(){ 
    argStack.push((char*)"2"); 
    argStack.push((char*)"5"); 
    auto wrapped=funcWrap(add); 
    wrapped(); 
    std::cout << retStack.top(); 
    return 0; 
} 

und dies wird „7“ auf stdout drucken.

Vor einiger Zeit habe ich ein paar Tage im SO suchen und kam mit so etwas wie die folgenden (übersetzbar) bis:

#include <iostream> 
#include <stack> 
#include <string> 
#include <sstream> 
#include <tuple> 
#include "apply_tuple.h" 
using std::cout; 
using std::endl; 
using std::string; 
std::stack<string> argStack; 
std::stack<string> retStack; 

template<typename T> 
T getNextArg(){ 
    std::stringstream ss; 
    ss.str(argStack.top()); 
    argStack.pop(); 
    T convertedVar; 
    ss >> convertedVar; 
    return convertedVar; 
} 

template <typename Fn, typename funcSig> struct funcWrap; 

template <typename Fn, typename R, typename ...Args> 
struct funcWrap<Fn, R(Args...)>  // specialized for typename = R(Args...) 
{ 
    using funcType= Fn; 
    funcType *wrappedFunc; 

    funcWrap(funcType &inputFunc){ 
     this->wrappedFunc=&inputFunc; 
    } 
    void operator()(){ 
     auto tup =std::make_tuple(getNextArg<Args>()...); 
     std::stringstream ss; 
     ss << apply_tuple(*wrappedFunc, tup); 
     retStack.push(ss.str()); 
    } 
}; 

int add(float a, float b){ 
    return a+b; 
} 

int main(){ 
    argStack.push(string("2")); 
    argStack.push(string("5")); 
    auto wrapped=funcWrap<decltype(add), decltype(add)>(add); 
    wrapped(); 
    cout << retStack.top(); 
    return 0; 
} 

Wo apply_tuple von https://www.preney.ca/paul/archives/1099 ist. Es ruft einfach den Funktionszeiger mit den Argumenten auf, die im gelieferten Tupel enthalten sind.

Ich habe eine Züchter-Version dieses Musters für eine Weile verwendet (meist um automatisch eine Schnittstelle zu Debug-Code auf einem Arduino zu generieren), und es funktioniert gut, aber es hat 2 große Probleme, die ich mich prügele up versuchen, zu beheben: es ist redundant (in dem ich den Funktionsnamen 3 Mal im Wrapping-Prozess schreiben), und es funktioniert nicht für Member-Funktionen. Kann man das beheben?

Antwort

2

Es scheint, dass Ihr Hauptproblem das Erstellen des Funktionsobjekts ist. Der einfachste Ansatz, die wiederholte Spezifikation der Funktion zu vermeiden und die Elementfunktionen zu überdecken, besteht darin, eine Factory-Funktion zu verwenden, die die relevanten Typen ableitet. Durch entsprechendes Überladen dieser Funktion kann leicht zwischen normalen und Elementfunktionen unterschieden werden. Ich nehme an, die doppelte Verwendung des Funktionstyps ist für Funktionsobjekte, bei denen sich der Typ der Darstellung und die Signatur tatsächlich unterscheiden.

template <typename R, typename... A> 
funcWrap<R(A...), R(*)(A...)> 
wrap(R(*fun)(A...)) { // deal with functions 
    return funcWrap<R(A...), R(*)(A...)>(fun); 
} 
template <typename Signature, typename F> 
funcWrap<Signature, F> 
wrap(F&& f) { // deal with function objects 
    return funcWrap<Signature, F>(std::forward<F>(f)); 
} 
template <typename R, typename S, typename... A> 
funcWrap<R(S&, A...), R (S::*)(A...)> 
wrap(R(S::*mem)(A...)) { 
    return funcWrap<R(S&, A...), R (S::*)(A...)>(mem); 
} 

würden Sie verwenden die wrap() Funktion wie diese

float add(float, float); 
struct adder { float operator()(float, float); }; 
struct foo { foo mem(foo); }; 

int main() { 
    auto wfun = wrap(&add); 
    auto wobj = wrap<float(float, float)>(adder()); 
    auto wmem = wrap(&foo::mem); 
} 

Sie werden mit heraufbeschwört ein geeignetes Objekt beschäftigen müssen auf Ihrem Mitglied anrufen und Sie müssen wahrscheinlich einige Spezialisierungen Ihrer Funktionswrapper funcWrap. Für den Member-Funktionsfall müssen Sie möglicherweise auch geeignete Überladungen für const Elementfunktionen haben. Für Member-Funktionen kann es auch sinnvoll sein, das Objekt beim Umbrechen des Members anzugeben und entsprechend zu erfassen. Die Verwendung von std::bind() oder Lambda-Funktion-Member-Funktionen könnte auch mit dem Wrapper für die Funktionsobjekte gebunden werden, aber dies erfordert höchstwahrscheinlich die Angabe der Signatur, die ansonsten abgeleitet werden kann.

Nachfolgend finden Sie eine komplette Demo mit C++ 11 Kompilieren aller erforderlich, um die Bits und Bobs zeigt diese Umhüllung Arbeit zu machen:

#include <iostream> 
#include <sstream> 
#include <stack> 
#include <string> 
#include <stdexcept> 
#include <tuple> 
#include <type_traits> 
#include <utility> 
#include <cstddef> 

// ---------------------------------------------------------------------------- 

template <typename T> 
typename std::decay<T>::type makeArg(std::stack<std::string>& args) { 
    typename std::decay<T>::type rc; 
    if (args.empty() || !(std::istringstream(args.top()) >> rc)) { 
     throw std::runtime_error("can't create argument from '" + (args.empty()? "<empty>": args.top()) + "'"); 
    } 
    args.pop(); 
    return rc; 
} 

// ---------------------------------------------------------------------------- 

namespace util 
{ 
    template<typename T, T...> 
    struct integer_sequence { 
    }; 
    template<std::size_t... I> 
    using index_sequence = integer_sequence<std::size_t, I...>; 

    template <typename T, std::size_t N, T... I> 
    struct integer_sequencer { 
     using type = typename integer_sequencer<T, N - 1, N - 1, I...>::type; 
    }; 
    template <typename T, T... I> 
    struct integer_sequencer<T, 0, I...> { 
     using type = integer_sequence<T, I...>; 
    }; 

    template<typename T, T N> 
    using make_integer_sequence = typename integer_sequencer<T, N>::type; 
    template<std::size_t N> 
    using make_index_sequence = make_integer_sequence<std::size_t, N>; 

    template <typename F, typename T, std::size_t... I> 
    auto apply_aux(F&& fun, T&& tuple, index_sequence<I...>) -> decltype(fun(std::get<I>(tuple)...)) { 
     return fun(std::get<I>(tuple)...); 
    } 

    template <typename F, typename T> 
    auto apply(F&& f, T&& t) 
     -> decltype(apply_aux(std::forward<F>(f), std::forward<T>(t), make_index_sequence<std::tuple_size<typename std::decay<T>::type>::value>())) { 
     return apply_aux(std::forward<F>(f), std::forward<T>(t), make_index_sequence<std::tuple_size<typename std::decay<T>::type>::value>()); 
    } 
} 

// ---------------------------------------------------------------------------- 

template <typename S, typename F> class funcWrap; 

template <typename R, typename... A, typename F> 
class funcWrap<R(A...), F> { 
private: 
    F fun; 

public: 
    funcWrap(F fun): fun(fun) {} 

    std::string operator()(std::stack<std::string>& args) { 
     std::tuple<typename std::decay<A>::type...> t{ makeArg<A>(args)... }; 
     std::ostringstream out; 
     out << util::apply(this->fun, t); 
     return out.str(); 
    } 
}; 

template <typename R, typename... A, typename S, typename... B> 
class funcWrap<R(A...), R (S::*)(B...)> { 
private: 
    R (S::*mem)(B...); 

public: 
    funcWrap(R (S::*mem)(B...)): mem(mem) {} 

    std::string operator()(std::stack<std::string>& args) { 
     std::tuple<typename std::decay<A>::type...> t{ makeArg<A>(args)... }; 
     std::ostringstream out; 
     out << util::apply([=](S& s, B... b){ return (s.*(this->mem))(b...); }, t); 
     return out.str(); 
    } 
}; 

// ---------------------------------------------------------------------------- 

template <typename R, typename... A> 
funcWrap<R(A...), R(*)(A...)> 
wrap(R(*fun)(A...)) { // deal with functions 
    return funcWrap<R(A...), R(*)(A...)>(fun); 
} 
template <typename Signature, typename F> 
funcWrap<Signature, F> 
wrap(F&& f) { // deal with function objects 
    return funcWrap<Signature, F>(std::forward<F>(f)); 
} 
template <typename R, typename S, typename... A> 
funcWrap<R(S&, A...), R (S::*)(A...)> 
wrap(R(S::*mem)(A...)) { 
    return funcWrap<R(S&, A...), R (S::*)(A...)>(mem); 
} 

float add(float f0, float f1) { return f0 + f1; } 
struct adder { 
    float value; 
    explicit adder(float value): value(value) {} 
    float operator()(float f0, float f1) { 
     return value + f0 + f1; 
    } 
}; 
struct foo { 
    float value; 
    foo(): value() {} 
    explicit foo(float value): value(value) {} 
    foo mem(foo f) { return foo(value + f.value); } 
}; 

std::istream& operator>> (std::istream& in, foo& f) { 
    return in >> f.value; 
} 
std::ostream& operator<< (std::ostream& out, foo const& f) { 
    return out << f.value; 
} 

int main() { 
    std::stack<std::string> args; 

    auto wfun = wrap(&add); 
    args.push("17.5"); 
    args.push("42.25"); 
    std::cout << "wfun result=" << wfun(args) << "\n"; 

    auto wobj = wrap<float(float, float)>(adder(3.125)); 
    args.push("17.5"); 
    args.push("42.25"); 
    std::cout << "wobj result=" << wobj(args) << "\n"; 

    auto wmem = wrap(&foo::mem); 
    args.push("17.5"); 
    args.push("42.25"); 
    std::cout << "wmem result=" << wmem(args) << "\n"; 
} 
+0

noch nicht bekommen diese (gcc 4.8, mit -c + zu kompilieren + 1y), trotz Korrektur einiger Tippfehler (zB operator() (float, float) für den Addierer). Dein Ansatz macht aber für mich durchaus Sinn. Es sind nur die Details, die, wie bei Templates üblich, witzige Dinge in meinem Gehirn machen. – user3849418

+0

@ user3849418: Ja, es gab ein paar Tippfehler: Ich hatte den Code zusammen auf einem Mobiltelefon zusammengeschustert. Ich habe eine vollständige Implementierung hinzugefügt, die zeigt, dass dieser Ansatz funktioniert. –

+0

Oh wow. Ich bin ... in Ehrfurcht. Dies kompiliert und macht genau das, was ich gefragt und benötigt habe. Ich werde nicht so tun, als ob ich es jetzt vollständig verstehe, aber ich werde daran arbeiten und ich vermute, dass ich dabei eine Menge lernen werde. Vielen Dank. – user3849418

Verwandte Themen