C++ Gurus



  • 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.



  • Ja, dass man die Tests durchnummeriert ist nicht so gut und man muss nebenbei noch Code schreiben. Aber ansonsten finde ich das recht gut gemacht und die Fehlerausgabe kannst du doch mit Kommentaren versehen.



  • volkard schrieb:

    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;}

    Das ist als Beispiel zu verstehen. Wenn du merkst, du willst etwas als eigene Funktion haben und hast es vielleicht sogar schon ein paar mal ausgeschrieben -> extract method. Um das ?: geht es jetzt gar nicht. Es ist dir bestimmt schon öfter passiert, das etwas länger wird, als erwartet und du ne eigene Funktion draus machst.

    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.

    Ich mach zwei Mausklicks. Dauert 3 Sekunden. Du suchst je nach Komplexität bis zu einer halben Stunde in deinem ganzen Projekt die Funktion (natürlich mit Compiler) und baust auch noch einen Fehler rein. Natürlich ist es noch das beste, was du machen kannst, aber die Vorgehensweise ist trotzdem affig und nur das es nicht besser geht, entschuldigt das.

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

    Sag das nicht, bevor du es nicht ein paar mal zu deiner Zufriedenheit benutzt hast. 🙂

    kann ich mit eclipse ein eigenes makefile benutzen?

    sicher. Aber für C++ ist Eclipse eh nicht so toll. Die beschriebenen Funktionen gibt's nur für Java, wahrscheinlich auch aus den Gründen, die du angesprochen hast.

    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;
          }
    

    Ich finde das nicht gut lesbar. Ich sehe in foreach auch immer eine Art Garantie, dass die Collection nicht verändert wird. Kann sein, dass es an meiner Javanisierung liegt. Zumindest finde ich es schön, wenn ich den Iterator nicht mehr benutzen muss, sondern wenn der Compiler das für mich macht. Eine entsprechende Variante solltest du IMHO auf jeden Fall mal probieren.
    <Nachtrag>Das Verbergen des Iterators ist zumindest in Java und C# absolut Bestandteil des Designs von foreach. Siehe auch: http://java.sun.com/j2se/1.5.0/docs/guide/language/foreach.html </Nachtrag>

    HumeSikkins schrieb:

    Letztlich ist eine Refactoring-IDE für C++ aber deutlich komplexer als für andere Sprachen.

    Ja, leider. Hier zeigt sich definitiv ein Nachteil von komplexen Sprachen. Es tut einfach weh, wenn man mit wirklich genialen IDEs gearbeitet hat, die deinen Code richtig verstehen zu scheinen und dann mal wieder was in C++ programmiert. Es ist ernüchternd. 😞
    Das von dir angesprochene foreach werd ich mir mal zu Gemüte führen. 🙂



  • Ja, leider. Hier zeigt sich definitiv ein Nachteil von komplexen Sprachen

    Ich denke nicht, dass das an der Komplexität der Sprache liegt. Vielmehr an
    den wenigen Informationen die ein externes Tools so ohne weiteres aus einem C++ Programm ableiten kann. Würde man z.B. Compiler und Editor stärker integrieren, wäre Refactoring auch deutlich leichter zu machen.



  • Die Visual C++ IDE benutzt zumindest den Parser aus dem Compiler. Scheint trotzdem nicht so einfach zu sein, denn für C++ hat man kein Refactoring eingebaut. 😞 Volkard hat IMHO auch ein paar gute Beispiele für Schwierigkeiten gegeben.



  • Optimizer schrieb:

    Ich finde das nicht gut lesbar. Ich sehe in foreach auch immer eine Art Garantie, dass die Collection nicht verändert wird. Kann sein, dass es an meiner Javanisierung liegt.

    Jo, exakt. In C++ giibt es dafuer naemlich const 😉

    Zumindest finde ich es schön, wenn ich den Iterator nicht mehr benutzen muss, sondern wenn der Compiler das für mich macht.

    In C++ muss man nicht einen iterator verwenden, sondern man darf.



  • Was soll das heißen, man darf? Wenn volkards foreach-loop mir einen Iterator gibt, muss ich den benutzen, um den Wert zu erhalten. Ich kann hier keine Eigenheit von C++ erkennen, ich muss grundsätzlich nicht einen Iterator benutzen, aber ich kann auch einen Iterator benutzen, wenn ich mit einer Collection aus dem Java API arbeite. 😕
    Und was ich sagen wollte ist, dass ich ein foreach schöner finde, wo man keinen Iterator zu Gesicht bekommt.

    Was das const betrifft: In Java schreibt man oft readonly-wrapper, z.B. für das List-Interface. Damit kann ich dann alle möglichen Arten von Listen schützen. Für eine Map brauche ich natürlich wieder einen anderen Wrapper, aber prinzipiell ist der Schreibschutz von verschiedenen Collections sehr einfach gebaut (ich brauch vielleicht 2-3 Wrapper), während ich in C++ bei _jeder_ Klasse viele Methoden mit const überladen muss, einen eigenen Iterator verwenden muss für den const-Zugriff, usw.
    Hat jetzt aber IMHO nicht direkt was mit der foreach-loop zu tun. Eine foreach-loop ist für mich konzeptionell eine Aufzählung und eine Aufzählung ist für mich konzeptionell nicht eine Änderung am Container.
    Zusammenfassend wollte ich eigentlich nur feststellen, dass die Sprache IMHO nichts damit zu tun haben dürfte, was eine Aufzählung ist, aber ich bin wie immer abgeschweift. 🙄



  • Optimizer schrieb:

    Zusammenfassend wollte ich eigentlich nur feststellen, dass die Sprache IMHO nichts damit zu tun haben dürfte, was eine Aufzählung ist, aber ich bin wie immer abgeschweift. 🙄

    hast du kapiert, weshalb ich einen iterator brauche? geht das in deinen javaverdummten gummikopp rein? lies nochmal, was ich schrieb! ich will nicht zwei sorten foreach, je nachdem, ob ich einen wert oder ein position berechnen will und ich will erst recht keine dritte sorte, wenn ich beides brauche. so ein * tut nicht wirklich so weh wie ein halbdurchdachtes foreach.


Anmelden zum Antworten