Ich versuche, einen Code zu schreiben, der eine als Vorlagenparameter angegebene Klassenmethode aufruft. Zur Vereinfachung kann man annehmen, dass die Methode einen einzigen Parameter (von beliebigem Typ) hat und void zurückgibt. Ziel ist es, Boilerplate auf der aufrufenden Seite zu vermeiden, indem der Parametertyp nicht eingegeben wird. Hier ist ein Codebeispiel:
template <class Method> class WrapMethod {
public:
template <class Object>
Param* getParam() { return ¶m_; }
Run(Object* obj) { (object->*method_)(param_); }
private:
typedef typename boost::mpl::at_c<boost::function_types::parameter_types<Method>, 1>::type Param;
Method method_;
Param param_
};
Jetzt kann ich in der aufrufenden Site die Methode verwenden, ohne den Typ des Parameters angeben zu müssen.
Foo foo;
WrapMethod<BOOST_TYPEOF(&Foo::Bar)> foo_bar;
foo_bar.GetParam()->FillWithSomething();
foo_bar.Run(foo);
Dieser Code funktioniert also, und ist fast das, was ich will. Das einzige Problem ist, dass ich den BOOST_TYPEOF-Makroaufruf auf der aufrufenden Seite loswerden möchte. Ich möchte gerne etwas schreiben können wie WrapMethod<Foo::Bar> foo_bar
anstelle von WrapMethod<BOOST_TYPEOF(&Foo::Bar)> foo_bar
.
Ich vermute, dass dies nicht möglich ist, da es keine Möglichkeit der Bezugnahme auf eine Methodensignatur andere als mit der Methodensignatur selbst (die eine Variable für WrapMethod ist, und etwas ziemlich groß an der aufrufenden Website eingeben) oder immer den Methodenzeiger und dann tun typeof.
Für Hinweise zur Behebung dieser Probleme oder für andere Ansätze zur Vermeidung der Eingabe des Parametertyps auf der aufrufenden Seite sind wir dankbar.
Nur um meine Bedürfnisse zu verdeutlichen: die Lösung darf nicht den Typename Param in der aufrufenden Seite haben. Außerdem kann sie FillWithSomething nicht innerhalb von WrapMethod (oder ähnlich) aufrufen. Da sich der Name der Methode von Typ Param zu Typ Param ändern kann, muss er in der aufrufenden Site stehen. Die Lösung, die ich gegeben habe, erfüllt diese beiden Einschränkungen, aber braucht die hässliche BOOST_TYPEOF in der aufrufenden Website (mit ihm innerhalb WrapMethod oder andere Umleitung wäre in Ordnung, da das ist Code meine api Benutzer nicht sehen, solange es korrekt ist).
Antwort:
Soweit ich das beurteilen kann, gibt es keine mögliche Lösung. Dies läuft darauf hinaus, dass es unmöglich ist, etwas zu schreiben wie WrapMethod<&Foo::Bar>
wenn die Signatur von Bar nicht im Voraus bekannt ist, auch wenn nur die Kardinalität erforderlich ist. Allgemeiner ausgedrückt: Sie können keine Template-Parameter haben, die Werte (nicht Typen) annehmen, wenn der Typ nicht festgelegt ist. Zum Beispiel ist es unmöglich, etwas zu schreiben wie typeof_literal<0>::type
was zu einer Auswertung von int
et typeof_literal<&Foo::Bar>::type
was zu folgenden Ergebnissen führen würde void (Foo*::)(Param)
in meinem Beispiel. Beachten Sie, dass weder BOOST_TYPEOF noch decltype helfen würden, weil sie in der Aufrufsite stehen müssen und nicht tiefer im Code vergraben werden können. Die legitime, aber ungültige Syntax unten würde das Problem lösen:
template <template<class T> T value> struct typeof_literal {
typedef decltype(T) type;
};
In C++0x kann man, wie in der ausgewählten Antwort (und in anderen, die BOOST_AUTO verwenden), das Schlüsselwort auto verwenden, um das gleiche Ziel auf andere Weise zu erreichen:
template <class T> WrapMethod<T> GetWrapMethod(T) { return WrapMethod<T>(); }
auto foo_bar = GetWrapMethod(&Foo::Bar);