Problem mit Vector von Funktoren
-
Ich hätte gerne einen Vektor von Funktoren, die aber jeweils auf unterschiedliche Memberfunktionen von unterschiedlichen Klassen zeigen können bzw. evtl auch auf eine normale Funktion. Bedingung dabei ist natürlich, dass Rückgabewert und Parameterliste jeweils gleich ist. Hier erstmal mein Testcode:
#include <iostream> #include <vector> void test() { std::cout << "test()" << std::endl; } class Test { public: void test() { std::cout << "Test::test()" << std::endl; } }; class Test2 { public: void test() { std::cout << "Test2::test()" << std::endl; } }; class Functor { public: Functor(void (* _func)()) : Object(0), Fun(_func) {} template<class C> Functor(C* _o, void (C::* _m)()) : Object(_o) {} void operator()() { if (!Object) { // normal function Fun(); } else { // call object's memberfunction } } private: void* Object; void (* Fun)(); }; int main() { Test t; Test2 t2; std::vector<Functor> vec; vec.push_back( Functor(&test) ); vec.push_back( Functor(&t, &Test::test) ); vec.push_back( Functor(&t2, &Test2::test) ); for (int i = 0; i < vec.size(); i++) { vec[i](); } return 0; }So ungefähr hätte ich es gerne, wenn es denn möglich ist. Mein Problem ist, dass ich nicht weiß, wie ich den Zeiger auf die Memberfunktion in den Funktor bekomme und als nächstes wie ich diese dann mit dem Zeiger auf das Objekt aufrufe.
Ich habe es hier jetzt mal versucht nur den Konstruktor des Funktors als Template zu realisieren, da ich ansonsten das Problem habe, dass ich die Template-Parameter bei der Definition des Vektors angeben muss und ich somit nicht Memberfunktionen von verschiedenen Klassen in dem Vektor speichern kann (und normale Funktionen würde wegfallen, was aber nicht sooo tragisch wäre). Einzige Möglichkeit wäre hier dann (soweit ich weiß) den Functor vom Typen einer Basisklasse zu nehmen und in der Zuweisung auf die Basisklasse zu casten, was aber voraussetzen würde, dass alle Memberfunktionen von Klassen sind, die von einer Basisklasse abgeleitet werden, was ich aber nicht will.
Hat da jmd. eine Idee?
-
Erstmal kannst du eine "normale" Funktion und eine Memberfunktion nicht in den selben Typ speichern (letztere müsstest du in (C::*)() (oder so ähnlich) speichern) und zweitens weiß ich nicht einmal, ob deine Designidee überhaupt realisierbar ist - Wozu soll das überhaupt sinnvoll sein?
-
Das ganze soll mir als Callback-Mechanismuns dienen. Ich möchte z.B. eine Klasse haben:
class EventHandler { public: enum EventType { onSize, onClose, }; void addListener(EventType _e, Functor _f) { EventTable[_e].push_back(_f); } void notifyListener(EventType _e) { for (int i = 0; i < EventTable[_e].size(); i++) { EventTable[_e][i](); } } private: std::map< EventType, std::vector<Functor> > EventTable; };also so in der Art.
Zur weiteren Erläuterung:
Ich bastle für mich zu Lernzwecken an einem kleinen GUI-Toolkit, womit ich mir einige Dinge klarmachen möchte.Bisher habe ich das so gelöst, dass ich die Klasse Functor komplett als Template realisiert habe und auf eine Basisklasse caste, wie gesagt möchte ich das aber nicht mehr. Es soll egal sein, was das für eine Klasse ist, dessen Memberfunktion aufgerufen werden soll.
Ein Lösung, die mir folgendes erlauben würde wäre auch schön:
Klasse1 k; Klasse2 k2; std::vector< Functor<Klasse1> > vec; vec.push_back( Functor(&k, &Klasse1::test) ); vec.push_back( Functor(&k2, &Klasse2::test) );Problem hierbei ist allerdings, dass mir beim zweiten push_back eine Meldung ausgeworfen wird, dass die Methode push_back für Klasse2 nicht existiert, was auch klar ist, weil ich den Vektor ja als Funktor von Klasse1 erstellt habe.
-
Ich glaube, da kommst du um deine Lösung nicht herum - eventuell noch mit ein paar Hilfsfunktionen drumherum:
class callback { public: virtual void call() = 0; virtual ~callback() {} } template<typename T> class mem_caller_t : public callback { public: mem_caller_t(T& ob, void (T::*mem)()) : m_ob(ob),m_mem(mem) {} void call() { m_ob.*m_mem(); } private: T& m_ob; void (T::*m_mem)(); } template<typename T> mem_caller_t* mem_caller(T& ob,void(T::*mem)()) { return new mem_caller_t(ob,mem); } ... vector<callback*> vec; vec.push_back(mem_caller(k1,&Klasse1::test)); vec.push_back(mem_caller(k2,&Klasse2::print)); ... for(int i=0;i<vec.size();++i) vec[i]->call();(und mit einer zweiten Klasse glob_caller_t kannst du dort sogar normale Funktionen integrieren)
-
Schau dir mal Functor aus Loki an
-
LokiFunctor schrieb:
Schau dir mal Functor aus Loki an
Noch besser ist die praxis-erprobte Variante aus der boost-Library. Hier findet mantiz alles was er braucht.
Gruß
WernerEdit: Doch nicht 'alles'. boost.bind braucht man auch noch. Dann bleiben aber keine Wünsche in Sachen Callback mehr offen.
-
LokiFunctor schrieb:
Schau dir mal Functor aus Loki an
Werner Salomon schrieb:
LokiFunctor schrieb:
Schau dir mal Functor aus Loki an
Noch besser ist die praxis-erprobte Variante aus der boost-Library. Hier findet mantiz alles was er braucht.
Gruß
WernerDanke, ich werd's mir auf jeden Fall mal ansehen, allerdings geht es mir ja gerade darum zu lernen und nicht irgendwelche Bibliotheken zu verwenden.

Ich hab's jetzt auf die Schnelle so umgesetzt, muss ich aber noch 'in schön' machen:
#include <iostream> #include <map> #include <vector> void test() { std::cout << "test()" << std::endl; } class Test { public: void test() { std::cout << "Test::test()" << std::endl; } }; class Test2 { public: void test() { std::cout << "Test2::test()" << std::endl; } }; class FunctorBase { public: virtual void operator()() = 0; virtual ~FunctorBase() {} }; template<class C> class FunctorM : public FunctorBase { public: FunctorM(C* _o, void (C::* _m)()) : Object(_o), Method(_m) {} virtual void operator()() { (*this->Object.*this->Method)(); } private: C* Object; void (C::* Method)(); }; class FunctorF : public FunctorBase { public: FunctorF(void (* _fun)()) : Fun(_fun) {} virtual void operator()() { (*this->Fun)(); } private: void (* Fun)(); }; class EventHandler { public: enum EventType { onSize, onClose, }; void addListener(EventType _e, void (* _f)()) { EventTable[_e].push_back(new FunctorF(_f)); } template<class C> void addListener(EventType _e, C* _o, void (C::* _m)()) { EventTable[_e].push_back(new FunctorM<C>(_o, _m)); } void notifyListener(EventType _e) { for (int i = 0; i < EventTable[_e].size(); i++) { (*EventTable[_e][i])(); } } private: typedef std::map<EventType, std::vector<FunctorBase*> > EventTableType; EventTableType EventTable; }; int main() { Test t; Test2 t2; EventHandler evt; evt.addListener(EventHandler::onSize, &test); evt.addListener(EventHandler::onSize, &t, &Test::test); evt.addListener(EventHandler::onClose, &t2, &Test2::test); evt.notifyListener(EventHandler::onSize); evt.notifyListener(EventHandler::onClose); return 0; }
-
Ja, so geht's auch
Allerdings solltest du die Namen mit _ besser dem Compiler überlassen (da könnte es Probleme geben).(und du brauchst noch einen Dtor für deinen EventHandler, der die ganzen per new erzeugten Funktor-Objekte wieder freigibt).
-
CStoll schrieb:
Ja, so geht's auch
Allerdings solltest du die Namen mit _ besser dem Compiler überlassen (da könnte es Probleme geben).Oh, das ist mir neu. Ich finde das so eigentlich ganz nett, um die Parameter besser von anderen Variablen zu unterscheiden.
Dann muss ich mir da was anderes einfallen lassen. 
CStoll schrieb:
(und du brauchst noch einen Dtor für deinen EventHandler, der die ganzen per new erzeugten Funktor-Objekte wieder freigibt).
Ja, der kommt noch, das war bei 'in schön machen' mitinbegriffen.

Vielen Dank für die Hilfe.
