Typelist / Code Generierung / Findet falsche Funktion
-
Hallo zusammen,
Während ich gerade Modern C++ Design lese, kommen mir allerhand Ideen, welche ich auch ausprobiere. Absolut geiles Buch!
Bei einer dieser Ideen ist mir ein Fehler an den Kopf geworfen worden, den ich schlicht und einfach nicht verstehe und gespannt bin, ob mir jemand diesen Fehler erklären kann.Ich verwende hier verschiedene Konstrukte aus der Loki-Bibliothek, bzw. entsprechend Nachgebaute und werde diese somit nicht hier nochmals aufführen und nur meinen Code reinsetzen.
Die Idee war eine Art von Event-System aufzubauen, welches über eine Typelist generiert wird.
EventListener.hpp
////////////////////////////////////////////////////////////////////////// // EventListener : Header file #ifndef EVENT_H_EVENT_LISTENER_GUARD__ #define EVENT_H_EVENT_LISTENER_GUARD__ #include "Typelist.hpp" template<typename ListT> class EventListener; template<> class EventListener<util::NullType> { }; template<typename HeadT, typename TailT> class EventListener<tl::Typelist<HeadT, TailT> > : public EventListener<TailT> { protected: virtual void on_event(HeadT&) = 0; }; #endif // EVENT_H_EVENT_LISTENER_GUARD__EventConsumer.hpp
////////////////////////////////////////////////////////////////////////// // EventConsumer : Header file #ifndef EVENT_H_EVENT_CONSUMER_GUARD__ #define EVENT_H_EVENT_CONSUMER_GUARD__ #include "Typelist.hpp" #include "EventListener.hpp" template<typename EventListT> class EventConsumer : public EventListener<EventListT> { private: typedef EventListT EventList; template<typename EventT> void handle_event(EventT& evt, util::Int2Type<true>) { on_event(evt); } template<typename EventT> void handle_event(EventT& evt, util::Int2Type<false>) { } public: template<typename EventT> void handle_event(EventT& evt) { static bool const DO_SELECT = tl::IndexOf<EventList, EventT>::RESULT >= 0; handle_event( evt, util::Select < DO_SELECT, util::Int2Type<true>, util::Int2Type<false> >::Result()); } }; #endif // EVENT_H_EVENT_CONSUMER_GUARD__main.cpp
////////////////////////////////////////////////////////////////////////// // main : Implementation file #include "Typelist.hpp" #include "EventConsumer.hpp" #include <string> #include <iostream> class TestWidget : public EventConsumer<M_TYPELIST_3(int, std::string, char)> { protected: virtual void on_event(int&) { std::cout << "int" << std::endl; } virtual void on_event(std::string&) { std::cout << "string" << std::endl; } virtual void on_event(char&) { std::cout << "char" << std::endl; } }; int main() { using namespace tl; TestWidget widget; int x; std::string l; char s; widget.handle_event(x); widget.handle_event(s); widget.handle_event(l); std::cin.get(); return 0; }Ergibt die zwei Fehler:
1>c:\...\eventconsumer.hpp(20) : error C2664: 'EventListener<ListT>::on_event' : cannot convert parameter 1 from 'char' to 'int &' 1> with 1> [ 1> ListT=tl::Typelist<int,tl::Typelist<std::string,tl::Typelist<char>>> 1> ] 1> c:\...\eventconsumer.hpp(42) : see reference to function template instantiation 'void EventConsumer<EventListT>::handle_event<EventT>(EventT &,util::Int2Type<V>)' being compiled 1> with 1> [ 1> EventListT=tl::Typelist<int,tl::Typelist<std::string,tl::Typelist<char>>>, 1> EventT=char, 1> V=1 1> ] 1> c:\...\main.cpp(44) : see reference to function template instantiation 'void EventConsumer<EventListT>::handle_event<char>(EventT &)' being compiled 1> with 1> [ 1> EventListT=tl::Typelist<int,tl::Typelist<std::string,tl::Typelist<char>>>, 1> EventT=char 1> ] 1>c:\...\eventconsumer.hpp(20) : error C2664: 'EventListener<ListT>::on_event' : cannot convert parameter 1 from 'std::string' to 'int &' 1> with 1> [ 1> ListT=tl::Typelist<int,tl::Typelist<std::string,tl::Typelist<char>>> 1> ] 1> c:\...\eventconsumer.hpp(42) : see reference to function template instantiation 'void EventConsumer<EventListT>::handle_event<EventT>(EventT &,util::Int2Type<V>)' being compiled 1> with 1> [ 1> EventListT=tl::Typelist<int,tl::Typelist<std::string,tl::Typelist<char>>>, 1> EventT=std::string, 1> V=1 1> ] 1> c:\...\main.cpp(45) : see reference to function template instantiation 'void EventConsumer<EventListT>::handle_event<std::string>(EventT &)' being compiled 1> with 1> [ 1> EventListT=tl::Typelist<int,tl::Typelist<std::string,tl::Typelist<char>>>, 1> EventT=std::string 1> ]Ich hoffe das ist nicht zu viel Code, aber ich glaube kaum, dass man das deutlich minimieren kann, um zu verstehen, was hier passiert.
Eine Lösung habe ich zwar gefunden, in dem ich EventListener ein wenig umgebaut habe, so dass ich direkt eine nicht virtuelle Funktion im richtigen EventListener aufrufen kann. Diese nicht virtuelle Funktion leitet den Event dann weiter an die virtuelle Funktion. Ist allerdings etwas umständlich, wie ich finde. Es wäre schöner, wenn die obige Lösung funktionieren würde

Ich hoffe jemand kann sich die Zeit nehmen und mir helfen. Bei Fragen fragen!
Grüssli
-
Ist wirklich komisch. Wenn man on_event public macht und dann von außen aufruft, so funktioniert es - innerhalb der Template-Methode nicht. Es nützt auch nichts, wenn man in EventConsumer alle Methoden konkret hinschreibt (was natürlich nicht im Sinne des Erfinders ist, aber so als Test)
Was für einen Compiler benutzt Du? Ich habe mit VC9 express genau das von Dir beschriebene Verhalten.
Gruß
Werner
-
VC9 SP1 Team Suite, also wohl den gleichen Kompiler wie du.
In der Theorie sollte die Funktion aber gefunden werden, oder? Also ist das ein Kompilerfehler?Grüssli
-
Ist doch logisch, eine Elternklasse kann doch nicht die Methoden des Kinds aufrufen

edit: Upsi, hab die Klasse EventListener glatt übersehen...

-
Jemand noch eine Idee? Kann das vielleicht jemand mit einem anderen Kompiler testen? Ansonsten auch nicht so wichtig, wäre vielleicht höchstens für ein Bug-Report an Microsoft interessant, falls dies wirklich ein Bug ist.
Danke für die Mühen.
Grüssli
-
Badestrand schrieb:
Ist doch logisch, eine Elternklasse kann doch nicht die Methoden des Kinds aufrufen

Mit CRTP geht das wunderbar

http://en.wikipedia.org/wiki/Curiously_Recurring_Template_Pattern
-
template<typename HeadT, typename TailT> class EventListener<tl::Typelist<HeadT, TailT> > : public EventListener<TailT> { protected: virtual void on_event(HeadT&) = 0; };Das Problem ist hier die Vererbung. Du versucht über Vererbung zu überladen. Bei der ersten Instanzierung hast du ein on_event(int&) und versucht durch die Vererbung weitere reinzuholen, doch das lokale, also das erste on_event(int&) überdeckt die anderen.
Überladen funktioniert nicht über Klassen hinweg. Das endet immer bei Überdeckung ...
Edit: Dies ist auch der Grund für deine Fehlermeldung. Der Compiler kennt nur das on_event für int&.
-
@KasF,
Stimmt, habe es inzwischen auch im Standard 98 gefunden:
13.2 Abschnitt 1
Überladung geht nur, wenn die Funktionen im selben "Scope" sind. Eine Memberfunktion in einer Elternklasse ist nicht im gleichen "Scope" wie eine Memberfunktion in einer Kindklasse.Mist.
Dann ist meine zweite Lösung, welche ich hinbekommen habe, doch die einzige Möglichkeit. Ich füge sie nur zur Vollständigkeit noch dazu:
EventListener.hpp
////////////////////////////////////////////////////////////////////////// // EventListener : Header file #ifndef EVENT_H_EVENT_LISTENER_GUARD__ #define EVENT_H_EVENT_LISTENER_GUARD__ #include "Typelist.hpp" template<typename EventT> class EventListener { protected: virtual void on_event(EventT&) = 0; void on_event(EventT& evt, util::Type2Type<EventT>) { on_event(evt); } }; template<typename ListT> class EventListenerCreator; template<> class EventListenerCreator<util::NullType> { }; template<typename HeadT, typename TailT> class EventListenerCreator<tl::Typelist<HeadT, TailT> > : public EventListenerCreator<TailT> , public EventListener<HeadT> { }; #endif // EVENT_H_EVENT_LISTENER_GUARD__////////////////////////////////////////////////////////////////////////// // EventConsumer : Header file #ifndef EVENT_H_EVENT_CONSUMER_GUARD__ #define EVENT_H_EVENT_CONSUMER_GUARD__ #include "Typelist.hpp" #include "EventListener.hpp" template<typename EventListT> class EventConsumer : public EventListenerCreator<EventListT> { private: typedef EventListT EventList; template<typename EventT> void handle_event(EventT& evt, util::Int2Type<true>) { EventListener<EventT>::on_event(evt, util::Type2Type<EventT>()); } template<typename EventT> void handle_event(EventT& evt, util::Int2Type<false>) { } public: template<typename EventT> void handle_event(EventT& evt) { static bool const DO_SELECT = tl::IndexOf<EventList, EventT>::RESULT >= 0; handle_event( evt, util::Select < DO_SELECT, util::Int2Type<true>, util::Int2Type<false> >::Result()); } }; #endif // EVENT_H_EVENT_CONSUMER_GUARD__Wichtige Unterschiede:
EventListenerist ein Klassentemplate, welches als Templateparameter nur den entsprechenden Eventtyp erwartet. Dadurch ist es leicht die richtige Basisklasse anzusprechen.EventListenerCreatorist nun zuständige für die Erstellung der Hierarchie.EventConsumer::handle_event(EventT, util::Int2Type<true>)leitet den Event zuerst in den richtigen Scope um, damit die Überladung funktioniert.
Grüssli