Function pointer einer Methode übergeben
-
Hallo,
ich möchte aus einem Objekt factory die Methode getNumberInt() einer Funktion übergeben. Allerdings bekomme ich eine Fehlermeldung, da ich nicht genau weiß, wie ich die Methode in der Funktion doSomething() genau angeben soll:
void doSomething<int>(T (__cdecl *)(void))' : cannot convert argument 1 from 'int' to 'int (__cdecl *)(void)'
DataFactory.h:
class DataFactory { public: DataFactory(); ~DataFactory(); int getNumberInt(); };
DataFactory.cpp:#
int DataFactory::getNumberInt() { return 5; }
main.cpp:
template <typename T> void void doSomething(T(*fp)()) { T var; var = (*fp)(); } DataFactory factory; doSomething<int>(factory.getNumberInt());
Wie muss ich denn "factory.getNumberInt()" angeben, damit ich keine Fehlermeldung mehr bekomme?
-
Probiere es mal bitte ohne die Klammern. Die machen daraus nämlich einen Funktionsaufruf.
doSomething<int>(factory.getNumberInt);
-
anno schrieb:
doSomething<int>(factory.getNumberInt());
Du übergibst einen int, aber die Funktion erwartet nen Funktionszeiger.
-
Ich habe es mal kurz etwas umgeschrieben:
class MyClass { public: template <typename T> void doSomething(T(*fp)()) { T var; var = (*fp)(); } };
main.cpp:
DataFactory factory; MyClass myclass; myclass.doSomething<int>(factory.getNumberInt);
Jetzt bekomem ich die Meldung:
'DataFactory::getNumberInt': function call missing argument list; use '&DataFactory::getNumberInt' to create a pointer to member
Wie muss ich die Methode hier übergeben?
-
Hallo,
@no function, leider nicht ganz richtig, ein kleines Detail stimmt nicht.
Die Funktion
doSomething
erwartet einen Funktionszeiger,&factory.getNumberInt
hat jedoch den Typ Methodenzeiger.Als ersten Schritt schlage ich vor,
doSomething
folgendermassen zu ändern:template<typename T> void doSomething(T Fnc) { auto var = Fnc(); }
Damit akzeptiert
doSomething
nicht nur Funktionen sondern auch Funktoren und Lambdas.std::function
ist hier eine unnötige Verlangsamung, das Duck-Typing erledigt die Sache auch.Wie übergeben wir nun die Methode dieser Funktion? Nun, hier bietet sich
std::function
theoretisch erneut an, ich bevorzuge jedoch eine triviale Lösung, die ohne Type-Erasure (und daher auch ohne dynamischem Speicher) auskommt:template<typename MethodType> struct MethodTraits; template<typename ClassType, typename ReturnType> struct MethodTraits<ReturnType(ClassType::*)()> { typedef ReturnType ReturnT; }; template<typename ClassType, typename ReturnType> struct MethodTraits<ReturnType(ClassType::*)() const> { typedef ReturnType ReturnT; }; template<typename ClassType, typename ReturnType> struct MethodTraits<ReturnType(ClassType::*)() volatile> { typedef ReturnType ReturnT; }; template<typename ClassType, typename ReturnType> struct MethodTraits<ReturnType(ClassType::*)() const volatile> { typedef ReturnType ReturnT; }; template<typename ClassType, typename MethodType> class MethodBinder { ClassType* Instance; MethodType MethodPtr; public: explicit MethodBinder(ClassType* Instance, MethodType MethodPtr) : Instance(Instance), MethodPtr(MethodPtr) { } typename MethodTraits<MethodType>::ReturnT operator() () const { return (this->Instance->*(this->MethodPtr))(); } }; template<typename ClassType, typename MethodType> MethodBinder<ClassType, MethodType> BindMethod(ClassType* Instance, MethodType MethodPtr) { return MethodBinder<ClassType, MethodType>(Instance, MethodPtr); }
(Dieses Konzept lässt sich auf beliebig viele Argumente erweitern.)
Damit lässt sich der Funktionsaufruf wie folgt schreiben:
doSomething(BindMethod(&factory, &DataFactory::getNumberInt));
Getestet: http://ideone.com/vFk3Eo
-
Sowas nennt man dann std::bind. Oder Lambdas.
-
asfdlol schrieb:
Hallo,
@no function, leider nicht ganz richtig, ein kleines Detail stimmt nicht.
Ich habe in der neusten Variante die Function als Methode in einer Klasse neu implementiert.
class MyClass { public: template <typename T> doSomething(T Fnc) { auto var = Fnc(); } };
Damit funktioniert der MethodBinder leider nicht mehr:
int MyClass::doSomething<int>(T)' : cannot convert argument 1 from 'MethodBinder<DataFactory,int (__thiscall DataFactory::* )(void)>' to 'int
-
Kellerautomat schrieb:
Sowas nennt man dann std::bind. Oder Lambdas.
Wie dumm von mir. Du hast recht, wenn ich schon
auto
nutze, dann kann ich auch gleich die restlichen C++11-Features nehmen.anno schrieb:
[...]
Das liegt höchstwahrscheinlich daran, dass du das Template-Argument von
doSomething
immer noch manuell mit<int>
angibst. Lass es den Compiler selbst deduzieren (indem du die spitzen Klammern samt Inhalt beim Aufruf vondoSomething
weglässt). Damit sollte sich das auch erledigen.