Dynamisch Casten bei rein virtuelle Basisklasse



  • Ich habe trotzdem noch ein Problem:
    Ich habe wieder folgende Klassen:

    class Manipulator : public unary_function<string,bool>{
      public:
        virtual bool operator() (string& s) = 0;
        ...
    };
    
    class Contains : public Manipulator{
      public:
        virtual bool operator() (string& s)
        {
          ...        
        }
        ...
    };
    

    Ich möchte einen Zeiger auf einen solchen Manipulator, der sozusagen noch alles werden kann. Gut, rein virtuelle Klassen können nicht instanziert werden, aber ein Pointer ist ja keine Instanzierung.

    Manipulator* m;
    m = new Contains("hello");
    
    find_if(vec.begin(),vec.end(),*m)  //<--- Erwartungsgemäß Fehler!
    

    Die Zuweisung klappt auch erwartungsgemäß, nur bei der Anwendung habe ich dann natürlich ein Problem:
    Ich muss m dereferenzieren und das bekommen, auf das der Pointer zeigt. Nur kann ja solch ein Pointer nicht auf ein Objekt zeigen, da sein Typ ja rein virtuell ist, es solche Objekte also gar nicht gibt.
    Ich müsste also casten, kann aber nicht direkt auf Contains oder ähnliches casten, da ich dort den genauen Typ von m nicht kenne.
    Ich habe bereits probiert mit

    *(dynamic_cast< unary_function<string,bool>* >(m)))
    

    auf die letzte gemeinsame, nicht rein virtuelle Basisklasse zu casten, das beschert mir jedoch ein "Call of nonfunction" in _algobase.c
    Liegt das daran, dass operator() dann nicht gefunden wird?
    Wie kann ich das Problem lösen?



  • #include <boost/bind.hpp>
    
    class Manipulator : public unary_function<string,bool>{
    public:
    	virtual bool operator() (string& s) = 0;
    };
    
    class Contains : public Manipulator{
    public:
    	Contains(const string& str)
    		: param(str)
    	{
    	}
    	virtual bool operator() (string& s)
    	{
    		cout << "hello " << s << endl;
    		return true;
    	}
    
    	string param;
    };
    
    int main()
    {
    	vector<string> vec;
    	vec.push_back("hello");
    	Manipulator* m;
    	m = new Contains("hello");
    
    	find_if(vec.begin(),vec.end(),boost::bind(&Manipulator::operator(),m,_1));
    }
    

    alternativ kannst du auch die binder der stl (bind1st, bind2nd) in Verbindung mit mem_fun mem_fun_ref mem_fun_ptr verwenden, ist jedoch nicht so komfortabel wie boost::bind



  • Wieso mem_fun hier? Ich hab doch ein Funktionsobjekt.
    Könntest du ein Beispiel ohne boost machen?



  • Das Problem an der Sache ist du kannst das Manipulator-Objekt ja nicht direkt ansprechen, also benötigst du eine Komponente zwischen find_if und deinem Manipulator, dieses Bindeglied soll den string von find_if an Manipulator::operator() weiterleiten.

    Dazu benötigt er das Objekt m und die entsprechende Funktion f (welche operator()-darstellt), der Aufruf sieht dann wie folgt aus: (m->*f)(derString).

    Nen kleines Binder-Beispiel für dein Anwendungsfall:

    template< class Obj, typename Func >
    struct Binder 
    {
    	Obj obj;
    	Func func;
    
    	Binder( Obj obj, Func func )
    		: obj( obj ), func( func )
    	{
    	}
    
    	bool operator()(string& str)
    	{
    		return (obj->*func)(str);
    	}
    };
    
    int main()
    {
    	vector<string> vec;
    	vec.push_back("hello");
    	Manipulator* m;
    	m = new Contains("hello");
    
    	find_if(vec.begin(),vec.end(), Binder< Manipulator*, bool(Manipulator::*)(string&)>(m, &Manipulator::operator()) );
    }
    

    hoffe du versteht jetzt das bestehende problem



  • Abgesehen davon, dass das Beispiel bei mirnicht funktioniert(viele Fehler der letzten Zeile), glaube ich das Problem nun teilweise verstanden zu haben.
    Könnte man nicht das ganze ohne das Hilfstemplate Binder machen?

    Aber gut, den Aufruf (obj->*func)(str) muss man in eine Klasse schreiben mit oeprator(), sonst haben wir wieder kein Funktionsobjekt. Aber ginge hier nicht mit mem_fun & Co und dafür ohne Binder?



  • warum das bei dir nicht geht, kann ich ohne fehlerbeschreibung schlecht nachvollziehen, aber um zu deiner zweiten frage zu kommen, geht sicherlich, aber ich kann dir da nicht helfen, ich verwende nur boost::bind



  • warum das bei dir nicht geht, kann ich ohne fehlerbeschreibung schlecht nachvollziehen

    Kein Problem...

    [C++ Error] main.cpp(195): E2272 Identifier expected
    [C++ Error] main.cpp(195): E2188 Expression syntax
    [C++ Error] main.cpp(195): E2299 Cannot generate template specialization from 'Binder<Obj,Func>'
    [C++ Error] main.cpp(196): E2121 Function call missing )
    [C++ Error] main.cpp(196): E2379 Statement missing ;
    

    Ich fürchte er kriegt bool(Manipulator::*)(string&) nicht gebacken.
    Ich guckmal, ob ich's mit mem_fun hinbekomme
    bzw. ptr_fun



  • versuchs mal mit

    template< class Ret, class Class, class Arg >
    struct Binder
    {
    	Class* obj;
    	typedef Ret(Class::*mfunc)(Arg);
    	mfunc func;
    
    	Binder( Class* obj, mfunc func )
    		: obj( obj ), func( func )
    	{
    	}
    
    	bool operator()(string& str)
    	{
    		return (obj->*func)(str);
    	}
    };
    
    int main()
    {
    	vector<string> vec;
    	vec.push_back("hello");
    	Manipulator* m;
    	m = new Contains("hello");
    
    	find_if(vec.begin(),vec.end(), Binder< bool, Manipulator, string& >(m, &Manipulator::operator()) );
    }
    


  • Ja, nun geht es. Sieht auch schon viel besser aus und man versteht den Code deutlich besser...

    Vielen, vielen Dank!



  • Also mit
    mem_fun(&Manipulator::operator())
    oder
    pointer_to_unary_function<string,bool>(&Manipulator::operator() )
    geht es auch nich. Noch jemand ne Idee?



  • Dein Problem ist, daß die Funktionsobjekte von find_if standardmäßig per Wert übergeben werden - und eine abstrakte Klasse kannst du nicht per Wert übergeben. Aber du kannst explizit die für deine Zwecke richtige Version von find_if erzeugen lassen:

    Manipulator* m=new Contains(...);
    
    typedef vector<string>::iterator vecit;
    
    vecit pos=find_if<vec_it,Manipulator&>(vec.begin(),vec.end(),*m);
    

    (Referenzen auf abstrakte Objekte dürfen übergeben werden)



  • Also g++ moniert immer noch, dass Manipulator instanziert wird:

    stl_algo.h: In function `_InputIter std::find_if(_InputIter, _InputIter, _Predicate) [with _InputIter =
    Filter::filter(std::vector<std::string, std::allocator<std::string>
    >&)::vecit, _Predicate = Manipulator&]':
    ..\main.cpp:79:   instantiated from here
    
    stl_algo.h:318: error: cannot
    allocate an object of type `Manipulator'
    stl_algo.h:318: error:  because the following virtual functions are abstract:
    ..\main.cpp:26: error: 	virtual bool Manipulator::operator()(std::string&)
    


  • CStoll schrieb:

    Dein Problem ist, daß die Funktionsobjekte von find_if standardmäßig per Wert übergeben werden - und eine abstrakte Klasse kannst du nicht per Wert übergeben. Aber du kannst explizit die für deine Zwecke richtige Version von find_if erzeugen lassen:

    Manipulator* m=new Contains(...);
    
    typedef vector<string>::iterator vecit;
    
    vecit pos=find_if<vec_it,Manipulator&>(vec.begin(),vec.end(),*m);
    

    (Referenzen auf abstrakte Objekte dürfen übergeben werden)

    du dereferenzierst hier aber m, was du nicht darfst, du kannst kein Objekt von Manipulator haben.

    Die einzig richtige Lösung ist das Verwenden eines Binders und da wir das Rad nicht neu erfinden wollen, ist boost::bind der richtige Weg. boost::bind ist ne header-only library, also einfach auf boost.org die lib runterladen, entpacken und boost/bind.hpp inkludieren.


  • Mod

    leerer schrieb:

    CStoll schrieb:

    Dein Problem ist, daß die Funktionsobjekte von find_if standardmäßig per Wert übergeben werden - und eine abstrakte Klasse kannst du nicht per Wert übergeben. Aber du kannst explizit die für deine Zwecke richtige Version von find_if erzeugen lassen:

    Manipulator* m=new Contains(...);
    
    typedef vector<string>::iterator vecit;
    
    vecit pos=find_if<vec_it,Manipulator&>(vec.begin(),vec.end(),*m);
    

    (Referenzen auf abstrakte Objekte dürfen übergeben werden)

    du dereferenzierst hier aber m, was du nicht darfst, du kannst kein Objekt von Manipulator haben.

    selbstverständlich dürfen zeiger auf abstrakte klassen dereferenziert werden. das problem ist, dass standardalgorithmen eine kopie des vergleichsprädikates benutzen (andernfalls würde man der algorithmus das prädikat gar nicht nutzen können, da op() nicht const ist). und abstrakte klassen können nunmal nur als subobjekte instanziiert werden.

    Die einzig richtige Lösung ist das Verwenden eines Binders und da wir das Rad nicht neu erfinden wollen, ist boost::bind der richtige Weg. boost::bind ist ne header-only library, also einfach auf boost.org die lib runterladen, entpacken und boost/bind.hpp inkludieren.

    es gibt nie nur eine lösung. eine einfache (und wohl auch schnellere) variante - vorausgesetzt alle manipulatoren sind zustandslos - wäre die verwendung normaler funktionen statt funktionsobjekten. anstelle eines polymorphen funktionsobjektes würde dann ein normaler funktionszeiger benutzt werden. nebenbei ist find_if als nicht mutierender algorithmus klassifiziert und darf folglich nicht mit funktoren verwendet werden, die ihr argument modifizieren.



  • nebenbei ist find_if als nicht mutierender algorithmus klassifiziert und darf folglich nicht mit funktoren verwendet werden, die ihr argument modifizieren.

    Nicht, nein? Und wie macht sich das bei nicht Beachtung bemerkbar?


  • Mod

    nirsaja logged out schrieb:

    nebenbei ist find_if als nicht mutierender algorithmus klassifiziert und darf folglich nicht mit funktoren verwendet werden, die ihr argument modifizieren.

    Nicht, nein? Und wie macht sich das bei nicht Beachtung bemerkbar?

    undefiniertes verhalten. abhängig von implementation, prädikat und sequenz passiert möglicherweise gar nichts besonderes - oder der computer explodiert. (dass die formalen anforderungen an parameter einiger algorithmen strenger sind als sie sein müssten ist ein bekanntes problem - in gewissen grenzen kann man sie in der praxis auch schon mal ignorieren...) 😉


Anmelden zum Antworten