Container mit verschiedenen Methodenpointern



  • Hi,

    struct Base {};
    
    struct A : public Base
    {
         P* bla(P*);
    };
    
    struct B : public Base
    {
         P* blub(P*);
    };
    
    struct C : public Base
    {
         P* blib(P*);
    };
    

    Jetzt hätte ich gerne sowas:

    map<Name, P* (T::*memfn)(P*)> mappi;
    

    Das Problem ist hier nur das T. Wie kann ich versch. Typen da rein packen. Boost is not allowed ...



  • KasF schrieb:

    Wie kann ich versch. Typen da rein packen.

    Garnicht. Selbst mit boost dürfte das schwierig werden, weil du eben immernoch die Typinformation irgendwo mitschleifst. Ich könnte mir höchstens vorstellen dass das mit einem boost::any oder ähnlichem und einigem Gefrickel machbar ist.

    Boost is not allowed ...

    Warum nicht?



  • Hrmpf,
    der Code landet nachher auf nem µC, der nicht gerade viel C++ versteht. Zudem gibts da nen Nein vom Cheff 🙂

    Was für Möglichkeiten gibts noch um das Problem zu lösen:
    Habe mehrere Komponenten, die gemeinsam kommunizieren sollen. Das Ganze über RPC. Es wird nur P* rumgereicht. Ich würde halt gerne sowas machen:

    struct A {
      void do() { s.call("B::Blub", pobj); }
    };
    
    struct B {
      B() { s.registerF(&B::Blub, "B::Blub"); }
      P* blub(P* p) { ... }
    };
    

    Ne RPC schicht gibts es drunter schon, die alle Komponenten kennt. Muss nur was drüber basteln, um die gewünschte Elementfunktion einer Komponente aufrufen zu können.

    Edit: Oh, da fällt mir doch was ein ...



  • Wenn du die Basisklasse verändern darfst sollte ne virtuelle Funktion tun, die dann in der jeweiligen abgeleiteten Klasse auf die eigentliche Funktuion redirected.



  • Du könntest dir deinen eigenen Wrapper bauen, z.B. sowas hier:

    #include <vector>
    
    struct A
    {
    	int* funcA( int* p )
    	{
    		cout << "A::funcA(): " << *p << endl;
    		return p;
    	}
    };
    
    struct B
    {
    	int* funcB( int* p )
    	{
    		cout << "B::funcB(): " << *p << endl;
    		return p;
    	}
    };
    
    template<typename R, typename P1>
    struct ICaller
    {
    	virtual ~ICaller() {	}
    	virtual R operator()( P1 p ) = 0;
    };
    
    template<typename T,typename R,typename P1>
    class Caller : public ICaller<R,P1>
    {
    	typedef R (T::*pfnMemFun)( P1 );
    
    	T&          Object;
    	pfnMemFun 	Member;
    
    public:
    	Caller( T& o, pfnMemFun m ) :
    		Object( o ),
    		Member( m )
    	{
    	}
    
    	R operator()( P1 p )
    	{
    		return (Object.*Member)( p );
    	}
    };
    
    int main()
    {
    	typedef ICaller<int*,int*> IntPtrCaller;
    	typedef std::vector<IntPtrCaller*> CallerVector;
    
    	A anA;
    	B anB;
    
    	Caller<A,int*,int*> CallA( anA, &A::funcA );
    	Caller<B,int*,int*> CallB( anB, &B::funcB );
    
    	CallerVector v;
    
    	v.push_back( &CallA );
    	v.push_back( &CallB );
    
    	int i = 5;
    
    	for( CallerVector::iterator it = v.begin(); it != v.end(); ++it )
    	{
    		(**it)( &i );
    	}
    }
    

    Dabei müssen die Objekte nichtmal vom gleichen Typ sein, lediglich die Methodensignatur muss übereinstimmen.



  • Perfekt DocShoe, vielen Dank. Ich glaube damit kann ich was anfangen 🙂



  • Oder auch etwas "weniger dynamisch", ganz ohne Heap und virtuelle Funktionen:

    #include <iostream>
    
    typedef char P;
    
    // -----[ Magie beginnt hier ]-----
    
    template< class Klasse, P* (Klasse::*MemFun)(P*) >
    struct wrappiPP
    {
      // jede Klasse/MemFun-Kombination bekommt ihre eigene
      // invoke-Funktion, die den Parameter weiterleitet...
      static P* invoke(void* pObjekt, P* arg) {
        return (static_cast<Klasse*>(pObjekt)->*MemFun)(arg);
      }
    };
    
    class delegatePP
    {
      void* pObj;          //< Zeiger auf das Objekt
      P* (*pf)(void*,P*);  //< Zeiger auf die invoke-Funktion, siehe oben
    
      // privater ctor (der Typ-Sicherheit wegen)
      delegatePP(void* o, P*(*pf)(void*,P*)) : pObj(o),pf(pf) {}
    
    public:
      // "Nullzeiger"
      delegatePP() : pObj(0), pf(0) {}
    
      P* operator()(P* arg) const { return pf(pObj,arg); }
    
      // erzeugt ein neues delegatePP-Objekt
      template<class Klasse, P*(Klasse::*MemFun)(P*), class K2>  
      static delegatePP make(K2* pObj2) {
        Klasse* pObj1 = pObj2;
        return delegatePP(pObj1,wrappiPP<Klasse,MemFun>::invoke);
      }
    };
    
    // -----[ Magie endet hier ]-----
    
    struct A {
      P* dings(P*) {std::cout<<"A::dings\n"; return 0;}
    };
    
    struct B {
      P* bums(P*) {std::cout<<"B::bums\n"; return 0;}
    };
    
    void machdat( delegatePP d )
    {
      d(0);
    }
    
    int main()
    {
      A a;
      B b;
      delegatePP x;
      x = delegatePP::make<A,&A::dings>(&a);
      machdat( x );
      x = delegatePP::make<B,&B::bums>(&b)
      machdat( x );
    }
    

    Edit: Habe versucht, das etwas leserlicher und leichter nachvollziehbar zu gestalten.

    Gruß,
    SP



  • Sebastian Pizer schrieb:

    ...

    Sorry Sebastian ... eigentlich bist Du mein Held, aber Docs Code finde ich trotzdem gleichzeitig übersichtlicher als auch schöner und besser zu verwenden und zu verstehen... 🕶

    Aber vielleicht hat Dein Ansatz ja Vorteile, die sich mir nicht auf Anhieb erschließen...

    Gruß,

    Simon2.



  • Simon2 schrieb:

    Sebastian Pizer schrieb:

    ...

    Aber vielleicht hat Dein Ansatz ja Vorteile, die sich mir nicht auf Anhieb erschließen...

    Das erklärte Ziel des OPs war es doch, eine Art "Funktionszeiger" zu benutzen. delegatePP kommt einem Funktions-Zeiger sehr nahe, wobei ICaller<>* eher etwas wie ein Zeiger auf ein Funktionszeiger ist. Man muss sich also noch um die Verwaltung eines Caller-Objektes kümmern.

    Natürlich lässt sich so ein ICaller<>* Zeiger in einem Wrapper verpacken, der im Destruktor das Caller-Objekt wieder löscht. Dann verhält sich dieser Wrapper wieder wie eine Art Funktionszeiger. Boost.Function macht das fast genauso.

    delegatePP ist einfach eine relativ spezielle Lösung -- speziell für Elementfunktionen einer bestimmten Signatur P*(P*). Diese Lösung emuliert einen Funktionszeiger ohne Freispeicherzugriff und virtuellen Funktionen. Gut, Caller<> ist auch relativ speziell, muss es aber gar nicht sein. Man hätte im Caller-Template beliebige Funktionsobjekte speichert können, statt sich auf Elementfunktionszeiger zu beschränken. ... Was ich damit sagen will ist, dass wenn man nur Objektzeiger und Funktionszeiger speichern will, nicht diese zusätzliche Indirektion benötigt.

    Was jetzt besser oder schlechter ist, hängt von den genauen Anforderungen und den "use cases" ab. So etwas wie Boost.Function (oder derartige Nachahmungen) ist natürlich sehr flexibel. Man muss aber auch einen relativ großen Aufwand betreiben, um etwas derartiges performant zu machen (Eine naive Boost.Funktion-Implementierung würde im Copy-Ctor eine virtuelle clone()-Funktion des Quell-Objektes aufrufen etc).

    Gruß,
    SP


Anmelden zum Antworten