Zeiger auf Klassenmethoden (Ableitung)



  • Huhu,
    ich habe ein kleines Problem mit Pointern auf Klassenmethoden.
    Gegeben sei folgende Klasse:

    class BaseClass{
    public:
    	BaseClass();
    	virtual ~BaseClass();
    
        ...
        int func1(char, int);
    	void takeFunc(int (BaseClass::*func)(char, int));
    };
    

    Der Methode takeFunc(..) kann ich einen Zeiger auf eine beliebige andere Klassenmethode übergeben (solang diese die entsprechende Signatur besitzt).
    Mein Problem ist nun das takeFunc(..) nun wirklich nur Pointer von der Klasse BaseClass annimmt. Wenn ich nun von BaseClass ableite und eine Methode mit entsprechender Signatur definiere kann ich diese nicht an takeFunc übergeben.

    class DerivedClass: public BaseClass {
    public:
    	DerivedClass();
    	virtual ~DerivedClass();
    	int func2(char, int);
    };
    
    ..
    
    DerivedClass dc;
    dc.takeFunc(&DerivedClass::func1); // Ok
    dc.takeFunc(&DerivedClass::func2); // Fehler
    

    Ich hätte gerne das takeFunc auch Zeiger auf Methoden von beliegen abgeleiteten Klassen aufnehmen kann.
    Hab ich irgendwas verpeilt oder gibt es einen Trick wie ich das machen kann? Oder muss ich mir irgendwie anders helfen?

    mfg xorm 🙂



  • mir fallen nur templates ein:

    class BaseClass{
    public:
        ...
        int func1(char, int);
        template <class T>
        void takeFunc(int (T::*func)(char, int));
    };
    

    Rest wie gehabt. Evtl. einen STATIC_ASSERT mechanismus einbauen der garantiert, dass T von BaseClass abgeleitet ist...



  • Nunja, verwunderlich ist das ja eigentlich nicht. Schließlich ist func2 ja keine Methode von BaseClass 😉 Kennst du boost::bind? Es lohnt sich wirklich, damit ein wenig herumzuspielen!



  • pumuckl schrieb:

    Rest wie gehabt. Evtl. einen STATIC_ASSERT mechanismus einbauen der garantiert, dass T von BaseClass abgeleitet ist...

    Wie soll dieser STATIC_ASSERT denn aussehen?



  • Aus modern C++ (bzw. der Loki-Bibliothek):

    template <bool> struct CompileTimeChecker
    { CompileTimeChecker(...); };
    template <> struct CompileTimeChecker<false> {};
    
    #define STATIC_CHECK(expr, msg) \
      { \
        class ERROR_##msg {}; \
        (void)sizeof(CompileTimeChecker< (expr) != 0 >((ERROR_##msg())); \
      }
    

    Dann noch der Check auf Vererbung:

    template <class T, class U>
    class Conversion
    {
      typedef char Small;
      struct Big {char dummy[2]; };
      static Small Test(U);
      static Big Test(...);
      static T MakeT();
    public:
      enum { sameType = false,
             exists = sizeof(Test(MakeT())) == sizeof(Small) };
    };
    
    template <class T> class Conversion<T,T>
    { public: enum { exists = true, sameType = true }; };
    
    #define SUPERSUBCLASS(T, U) \
      (Conversion<const U* const T*>::exists && \
      !Conversion<const T*, const void*>::sameType)
    

    benutzt wirds dann wie folgt:

    //...
    template<class T>
    void BaseClass::takeFunc(int (T::*func)(char, int))
    {
      STATIC_CHECK(SUPERSUBCLASS(BaseClass,T),
           Template_Param_Must_Inherit_BaseClass);
      /*...*/
    }
    

    das gibt dann eine Compilerwarnung á la
    "Error: Cannot convert ERROR_Template_Param_Must_Inherit_BaseClass to CompileTimeChecker<false>", wenn T nicht von BaseClass abgeleitet ist.



  • Hmm, das CHECK_... kompiliert bei mir nicht. Weder mit gcc noch mit VC9.

    Edit: sizeof ist in dem Kontext nicht zulässig, so sagt der Kompiler.



  • Danke für die Hinweise, werd ich mir mal näher angucken 🙂


  • Mod

    BOOST_STATIC_ASSERT( is_base_of< Base, Derived >::value );
    // oder
    BOOST_MPL_ASSERT(( is_base_of< Base, Derived > ));
    

    z.B.

    class BaseClass{
    public:
    
        ...
        void takeFunc(int (BaseClass::*func)(char, int));
        template <typename T>
        void takeFunc(int (T::*func)(char, int))
        {
            BOOST_MPL_ASSERT_MSG(( (is_base_of< BaseClass, T >), MEMBER_FUNCTION_OF_BaseClass_OR_DERIVED_REQUIRED, (int (T::*)(char, int)) ));
            BOOST_MPL_ASSERT_MSG(( (is_convertible< int (BaseClass::*)(char, int), (int (T::*)(char, int)) >), BaseClass_IS_AMBIGOUS_OR_INACCESSIBLE_OR_VIRTUAL_BASE, (T) ));
            takeFunc( static_cast< int (BaseClass::*)(char, int) >( func ) );
        }
    };
    

Anmelden zum Antworten