C++ Memberpointer auf Basisklasse konvertieren



  • Moin!

    Ich habe ein kleines Problem mit der Konvertierung eines Memberpointers und es ist warm 😉

    class Base
    {
    public:
      int a;
    };
    
    class Derived : public Base
    {
    public:
      int b;
    };
    
    class Wrapper
    {
    public:
      Derived derived;
    };
    
    int main( void )
    {
      Derived Wrapper::*wd = &Wrapper::derived;
      Base    Wrapper::*wb = &Wrapper::derived;  // 23: error: invalid conversion from ‘Derived Wrapper::*’ to ‘Base Wrapper::*’ [-fpermissive]
    
      return 0;
    }
    

    Auch clang äußert sich nicht sonderlich gnädig zu dem Thema:

    xin@trinity:~$ clang memberptr.cpp 
    memberptr.cpp:22:23: error: cannot initialize a variable of type 'Base Wrapper::*' with an rvalue of type 'Derived Wrapper::*'
      Base    Wrapper::*  wb = &Wrapper::derived;  // 23: error: invalid conversion from ‘Derived Wrapper::*’ to ‘Base ...
                          ^    ~~~~~~~~~~~~~~~~~
    1 error generated.
    

    Ich möchte eine Funktion aufrufen, die einen (Base Wrapper::*) als Argument erhält.

    Hat jemand einen Tipp, wie ich das C++ verklickert bekomme? ^^



  • Xin schrieb:

    Ich möchte eine Funktion aufrufen, die einen (Base Wrapper::*) als Argument erhält.

    So nicht machbar (ausser mit wüsten plattformabhängigen Casts)

    Alternativen sind Templates oder std::function.



  • memmer schrieb:

    Xin schrieb:

    Ich möchte eine Funktion aufrufen, die einen (Base Wrapper::*) als Argument erhält.

    So nicht machbar (ausser mit wüsten plattformabhängigen Casts)

    Alternativen sind Templates oder std::function.

    Wie ist mir egal. Ich brauche die Möglichkeit eine Funktion aufzurufen

    func( &Wrapper::Derived );
    

    wobei die Funktiondeklaration mir relativ egal ist, solange sie alle Member frisst, die von Base abgeleitet sind. An der Aufrufstelle kann ich keinen Cast gebrauchen, in der Funktion kann man machen, was Gott verboten hat.



  • es gibt dafür keine Standard conversion. in 4.11 Pointer to member conversions steht nur, wie Nullpointer konvertiert werden und die Konvertierung für die Memberklasse, aber nicht für den Typ, auf den gezeigt wird

    man muß das mit einem Reinterpret cast machen

    5.2.10 Reinterpret cast
    10 A prvalue of type “pointer to member of X of type T1” can be explicitly converted to a prvalue of a different type “pointer to member of Y of type T2” if T1 and T2 are both function types or both object types.71 The null member pointer value (4.11) is converted to the null member pointer value of the destination type. The result of this conversion is unspecified, except in the following cases:
    — converting a prvalue of type “pointer to member function” to a different pointer to member function type and back to its original type yields the original pointer to member value.
    — converting a prvalue of type “pointer to data member of X of type T1” to the type “pointer to data member of Y of type T2” (where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer to member value.

    also ungefähr so:

    Base    Wrapper::*wb = reinterpret_cast<Base Wrapper::*>(&Wrapper::derived);
    


  • Mir ist nicht ganz klar, wieso diese Funktion unbedingt einen Member-Pointer bekommen muss.



  • dd++ schrieb:

    es gibt dafür keine Standard conversion. in 4.11 Pointer to member conversions steht nur, wie Nullpointer konvertiert werden und die Konvertierung für die Memberklasse, aber nicht für den Typ, auf den gezeigt wird

    man muß das mit einem Reinterpret cast machen

    Plöd... ich bin eigentlich der Meinung, dass das ohne Cast machbar sein müsste. Der Cast stört mich allerdings da auch nicht sonderlich, dass dafür ein zusätzliches Template her muss, stört mich viel mehr - ich habe beliebig viele Klassen, die von Base abgeleitet sein können und Member anderer Klassen wären.

    Das müsste eigentlich einfacher gehen, schließlich geht die Konvertierung nur in die Funktion rein und die Verallgemeinerung des Typs ist nicht falsch. 😕

    Aber wenn's nicht anders geht, so bekomme ich mit der Kombination Template/reinterpret_cast zumindest für den User die Funktion passabel aufrufbar:

    class Base
    {
     public:
       int a;
    };
    
    class Derived : public Base
    {
      public:
        int b;
    };
    
    class Wrapper
    {
      public:
        Derived derived;
    };
    
    void targetfunc( Base Wrapper::*memptr )
    {
      // nothing to do
    }
    
    template< typename T, typename U >
    void func( T U::*memptr )
    {
      targetfunc( reinterpret_cast< Base U::* >( memptr ));
    }
    
    int main( void )
    {
      Derived Wrapper::*wd = &Wrapper::derived;
    
      func( &Wrapper::derived );
    
      return 0;
    }
    

    Das lässt sich fehlerfrei durch den (G++) Compiler jagen. Ich war gestern wohl wirklich schon was weich in der Birne, da war ich auf halben Weg, aber hab's nicht gesehen.

    Ich setz mich heute abend wieder an das Problem, mal gucken, ob ich noch woanders drüber stolpere. ^^

    Ich danke Dir in jedem Fall schonmal für Deine Hilfe. 🙂

    dot schrieb:

    Mir ist nicht ganz klar, wieso diese Funktion unbedingt einen Member-Pointer bekommen muss.

    Da es sich um ein syntaktisches Problem handelt, ist das ja zur Lösung auch nicht erforderlich und das hier nur ein stark verkürztes, auf das Problem zugeschnittenes Beispiel. ^^

    In meinem Fall leite ich Objekte von einer Klasse ab und greife dann auf bestimmte Member der Instanzen zu. Nur weiß ich zur Compilezeit halt noch nicht, auf welche, bzw. das ändert sich halt von Anwendung zu Anwendung. Daher muss ich sie halt übergeben.


  • Mod

    Das ist nicht nur ein syntaktisches Problem (klar bei Mehrfach- od. virtueller Vererbung). Eine saubere Lösung wäre, den Memberfunktionszeiger durch eine entsprechende (polymorphe) Klasse zu ersetzen. Soweit ich sehe, sprechen objektorientierte Prinzipien (LSP&co.) nicht gegen dein Vorhaben, allerdings ist es in C++ nicht unmittelbar implementiert (so wie es z.B. auch keine kontravarianten Funktionsparameter gibt).



  • camper schrieb:

    Eine saubere Lösung wäre, den Memberfunktionszeiger durch eine entsprechende (polymorphe) Klasse zu ersetzen.

    Mein Problem ist, dass ich die Klassen möglichst nicht anfassen oder manipulieren muss.
    Ich brauche den Marker "Base", der eine Zusatzinformationen hält, so dass ich Derived (und im echten Problem auch Wrapper) von Base ableiten muss, ansonsten sollen die Klassen nichts von mir wissen oder sich auf mich einstellen müssen.

    Die Datenklassen (Derived, Wrapper) haben weder ein gemeinsames Interface, noch sind sie miteinander verwandt, können aber verschachtelt sein (Derived ist in Wrapper) oder aufeinander zeigen (z.B. ein Derived * innerhalb von Wrapper) oder überhaupt nichts miteinander zu tun haben.

    Ich habe eine (oder mehrere) Konfiguration pro Klasse außerhalb der eigentlichen Datenklasse und greife damit auf die Member der Datenklasse zu. Reflection quasi.
    Eine "saubere" Lösung wäre schön, aber ich erwarte nichts aus einem Lehrbuch.

    Das ist jetzt schon ein ... sagen wir recht kreativer Einsatz von Templates und Ableitungen aka "Gefrickel". 😉


  • Mod

    Grob skizziert könnte das ganze ungefähr so aussehen

    class Base
    {
     public:
       int a;
    };
    
    class Derived : public Base
    {
      public:
        int b;
    };
    
    class Wrapper
    {
      public:
        Derived derived;
    };
    
    template <typename B, typename W>
    struct poly_memptr
    {
        friend B& operator->*(W* p, const poly_memptr<B, W>& memptr) { return memptr.apply(p); }
        virtual B& apply(W*) const = 0;
    };
    template <typename B, typename W>
    B& poly_memptr<B, W>::apply(W*) const {}
    
    template <typename B, typename W, typename T>
    struct poly_memptr_impl : poly_memptr<B, W>
    {
        poly_memptr_impl(T W::* p) : memptr_(p) {}
        virtual T& apply(W* p) const override { return p->*memptr_; }
        T W::* memptr_;
    };
    
    template <typename B, typename W, typename T>
    poly_memptr_impl<B, W, T> make_mem_ptr(T W::* p) { return poly_memptr_impl<B, W, T>( p ); };
    
    void targetfunc( const poly_memptr<Base, Wrapper>& memptr )
    {
        Wrapper w;
        Base& foo = &w->*memptr;
    }
    
    template< typename T, typename U >
    void func( T U::*memptr )
    {
      targetfunc( make_mem_ptr<Base>( memptr ) );
    }
    
    int main( void )
    {
      Derived Wrapper::*wd = &Wrapper::derived;
    
      func( &Wrapper::derived );
    
      return 0;
    }
    

Log in to reply