Curiouses bei der Verwendung von Templates



  • Hi Forum,

    ich habe ein Problem mit der Verwendung von Templates im folgenden Code und zwar nicht im Delegate sondern im Speziellen in der Klasse B.

    template<typename parameter>
    class Delegate {
        typedef void (*invoke_stub)(void const *, parameter);
        void const  *obj_ptr_;
        invoke_stub stub_ptr_;
    
        template<typename T, void (T::*Fxn)(parameter)>
        struct mem_fn_stub
        {
            static void invoke(void const * obj_ptr, parameter a0)
            {
                T * obj = static_cast<T *>( const_cast<void *>( obj_ptr ) );
                (obj->*Fxn)( a0 );
            }
        };
    
        template<void (*Fxn)(parameter)>
        struct function_stub
        {
            static void invoke(void const *, parameter a0)
            {
                (*Fxn)( a0 );
            }
        };
    
    public:
        Delegate() : obj_ptr_( 0 ), stub_ptr_( 0 ) { }
    
        template<typename T, void (T::*Fxn)(parameter)>
        void bind(T * obj)
        {
            obj_ptr_ = const_cast<T const *>( obj );
            stub_ptr_ = &mem_fn_stub<T, Fxn>::invoke;
        }
    
        template<void (*Fxn)(parameter)>
        void bind()
        {
            obj_ptr_ = 0;
            stub_ptr_ = &function_stub<Fxn>::invoke;
        }
    
        void operator ()(parameter a0) const
        {
            ( *stub_ptr_ )( obj_ptr_, a0 );
        }
    };
    
    void cb(int& cbd) {
    }
    
    class A {
    public:
        Delegate<int&> callback;
    };
    
    template <class T>
    class B {
        typedef T TypeT;
    //    typedef A TypeT;
    public:
        void cb(int&) {}
    
        B() {
            int i=5;
            TypeT *_t=new TypeT();
            _t->callback.bind<&::cb>();
            _t->callback(i);
            _t->callback.bind<B,&B::cb>(this);
            _t->callback(i);
    
        }
    };
    
    int main (int argc, char **argv) {
        B<A> b;
        return 0;
    }
    

    Wenn ich das typedef aus Zeile 61 benutze, wo das Typedef direkt die Klasse A verwendet, wird der Code ordnungsgemaess uebersetzt und funktioniert auch einwandfrei. Wenn ich hingegen das typedef mit dem Template-Parameter verwende erhalte ich folgende Compiler-Fehler.

    curious.cc: In constructor ‘B<T>::B()’:
    curious.cc:67: error: expected primary-expression before ‘)’ token
    curious.cc:69: error: expected primary-expression before ‘,’ token
    curious.cc: In constructor ‘B<T>::B() [with T = A]’:
    curious.cc:76: instantiated from here
    curious.cc:67: error: invalid operands of types ‘<unresolved overloaded function type>’ and ‘void ()(int&)’ to binary ‘operator<’
    curious.cc:76: instantiated from here
    curious.cc:69: error: invalid operands of types ‘void (B<A>::
    )(int&)’ and ‘B<A>* const’ to binary ‘operator>’

    Ich weiss nicht, was an dem Code falsch ist und hoffe, Ihr koennt mir helfen, denn ich weiss momentan echt nicht, wo und an welcher ecke ich das Problem angehen kann. Mir sagt in diesem Zusammenhang die Compilermeldung leider auch nicht so richtig was.

    Danke schon mal im Voraus
    Michael



  • Compiler?

    Mit VC++ 2008 funktioniert das einwandfrei. Kann gut sein, dass ein älterer Compiler hier Probleme hat.



  • Naja, dies freut mich zwar soweit, dass der Code scheinbar richtig ist, aber ...

    Ich verwende den g++ in der Version 4.3.2 unter Linux. Wie es aussieht scheint dies wohl ein g++-Bug zu sein. Kann dies jemand bestaetigen? Wenn ja, werde ich mal einen Bugzilla-Eintrag beim gcc machen.



  • Kein Bug.Mach mal so:

    B() {
        int i=5;
        TypeT *_t=new TypeT();
        _t->callback.template bind<&::cb>();
        _t->callback(i);
        _t->callback.template bind<B,&B::cb>(this);
        _t->callback(i);
    }
    

    Wenn _t ein Templatetyp ist, musst du dem compiler gesondert mitteilen, wenn du planst,über ihn eine Templatemethode aufzurufen.



  • Ja, dies funktioniert. Aber so richtig verstehe ich es nicht, muss ich zugeben. Kannst Du dies noch ein bisschen erklaeren bzw. wo findet man dies entsprechend im Standard?



  • Das ist recht einfach zu verstehen:

    Die Syntax ist an der Stelle Kontextabhängig. "bind" muss keine Funktion sein, sondern ist vielleicht eine normale Variable.

    Wenn der Compiler also etwas liest wie:

    _t->callback.bind<&::cb>()
    

    kann er zum Zeitpunkt des parsen nicht heraus bekommen, was "bind" überhaupt ist, und kann demzufolge nicht wissen, ob das "<" Zeichen ein operator< oder eine template Klammer darstellt. Der Compiler kann zwar weiter lesen, und könnte dann bei dem Ausdruck ">()" auf die Idee kommen, dass das ganze ein Funktionsaufruf sein könnte, aber es könnte auch ein einfacher Sytaxfehler sein.Deswegen hat man sich beim Standardcommitee dazu entschlossen(nein, ich hab den Standard nicht hier), dass man es den Compilerbauern einfacher macht, und deswegen vorschreibt, dass bind nur dann eine Templatemethode sein kann, wenn dies durch ein "template" angekündigt wurde.

    Anschaulicher wirds vielleicht an diesem Beispiel:

    _t.foo<5>(3+6);
    

    Ohne zu wissen, was foo ist, kann man nicht zwischen einem Methodenraufruf mit einer Zahl als Templateparameter und dem hier unterscheiden:

    (_t.foo<5)>3+6
    


  • Jetzt hat es klick gemacht. Danke!!! man lernt wohl wirklich nie aus ...

    Deine Signatur spricht uebrigens fuer sich und trifft hier wirklich absolut zu.


Anmelden zum Antworten