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";
}
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
@ 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. –
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