adresse einer virtuellen memberfunktion als template-parameter für einen member



  • Ich hoffe der Titel hat niemandem Kopfschmerzen bereitet.

    Es dreht sich um folgendes:

    Ich möchte die Adresse einer virtuellen Memberfunktion einer Klasse als Templateparameter für einen Member dieser Klasse benutzen, was unter dem C++ Compiler von VS2005 einen internen Kompilierfehler verursacht.

    Vermutlich sagt das Codebeispiel unten mehr aus als jede Worte darüber es je könnten.

    template<class T, void(T::*F)()>
    struct foo 
    {
    };
    
    struct bar
    {
    	virtual void something()
    	{
    	}
    
    	foo<bar, &bar::something> blub;	
    };
    
    int main()
    {
    	return 0;
    }
    

    GCC kompiliert den Code problemlos, bei VS2005 gibts diesen netten internen Kompilierfehler:

    ...\main.cpp(3) : fatal error C1001: Interner Compilerfehler.
    (Compilerdatei "msc1.cpp", Zeile 1393)
     Vereinfachen oder ändern Sie das Programm im Umfeld der oben aufgeführten Positionen. Wählen 
    Sie im Menü "Hilfe" von Visual C++ den Befehl "Technischer Support", 
    oder öffnen Sie die Hilfedatei des technischen Supports, um weitere Informationen zu erhalten.
    

    Interessanterweise funktioniert es problemlos, wenn:
    - Die Funktion nicht virtuell ist.
    oder
    - blub kein Datenmember von bar ist.

    Was auch möglich ist, ist eine nicht-virtuelle Funktion zu schreiben, die die virtuelle Aufruft und diese dann als Templateparameter zu benutzten.

    Jetzt zu meiner Frage:
    Hat jemand eine kluge Idee für einen schöneren Workaround als eine Wrapper-Funktion?
    Kann es sein, dass ein Kompiler den obrigen Code garnicht erst kompilieren muss? Ist es vielleicht nicht erlaubt die Adresse einer virtuellen Memberfunktion als Templateparameter für Datenmember zu benutzen?
    Kommentare? Ideen? Anregungen?

    Falls sich jemand fragt, wozu zum Teufel man sowas braucht:
    http://boost-sandbox.cvs.sourceforge.net/boost-sandbox/boost-sandbox/libs/property/test/object_properties.cpp?revision=1.2&view=markup

    Im Grunde ist das Problem hier nur vereinfacht dargestellt.

    Gruß, pyrokar 🙂


  • Mod

    Ich kann im Standard nichts finden, das dieses Konstrukt verbietet - vermutlich hat der Compiler hier Schwierigkeiten, weil er die vtable für eine Klasse erst am Ende der Klassendefinition erstellt (reine Vermutung). Herumspielen mit /vmx Switches oder #pragmas scheint nichts zu bringen. Einen weiteren Workaround hab ich noch gefunden (compiliert, aber ungetested):

    struct bar_base { virtual void something() { } }; // mit pure geht es nicht
    
    struct bar : bar_base
    {
        virtual void something()
        {
        }
    
        foo<bar,static_cast<void(bar::*)()>(&bar_base::something)> blub;
    };
    

    edit: doch nicht legal, da das kein konstanter Ausruck mehr ist, aber msvc frisst es, keine Ahnung was g++ dazu sagt. vielleicht erfüllt

    foo<bar_base,&bar_base::something> blub;
    

    ja auch den Zweck für dich



  • also bei mir wird dieses Konstrukt ohne Probleme von VC++ 2005 compiliert



  • Das obere?
    Mir hat das den VC2005 SP1 compiler abgeschossen...



  • hth schrieb:

    Das obere?
    Mir hat das den VC2005 SP1 compiler abgeschossen...

    echt? mir nicht
    ah halt - mir auch, wenn ich im Debugmodus compiliere - im Releasemodus passiert nix


  • Mod

    stimmt: /Gm (Code Generation/Enable Minimal Rebuild) ist der schuldige Schalter



  • Der BCC (5.9) erzeugt bei obigem Beispiel auch einen ICE, und auch hier funktioniert es, wenn man die Deklaration von blub z.B. nach main() verschiebt (also trifft campers Vermutung wohl zu). Auch campers Workaround wird übersetzt.

    Was den Nutzen angeht: solche Konstrukte wie diesen boost::property-Entwurf habe ich früher auch manchmal gebaut, aber abgesehen davon, daß es schön aussieht, ist der Nutzen ohne Möglichkeiten wie Reflection doch recht beschränkt, und zudem erhöht es lediglich den Speicherbedarf der Klasse wegen des in dem Property-Objekts gespeicherten this-Zeigers. Oder übersehe ich da etwas?



  • Danke für die vielen aufschlussreichen Antworten, besonders an camper u. Checker&Murckser.

    Tatsächlich läuft es ohne /Gm problemlos durch.
    Genau so etwas hab ich gesucht 🙂

    audacia schrieb:

    ...
    Was den Nutzen angeht: solche Konstrukte wie diesen boost::property-Entwurf habe ich früher auch manchmal gebaut, aber abgesehen davon, daß es schön aussieht, ist der Nutzen ohne Möglichkeiten wie Reflection doch recht beschränkt, und zudem erhöht es lediglich den Speicherbedarf der Klasse wegen des in dem Property-Objekts gespeicherten this-Zeigers. Oder übersehe ich da etwas?

    Stimmt, man könnte zwar noch ein paar mehr Spielereien mit den properties anstellen, aber der Hauptnutzen ist dass es schöner aussieht.

    Ich finde halt sowas wie:

    car.Speed += 20;
    

    schon deutlich schöner als:

    car.setSpeed(car.getSpeed() + 20);
    

    Das ist mir als Programmierer den überflüssigen this-Zeiger etc. wert.

    Die Frage beim Klassendesign, die hierbei natürlich aufkommt ist, ob man die get/set-Funktionen dann private macht damit der Nutzer nur das Property benutzen kann oder ob man sie public lässt.



  • Verwirrenderweise gibt folgendes trotzdem deaktiviertem /Gm wieder einen Internen Kompilierfehler:

    template<class T>
    struct wrapper
    {
    };
    
    template<class T, void(T::*F)()>
    struct foo 
    {
    };
    
    struct bar
    {
    	virtual void something()
    	{
    	}
    
    	wrapper<
    		foo<bar, &bar::something>
    	> blub;
    };
    
    int main()
    {
    	return 0;
    }
    

    Es gilt wieder:
    - Macht man something nicht-virtuell, geht es.
    - Packt man blub außerhalb von bar, geht es ebenfalls.

    Ein typedef für foo<bar, &bar::something> löst das Problem leider nicht.

    Wirklich komischer Compiler, dieser MSVC++.

    Leider ist so etwas für die Benutzung von Boost.Property nötig.


  • Mod

    Der Workaround über die Basisklasse funktioniert offenbar nicht:

    struct bar_base { virtual void something() {} };
    struct bar : bar_base
    {
        virtual void something()
        {
        }
    
    };
    
    #include<iostream>
    int main()
    {
        std::cout << (&bar::something==&bar_base::something);  // gibt 1 aus
    //    int x[&bar::something==&bar_base::something];  // geht schief
        return 0;
    }
    

    Diese Konvertierung während des Compilierens erfolgt offenbar nicht richtig und ist ohnehin nicht konform. Die Benutzung einer nichtvirtuellen Funktion dürfte die beste Lösung sein, das ist standardkonform und m.E. sowieso guter Stil.



  • Ich wuerde trotzdem mal noch MS Bescheid sagen, damit die das fixen. Dafuer bezahlt man schliesslich denen auch das Geld. f'`8k

    Autocogito

    Gruß, TGGC (making great games since 1992)



  • Pyr0kar schrieb:

    Das ist mir als Programmierer den überflüssigen this-Zeiger etc. wert.

    Auch den Laufzeitnachteil, der entsteht, weil die Funktion nicht inline generiert werden kann, da auf sie über einen Zeiger zugegriffen wird?

    Eine eher unschöne, aber saubere Lösung dürfte übrigens folgendes sein:

    template <class T, void (T::*F) (void)>
    	struct foo
    {};
    
    struct bar
    {
    	virtual void func (void) { }
    
    private:
    	void func_wrapper (void) { func (); }
    
    public:
    	foo <bar, &bar::func_wrapper> blub;
    };
    

  • Mod

    audacia schrieb:

    Eine eher unschöne, aber saubere Lösung dürfte übrigens folgendes sein:

    unschön, weil du public und private falsch gesetzt hast 😉 virtuelle Funktionen sollten sowieso nicht public sein.



  • camper schrieb:

    virtuelle Funktionen sollten sowieso nicht public sein.

    ??



  • audacia schrieb:

    camper schrieb:

    virtuelle Funktionen sollten sowieso nicht public sein.

    ??

    schließ mich an
    Wieso das denn? Ich hab die immer mit dem Sichtbarkeitsattribut versehen, was ich brauchte - auch public ...



  • *push*



  • @audacia:

    camper schrieb:

    virtuelle Funktionen sollten sowieso nicht public sein.

    Das bezieht sich doch auf deinen Code, wo aber virtual-func() private ist. Aber camper sagt "sollten sowieso", als ob du sie public hättest. Gehe mal deswegen davon aus das es nur ein vertipper war 😉

    Private virtuals machen nämlich nicht gerade viel Sinn ...



  • KasF schrieb:

    @audacia:

    camper schrieb:

    virtuelle Funktionen sollten sowieso nicht public sein.

    Das bezieht sich doch auf deinen Code, wo aber virtual-func() private ist. Aber camper sagt "sollten sowieso", als ob du sie public hättest. Gehe mal deswegen davon aus das es nur ein vertipper war 😉

    Würde ich nicht von ausgehen 😃

    Private virtuals machen nämlich nicht gerade viel Sinn ...

    In einem der Effectives steht was dazu drin. In der Tat machen private virtuals, die von public nonvirtuals aufgerufen werden, sehr viel Sinn.
    EDIT: Herby schreibt folgendes: http://www.gotw.ca/publications/mill18.htm



  • LordJaxom schrieb:

    Private virtuals machen nämlich nicht gerade viel Sinn ...

    In einem der Effectives steht was dazu drin. In der Tat machen private virtuals, die von public nonvirtuals aufgerufen werden, sehr viel Sinn.
    EDIT: Herby schreibt folgendes: http://www.gotw.ca/publications/mill18.htm

    Achso ok. Ich lese mir das mal später bzw. im Buch nochmal durch, wenn ich *wacher* bin 🙂



  • KasF schrieb:

    Das bezieht sich doch auf deinen Code, wo aber virtual-func() private ist.

    virtual void func() ist public!

    @LordJaxom: danke für den Link, das dürfte camper wohl gemeint haben.
    Zwar kann ich ihn verstehen, aber ich teile Sutters Ansichten in dieser Frage nicht generell. In diesem Fall wäre es aber wohl besser, wie camper vorschlägt:

    template <class T, void (T::*F) (void)>
        struct foo
    {};
    
    struct bar
    {
    private:
        virtual void func_impl (void) { }
    
    public:
        void func (void) { func_impl (); }
    
    public:
        foo <bar, &bar::func> blub;
    };
    

Anmelden zum Antworten