C++ Gurus
-
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.
-
Du brauchst den Iterator deshalb nicht in foreach. Du müsstest bei so einem (doch eher ungewöhnlichen Anwendungsfall) halt denn eben eine normale for-loop benutzen. Kein Mensch sagt, dass man mit foreach alles machen können muss. Und ich habe versucht zu begründen, warum ich der Meinung bin, dass foreach die Datenstruktur nicht verändern können sollte.
Aber bevor du jetzt endgültig nen Herzinfarkt kriegst, lassen wir es lieber dabei, da es sowieso dein foreach ist und du machen kannst, was du willst. Ich widme mich jetzt wieder meiner Verdummung, schönen Tag noch.
-
Optimizer schrieb:
Was soll das heißen, man darf? Wenn volkards foreach-loop mir einen Iterator gibt, muss ich den benutzen, um den Wert zu erhalten.
Der Sinn ist der: ein iterator ist mehr wert als ein direkter Wert, weil er Metainformationen besitzt die uU interessant sein koennen. Ein Java foreach hat das nicht. Da muss man iteratoren nehmen und die sind sicher nicht gratis wie in C++. Das finde ich schon wichtig.
Das ist halt der Unterschied. In C++ hat man die Moeglichkeit immer iteratoren zu verwenden, ein *i ist nicht schlimm, zumal es ja -> gibt. Eine lokale Referenz zu erstellen ist ja auch kein Problem.
Und was ich sagen wollte ist, dass ich ein foreach schöner finde, wo man keinen Iterator zu Gesicht bekommt.
Und was ich sagen wollte: ich finde es mit iterator schoener, weil ich einfach metainformationen gratis bekomme.
Was das const betrifft: In Java schreibt man oft readonly-wrapper, z.B. für das List-Interface.
Ich weiss, aber darum geht es nicht.
Warum kommt dauernd ein "aber in Java macht man es anders" wenn es um C++ geht?In C++ hast du einfach ein const um zu zeigen, dass die Collection nicht geaendert wird, oder aber du arbeitest mit const_iteratoren. Vollkommen egal wie man es in Java macht.
aber prinzipiell ist der Schreibschutz von verschiedenen Collections sehr einfach gebaut
Stimmt, ein 'const' ist dagegen ja extrem komplex
Ne, ein const fehlt in Java wirklich, aber sie werden es nie einbauen. Das ist echt ein minuspunkt.
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.
Du argumentierst jetzt nicht wirklich gegen ein const, oder? Das ist doch laecherlich.
Hat jetzt aber IMHO nicht direkt was mit der foreach-loop zu tun.
Eben, lass Java aus dem Spiel, das hat hier nix verloren.
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.
Und? In C++ macht man da const wenn man es nicht aendern will. So einfach ist das.
Nochmal langsam zum mitschreiben:
nicht-const: veraenderbar
const: aenderbarpasst, doch. In C++ will man keine Beschraenkungen auferlegt bekommen.
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.
Nein, du hast gesagt wie eine Aufzaehlung in Java ist, in C++ ist es aber anders. Mag sein dass Java 'perfekt' ist, aber in C++ macht man es halt anders.
Ich kann ja jetzt auch anfangen und sagen: schleifen sind doch doof, konzeptionell sollte es immer eine rekursion sein.
Ist genauso Begruendet wie "foreach darf container nicht aendern" aber in C++ halt absolut fehl am Platz.
-
HumeSikkins schrieb:
Wer braucht sprechende Namen, wenn wir einfach durchnummerieren können.
Was spricht gegen:
enum MyTestSuite { Test_TuDas, Test_TuDies, Test_TuJenes }; template<> template<> void object::test<MyTestSuite::Test_TuDas>()
MfG SideWinder
-
Shade Of Mine schrieb:
Ist genauso Begruendet wie "foreach darf container nicht aendern" aber in C++ halt absolut fehl am Platz.
Täusche ich mich, oder läßt std::for_each den Container auch unverändert?
-
Ich verstehe auch nicht, warum das in C++ so fehl am Platz sein sollte. Man siehe übrigens auch das von HumeSikkins vorgeschlagene foreach:
short array_short[] = {1,2,3}; BOOST_FOREACH( short & i, array_short ) { ++i; } // array_short contains {2,3,4} here
Es ist schließlich keineswegs so, dass man jede for-Schleife durch ein foreach ersetzen können muss. for ist ja gar nicht böse. foreach soll ja ein Spezialfall von for sein und damit auch einen kleineren Anwendungsbereich haben. Natürlich kann es jeder so für sich implementieren wie er will. Der Trend scheint aber in die Richtung zu gehen, den Iterator zu verbergen (auch wenn beim BOOST_FOREACH wohl generell eine Änderung _des Inhalts_ des Containers möglich ist).
-
Ich verstehe auch nicht, warum das in C++ so fehl am Platz sein sollte.
Weil die Sprache uA auch davon lebt keine Einschränkungen zu haben - steht aber bereits in 4 anderen Postings hier
MfG SideWinder
-
Jester schrieb:
Täusche ich mich, oder läßt std::for_each den Container auch unverändert?
for_each geht auf eine range und nicht auf einen container - die idee hier ist wohl eher ein apply statt einem for_each.
Natuerlich kann man aus bequemlichkeit ein foreach mit dereferenziertem iterator anbieten, habe ich bereits gesagt, dass eine referenz nicht weh tut, aber generell _verbieten_ iteratoren zu verwenden spricht nunmal gegen das C++ prinzip. Da kann Java noch so bunt sein.
-
SideWinder schrieb:
HumeSikkins schrieb:
Wer braucht sprechende Namen, wenn wir einfach durchnummerieren können.
Was spricht gegen:
enum MyTestSuite { Test_TuDas, Test_TuDies, Test_TuJenes }; template<> template<> void object::test<MyTestSuite::Test_TuDas>()
Ästhetik und vorallem die menschliche Faulheit (ein Methodennamen muss ich vergeben, aber niemand zwingt mich dazu eine neue Konstante anzulegen statt einfach test<42> zu schreiben).
Natürlich ist eine symbolische Konstante besser als eine Zahl, aber du willst mir doch nicht erzählen, das du diesen HACK wirklich schön findest. Und es gibt imo einen unterschied zwischen "geht" und "ist schön".Ein Unit-Test sollte schnell zu schreiben, leicht zu lesen und überhaupt eine leichtgewichtige Schönheit sein. Ansonsten wird er schlicht nicht geschrieben.
Der einzige Vorteil von TuT gegenüber z.B. CPPUnit ist der, dass man seine Tests nicht registrieren muss. Verwendest du jetzt die Aufzählung oder einen Kommentar oder sonstwas (würde ich auch machen, da eine Zahl als Name völlig indiskutabel ist), dann ist dieser Vorteil verloren. Es macht nämlich keinen Unterschied ob du jedesmal eine neue Konstante in deine Aufzählung schreiben musst oder ob die für jede neue Methode ein
CPPUNIT_TEST(testMethode);
in deine Test-Registrierungs-Map einfügst.
Du hast also letztlich den selben Aufwand, dafür aber eine hässlichere Syntax (jedesmal template <> template<>), weniger Portabilität und weniger Features.Der einzige Vorteil: Du hast voll-geil-Templates benutzt und kannst damit jetzt voll die Bräute beeindrucken
-
kann man zum doppelten template<> irgendwas im internet finden?