C++ Gurus



  • HumeSikkins schrieb:

    Alles in Allem denke ich, dass man den Code sicher noch etwas klarer machen kann. Ein Wunder würde ich allerdings selbst von Volkard oder Shade oder anderen Gurus nicht erwaten. Warum? Ähnliche Implementationen von
    anerkannten C++ Experten sind imo nicht wirklich weniger komplex

    Habe ich auch nie behauptet dass es unklar ist oder klarer geht. (btw, Guru bin ich auch nicht, ich schaue immer noch zu dir und volkard auf).

    Was mich aber beim ersten Blick verwirrt hat war ein H und ein Handler. Dass H fuer Handle oder Handler steht, ist in dem Zusammenhang genauso klar wie das E Event. Liegt aber wohl hauptsaechlich daran, dass Handler unguenstig gewaehlt wurde. Denn, soweit ich dass sehe, ist es nicht viel mehr als eine Verwaltung von Objekt und Methode. Und Handler ist gar sehr allgemein...

    Und da spiesst es sich IMHO. Du hast 2 Typen die Handler/Handle heissen... Das hat jetzt weniger mit dem H zu tun.

    PS: Falls jemand eine bessere Version hat, immer her damit.
    Muss aber die Anforderungen die ich beschrieben habe erfüllen. Also z.B. Typesafe und schneller als boost::signal sein.

    Der Code sieht IMHO ganz gut aus, die casts sind natuerlich ein Krampf, aber viel anders wird es nicht gehen...

    @volkard:
    taugt Code::Blocks denn mittlerweile etwas?



  • Ich möchte die Frage präzisieren. Beherrscht es einfaches Refactoring wie etwas umzubenennen oder Funktion extrahieren? Ich habe bisher noch keine IDE für C++ gesehen, die das kann. 😞

    volkard schrieb:

    ich will {for each (i,meincontainer) tuwas(*i);} schreiben und seit heute schreibe ich es.

    Ui, das will ich auch. Das heißt, ich tue es, aber leider nicht, wenn ich was mit C++ mache. 😉 Was hast du dir da feines zusammengehackt?



  • Shade Of Mine schrieb:

    taugt Code::Blocks denn mittlerweile etwas?

    ich hab den debugger noch nicht ausprobiert (spricht für mich, oder?). und das hinzufügen von files ist einen oder zwei klicks zu umständlich. und ich hab nur console-anwendungen gebaut. sonst sind mir keine nachteile aufgefallen. aber einen riesigen vorteil hat sie: ich kann mein usiversal makefile drunter haben und Code::Blocks ruft einfach mein makefile auf.
    das universal makefile mußte übrigens zerstört und wiederaufgebaut werden

    # Volkards universal makefile
    # (c) 1999-2005 volkard@normannia.de
    # feel free to use it in any ways.
    
    CXXFLAGS:=-Wall -Werror -march=i586 -O3 -fno-rtti -pipe -s -DNDEBUG -DUNITTEST
    #CXXFLAGS:=-Wall -Werror -march=i586 -O3 -fno-rtti -pipe -s -DNDEBUG
    
    PROJECT:=$(basename $(notdir $(CURDIR)))
    TARGET_EXT:=
    ifeq ($(OS),Windows_NT)
     TARGET_EXT:=.exe
    endif
    TARGET:=$(PROJECT)$(TARGET_EXT)
    
    zip:=$(PROJECT).rar
    sources:=$(wildcard *.cpp)
    objects:=$(sources:.cpp=.o)
    deps:=$(sources:.cpp=.d)
    
    all:	$(TARGET)
    
    .PHONY:	all clean run zip
    
    $(TARGET):	$(objects) $(deps)
    	$(CXX) $(CXXFLAGS) -o $(TARGET) $(objects)
    
    -include $(deps)
    
    %.o %.d:	%.cpp makefile
    	$(CXX) $(CXXFLAGS) -MMD -c $<
    
    clean:
    	-$(RM) $(TARGET) $(objects) $(deps)
    
    run:	$(TARGET)
    	./$(TARGET)
    
    zip:
    	-$(RM) $(zip)
    	rar a -r -s -m5 -ap$(PROJECT) $(zip) *.cpp *.h makefile
    

    das alte war zu doof, die dependencies auch immer neu zu schreiben, weil sich eine *.h geändert hat, die in einer *.d afgelistet war. es waren die *.d nur von den *.cpp abhängig, was falsch ist. jetzt werden mit -MMD immer zur gleichen zeit eine *.o und eine *.d gebaut, womit auch garantiert ist, daß die *.d immer akltuell ist.



  • Optimizer schrieb:

    volkard schrieb:

    ich will {for each (i,meincontainer) tuwas(*i);} schreiben und seit heute schreibe ich es.

    Ui, das will ich auch. Das heißt, ich tue es, aber leider nicht, wenn ich was mit C++ mache. 😉 Was hast du dir da feines zusammengehackt?

    das glaubste eh nicht, so einfach isses.
    ich hab übrigens den c++-standard verlassen und benutze hemmungslos typeof und auch die steuerung des alignments.

    #define each2(i,a) (typeof((a).begin()) i=(a).begin(),VHend=(a).end();i!=VHend;++i)
    //für for each(i,myVector)
    
    #define each3(i,a,b) (typeof(a) i=a,VHend=b;i!=VHend;++i)
    //für for each(i,0,100)
    

    ich muß leider each2 und each3 nehmen, weil ich keine möglichleit sah, für den MS-compiler beides eache heißen zu lassen (auf gcc kann ichs mit variadic macros.).



  • Optimizer schrieb:

    Ich möchte die Frage präzisieren. Beherrscht es einfaches Refactoring wie etwas umzubenennen oder Funktion extrahieren?

    äh. umbennenen macht man mit search&replace, oder? naja, man nennt es einfach um und fragt den compiler, wo's vorkommt, und macht die fehler alle raus. bin nir drauf gekommen, daß die ide das können sollte. auf jeden fall kann Code::Blocks sowas nicht. Code::Blocks ist eindeutig sparsam mit features (hoofentlich bleibt das auch so).

    und was heiß es, eine funktion zu extrahieren?



  • Aso, dann arbeitest du aber immer noch mit dem Iterator. Ein nettes Spielzeug ist es, aber an die Eleganz von

    List<int> foo = new List<int>();
    
    foreach( int x in foo )
        sum  +=  x;
    

    kommt es so noch nicht ran. Es müsste doch möglich sein, in dem Makro den Iterator gleich zu dereferenzieren und einer Referenz auf das aktuelle Objekt den Namen zu geben, den man als Parameter angibt, oder?



  • volkard schrieb:

    Optimizer schrieb:

    Ich möchte die Frage präzisieren. Beherrscht es einfaches Refactoring wie etwas umzubenennen oder Funktion extrahieren?

    äh. umbennenen macht man mit search&replace, oder? naja, man nennt es einfach um und fragt den compiler, wo's vorkommt, und macht die fehler alle raus. bin nir drauf gekommen, daß die ide das können sollte. auf jeden fall kann Code::Blocks sowas nicht. Code::Blocks ist eindeutig sparsam mit features (hoofentlich bleibt das auch so).

    und was heiß es, eine funktion zu extrahieren?

    Jede brauchbare Java- und C#- IDE kann das. Du machst einen Rechtsklick auf die Variable, Klasse, Funktion, Typparameter, sonstwas, sagst umbenennen und gibst nen neuen Namen an. Das ist um Welten besser als search & replace, weil dabei nichts kaputt gehen kann. Die IDE sagt dir, wenn das Umbenennen Ärger macht, weil es bereits eine andere Variable in dem Scope mit gleichen Namen gibt und sie benennt auch nichts um, was was anderes ist und nur zufällig genauso heißt.

    Funktion extrahieren ist folgendes:

    int a, b;
    ...
    foo( a >= b  ?  a : b );
    ...
    foo( a >= b  ?  a : b );
    

    Du markierst den Ausdruck "a >= b ? a : b", sagst extract method, gibst nen Namen ein und die IDE erstellt ne Methode

    int max(int a, int b)
    {
        return a >= b  ?  a : b;
    }
    

    und ersetzt jeden Code dieser Form durch den Aufruf.

    Kann auch sehr praktisch sein. Außerdem erwarte ich von einer IDE wenigstens noch, dass sie die Reihenfolge der Parameter bei einer Funktion ändern kann und alle Aufrufe anpasst. Das kannst du mit dem Compiler auch nicht immer, wenn du die Reihenfolge zweier Parameter vertauscht, die sich ineinander umwandeln lassen.



  • Kleiner Auszug aus dem, was Eclipse so kann:

    Rename
    Move (Klasse in einen anderen namespace (in java package natürlich) schieben
    Change Method signature (Reihenfolge vertauschen, Parameter entfernen)
    Convert anonymous class to nested
    Push down (Vererbungshierarchie)
    Pull up (Vererbungshierarchie)
    Extract Interface (aus Klasse)
    Generalize Type
    Use Supertype where possible
    Inline
    Extract Method
    Introduce Parameter
    Introduce Factory
    Convert local variable to field
    Encapsulate Field (getter und setter erstellen und private machen)

    das waren jetzt nicht alle, aber IMHO die interessantesten. Sowas kann es schon erheblich erleichtern, nachträglich den Code zu verbessern.



  • Optimizer schrieb:

    Aso, dann arbeitest du aber immer noch mit dem Iterator.

    ja. andere wege habe ich verworfen, weil ich nicht fordern will, daß isteratoren klein sind und schnell kopiert werden können. also meine bäume werden keine auswärtszeiger kriegen, wie die stl-baume, nur damit man einfach durchiterieren kann.

    List<int> foo = new List<int>();
    
    foreach( int x in foo )
        sum  +=  x;
    

    kommt es so noch nicht ran. Es müsste doch möglich sein, in dem Makro den Iterator gleich zu dereferenzieren und einer Referenz auf das aktuelle Objekt den Namen zu geben, den man als Parameter angibt, oder?

    dich stört nir das *i statt i?
    klar kann man das wegmachen. aber das wäre imho konzeptionell falsch und letztlich auch für den anwender unfreundlich. ich will c++ nur vereinfachen, nicht javaisieren.

    ganz in der nähe deiner absicht war ich, als ich mit sowas experimentierte:

    template<typename T>
    struct Runner{
       T begin;
       T end;
       ...
       operator DraufgezeigterTyp(T)&(){
          return *begin;
       }
       operator bool(){
          return begin!=end;
       }
       void operator++(){
          ++begin;
       }
    };
    


  • volkard schrieb:

    dich stört nir das *i statt i?

    Ja. bzw. das (*i)->foo(), wenn ich in der Collection Zeiger ablege.

    klar kann man das wegmachen. aber das wäre imho konzeptionell falsch und letztlich auch für den anwender unfreundlich. ich will c++ nur vereinfachen, nicht javaisieren.

    Wieso wäre das konzeptionell falsch? Eine foreach loop benutzt du doch, um auf allen enthaltenen Elementen zu arbeiten. Innerhalb einer foreach loop sollte man IMHO keine Änderungen am Container durchführen können. Sobald ich nen Iterator habe, kann ich das. Das finde ich konzeptionell falsch.
    Kommt es bei dir irgendwann vor, dass du in einer foreach loop etwas anderes mit dem Iterator machst, als ihn zu dereferenzieren?



  • Optimizer schrieb:

    Jede brauchbare Java- und C#- IDE kann das. Du machst einen Rechtsklick auf die Variable, Klasse, Funktion, Typparameter, sonstwas, sagst umbenennen und gibst nen neuen Namen an. Das ist um Welten besser als search & replace, weil dabei nichts kaputt gehen kann.

    ok. vortiel von java.
    in c++ kann dabei definitiv was kaputtgehen. ich denke nur mal an SFINAE, wo nichmal der compiler bemerken würde, daß was kaputt ist. an bedingte compilierung, daß ich gerade auf win arbeite und der linux-code ausbedingselt ist und dort auch nix ersetzt werden dürfe. könnte ja auch ausgedingselt sein, weil's toter code ist oder ne sicherheitskopie.

    Funktion extrahieren ist folgendes:

    int a, b;
    ...
    foo( a >= b  ?  a : b );
    ...
    foo( a >= b  ?  a : b );
    

    Du markierst den Ausdruck "a >= b ? a : b", sagst extract method, gibst nen Namen ein und die IDE erstellt ne Methode

    das brauche ich definitiv nicht. ich schreibe nie ?: und ich baue viele einzeiler. und ich schreibe vor allem nicht {int tmp=a;a=b;b=tmp;}

    Außerdem erwarte ich von einer IDE wenigstens noch, dass sie die Reihenfolge der Parameter bei einer Funktion ändern kann und alle Aufrufe anpasst. Das kannst du mit dem Compiler auch nicht immer, wenn du die Reihenfolge zweier Parameter vertauscht, die sich ineinander umwandeln lassen.

    doch, klar. das hab ich schon vor 12 jahren gemacht. man nennt die funktion zuerst um in foo (und macht alles fehlerfrei, um jeden erwischt zu haben). und dann nennt man sie zurück und vertauscht auch die parameter.

    und für eclipse ist mein rechner eh viel zu schwach. außerdem hat mich keines der features vom sockel gehauen. es kann halt keine ide geben, die allen gefällt. kann ich mit eclipse ein eigenes makefile benutzen?



  • @Optimizer
    Hast du schon mal Eric Nieblers BOOST_FOREACH benutzt? Ist imo zumindest einen Blick wert. Und die Komplexität der Implementation zeigt, dass manche Sachen doch lieber als Sprachmittel daherkommen sollten 😉



  • Optimizer schrieb:

    Kleiner Auszug aus dem, was Eclipse so kann:

    Rename
    Move (Klasse in einen anderen namespace (in java package natürlich) schieben
    Change Method signature (Reihenfolge vertauschen, Parameter entfernen)
    Convert anonymous class to nested
    Push down (Vererbungshierarchie)
    Pull up (Vererbungshierarchie)
    Extract Interface (aus Klasse)
    Generalize Type
    Use Supertype where possible
    Inline
    Extract Method
    Introduce Parameter
    Introduce Factory
    Convert local variable to field
    Encapsulate Field (getter und setter erstellen und private machen)

    das waren jetzt nicht alle, aber IMHO die interessantesten. Sowas kann es schon erheblich erleichtern, nachträglich den Code zu verbessern.

    für C++ nehm ich unter Linux SlickEdit. Der kann sowas auch.

    Sicherlich hat der andere macken, aber im großen und ganzen ist es brauchbar.
    Auf jeden fall besser als emacs und Eclipse-C++-plugin.



  • außerdem hat mich keines der features vom sockel gehauen.

    Die Features für C++-Programmierung (Alias CDT) sind leider noch nicht der Höhepunkt 😞 Eigene Makefiles kannst du aber afaik verwenden.

    MfG SideWinder



  • aber vielleicht mußte mal irgendwas praktisches wie einen guten http-server bauen, damit du objektiv erfahren bist

    Ich baue zur Zeit etwas sehr praktisches: Einen Answerset-Solver. Ok, ist so praktisch wie ein Knoten im Kreuz 😃

    volkard schrieb:

    HumeSikkins schrieb:

    Ich habe von dir eine Version deiner Volkard-Lib.

    oh, gibt es sowas noch?
    schick sie mir mal volkard@normannia.de

    Ok.

    Ich möchte die Frage präzisieren. Beherrscht es einfaches Refactoring wie etwas umzubenennen oder Funktion extrahieren? Ich habe bisher noch keine IDE für C++ gesehen, die das kann.

    Es gibt Ansätze. Schau dir z.B. mal Visual SlickEdit an. Letztlich ist eine Refactoring-IDE für C++ aber deutlich komplexer als für andere Sprachen. Dazu kommt, dass die C++ Community was sowas angeht immer etwas hinterherhinkt.
    Es fehlt hier manchmal einfach die Buzzword-Verliebheit. Das ist imo oft ein Vorteil, in solchen Fällen aber auch ein Nachteil. Und wo keine Nachfrage, da kein Angebot.
    Deshalb muss unsereiner halt viele viele Unit-Test schreiben (und dabei ziemlich hässliche Test-Libs benutzen) und dann beim Refactoring immer Micheal Feathers "Working Effectively with Legacy Code" (sehr zu empfehlen) und Martin Fowlers "Refactoring" zur Hand haben.



  • Optimizer schrieb:

    volkard schrieb:

    dich stört nir das *i statt i?

    Ja. bzw. das (*i)->foo(), wenn ich in der Collection Zeiger ablege.

    guter punkt. aber ich lege ja keine zeiger in collections rein. wenigstens nicht mehr ständig, wie noch neulich, als ich alle typen, die keinen copy-ctor anbieten, nur per zeiger speichern konnte.
    wenn ich mal (*i)-> schreibe, denke ich an dich und überlege, ob ich each2 ändern sollte.

    Wieso wäre das konzeptionell falsch? Eine foreach loop benutzt du doch, um auf allen enthaltenen Elementen zu arbeiten.

    ja.

    Innerhalb einer foreach loop sollte man IMHO keine Änderungen am Container durchführen können. Sobald ich nen Iterator habe, kann ich das. Das finde ich konzeptionell falsch.

    nicht richtig. die finale änderung muss möglich sein.
    in meinem aktuellen code isses zum beispiel extrem wirkungsvoll (und damit meine ich mehrtausendfache beschleunigung), in einer schleife

    for(...)
       for each(i,liste)
          if(i->...){
             machwas();
             break;
          }
    

    zu sagen

    for(...)
       for each(i,liste)
          if(i->...){
             machwas();
             if(i!=liste.begin())//hier tue ich das böse aber schnellmachende
                swap(*i,*(i-1));
             break;
          }
    

    bei anderen parametern ist auch mal

    size_t pos=i-liste.begin();
    swap(*(i+pos),*(i+pos/2));
    

    angesagt. (übrigend habe ich dabei herausgefunden, daß move-to-front-listen als doppelt verkettet listen gar nicht so notwendig sind, wie man immer meint. die nachbildung im vector mit dem pos/2-trick (oder auch pos-pos/16+1 !) wirkt auch sehr gut.)

    es ist da vieles vorstellbar, vielleicht will man das element auch löschen oder den iterator drauf in einer anderen liste speichern.

    Kommt es bei dir irgendwann vor, dass du in einer foreach loop etwas anderes mit dem Iterator machst, als ihn zu dereferenzieren?

    ich habe each erst seit gestern (ist bei einem redesign aufgetaucht, das ich immer so mache, daß ich ein neues verzeichnis anlege und alle dateien nacheinander reinhole und mir dabei alles angucke und nach möglichen änderungen (nicht nur verbesserungen, will ja nicht zu schlimm lokalen maxima efangen werden) suche. entsprechend mss ich jetzt erst noch die anderen files reinholen und alle mit each verseuchen, woimmer möglich. hab noch zu wenig erfahrung mit for each, um aus erfahrung zu sprechen.



  • Also Eclipse und Netbeans sind wirklich genial beim refactorn von Java-Code.

    Aber für C++ gibts IDE-Erweiterungen:
    http://www.ideat-solutions.com/index.htm
    http://www.slickedit.com/

    Allgemeine Refactoring Website:
    http://www.refactoring.com/



  • HumeSikkins schrieb:

    Deshalb muss unsereiner halt viele viele Unit-Test schreiben (und dabei ziemlich hässliche Test-Libs benutzen)

    wegen der wirklich hässlichen libs habe ich bis letze woche keine unittests geschrieben. aber dann wurde es doch irgendwie nötig, als ich bei den primzahlen bemerkte, daß ich fehler in einfachsten funktionen hatte, die ein kleiner brute-force-test ganz leicht und zuverlässig gefunden hätte.

    und die lib gefällt mir, weil's meine ist. ok, kommen sicherlch noch viele verbesserungen rein, aber erstmal klappt's so und ist brauchbar.
    ein lustiger doppelt verketteter ring aus globalen statischen objekten. der ring, weil's einfach ist und weil ich dann sogar den gesamt-test wachsen und schrumpfen lasse, je nach eladenen dlls oder schared objects. hat doch vorteile, wenn man ringe auch per hand bauen kann. statisch, weil die reihenfolge des includierens wichtig ist. ich muss immer den ersten fehler vorne haben, und eventuelle folgefehler hinten. das geht nur so.

    #ifndef TESTER_H
    #define TESTER_H
    
    class Tester{
    private:
        Tester* left;
        Tester* right;
        char const* name;
        bool (*func)();
        Tester();
        static Tester& root();
    public:
        Tester(char const* _name,bool (*_func)());
        ~Tester();
        static void run();
    };
    
    #ifdef UNITTEST
    
    #define TEST(name)\
     bool test##name();\
     static Tester tester##name(#name,&test##name)
    
    #else
    
    #define TEST(name) extern int test
    
    #endif
    
    #endif
    

    und

    #include "test.h"
    #include "iostream.h"
    
    Tester& Tester::root(){
        static Tester root;
        return root;
    }
    
    Tester::Tester(){
        left=this;
        right=this;
        right->left=this;
        left->right=this;
    }
    Tester::Tester(char const* _name,bool (*_func)()){
        left=root().left;
        right=&root();
        right->left=this;
        left->right=this;
        name=_name;
        func=_func;
    }
    Tester::~Tester(){
        left->right=right;
        right->left=left;
    }
    
    //ist goto wirklich notwendig? hab ich's geschafft, ein beispiel zu finden? 
    //komm' ich jetzt in's fernsehen?
    void Tester::run(){
        for(Tester* pos=root().right;pos!=&root();pos=pos->right){
            for(Tester* back=pos->left;back!=&root();back=back->left)
                if(back->func==pos->func)
                    goto continue2;
            cout<<pos->name<<"... "<<flush;
            if(!(*pos->func)()){
                cout<<"ERROR"<<endl;
                return;
            }
            cout<<"ok"<<endl;
    continue2:;
        }
    }
    


  • Ich finde tut ist ein nettes Unit Test Framework. boost::test dagegen benutzt Macros!



  • kingruedi schrieb:

    Ich finde tut ist ein nettes Unit Test Framework.

    Echt? Ich finde TUT ist eine Katastrophe und ein Beispiel dafür, dass ein krampfhaftes Anti-Makro-Dogma nicht weiterhilft. Wer will bitte Tests mit dem Namen

    template<>
    template<>
    void object::test<1>()
    

    haben? Yeah! Wer braucht sprechende Namen, wenn wir einfach durchnummerieren können. Und dann die Assert-Funktionen, die keinerlei nützliche Informationen liefern. Dann doch lieber ein Makro und nachher eine Ausgabe mit nützlichen Informationen wie Name der Datei, Name der Funktion und Zeile usw.

    Btw: Ich glaube Testframework schreiben ist mittlerweile eine ähnlich beliebte Übungsaufgabe wie Stringklasse. Ich habe hier auf meiner Platte allein drei Eigenkreationen. Eine davon gefällt mir sogar recht gut 😉
    Am Ende des Tages verwende ich dann aber doch immer CPPUnit. Grund: Das ist einigermaßen bekannt und verbreitet.


Anmelden zum Antworten