Makro
-
Hallo Leute,
ich versuch gerade nen open source code nachzuvollziehen.
Ich arbeite sonst immer mit strukturen und kenne mich mit makros nicht so aus.
Weiß einer von euch, was\-Zeichen zu bedeuten haben?
Oder ist das nur so ne Art Trennung? Und wisst ihr vielleicht für was die erste Variable a (mat4x4f_init(a, \) steht.
Als Kommentar hat der Autor geschrieben:/* Initialize 4x4 floating point matrix. Recall that the macro argument * `a' is being evaluated many times over so the expression designated * as `a' has to be free of any side effects. */Was meint er mit Nebeneffekten?
Mit Strukturen könnte man doch auch arbeiten, oder gibts da große Nachteile?
#define mat4x4f_init(a, \ a00, a01, a02, a03, a10, a11, a12, a13, \ a20, a21, a22, a23, a30, a31, a32, a33) \ do{ a[0][0]=a00; a[0][1]=a01; a[0][2]=a02; a[0][3]=a03; \ a[1][0]=a10; a[1][1]=a11; a[1][2]=a12; a[1][3]=a13; \ a[2][0]=a20; a[2][1]=a21; a[2][2]=a22; a[2][3]=a23; \ a[3][0]=a30; a[3][1]=a31; a[3][2]=a32; a[3][3]=a33; }while(0)
-
Mit dem \ kannst du Makros über mehrere Zeilen schreiben.
Allerdings ist das da ziemlicher Unsinn.. Ich würde eher zu einer Klasse raten, welche dir die Elemente verwaltet und auch sinnvoll initialisiert.
Seiteneffekte meint er, dass a mehrmals ausgewertet wird, sprich, wenn du jetzt so etwas übergeben würdest, wie a++, dann wird das da lediglich ersetzt und a jedesmal, wo es vorkommt ebenfalls inkrementiert, was ja nicht der Sinn ist.
-
Hallo,
das / Zeichen ist einfach nur ein Zeilenumbruch für den Compiler. Du kannst so über mehrere Zeilen schreiben.
Bei Makros musst Du immer auf Seiteneffekte achten, denn solche erstezen nur einen Teil des Quelltexts.Nimm folgendes Beispiel
#define Increase(a) (a * a) ... int k = 4; cout << Increase(k+1); //Kommt hier wirklich 25 raus? --> Nein!Achte immer auf gute Klammerung und mache eventuell Nutzen von einer anderen Methode...
lg, freakC++
-
Super
danke für die schnelle Antwort.
Ja ich werd dann glaub ich auch eher ne Klasse schreiben.
LG
-
Alleine diese Aussage ist beinahe starfbar

Wenn Du daraus eine Klasse machen würdest, dann spiele noch nicht mal mit dem Gedanken ein Makro zu erstellen. Ein Makro ist eher für kleinere Operationen zuständig.lg, freakC++
-
freakC++ schrieb:
Ein Makro ist eher für kleinere Operationen zuständig.
Makros sind gut für bedingte Kompilierung, aber für "Operationen" würde ich niemals zu Makros greifen. Ausnahmen wo Makros vielleicht Sinn machen, trotz der Seiteneffekte sind verkürzte Schreibweisen (z.b. einige sehr lange Templateschreibweisen, die nicht durch typedefs gemacht werden können).
-
asc schrieb:
Makros sind gut für bedingte Kompilierung, aber für "Operationen" würde ich niemals zu Makros greifen. Ausnahmen wo Makros vielleicht Sinn machen, trotz der Seiteneffekte sind verkürzte Schreibweisen (z.b. einige sehr lange Templateschreibweisen, die nicht durch typedefs gemacht werden können).
Mit Makros kann man tolle Metaprogrammierungssachen machen, ist dabei einfacher zu erlernen als Templatemetaprogrammierung und viel einfacher zu debuggen.
-
SeppJ schrieb:
und viel einfacher zu debuggen.
Äh?
Bei Templates kann man ohne Optimierungen wenigstens alle Schritte nachvollziehen. Bei Makros sieht man im Debugger nur das Makro, und nicht die evtl. zig Zeilen Code, die sich dahinter verbergen.
Um Makros zu debuggen muss ich den Code zur Hilfe nehmen, den der Präprozessor ausspuckt.
-
SeppJ schrieb:
Mit Makros kann man tolle Metaprogrammierungssachen machen, ist dabei einfacher zu erlernen als Templatemetaprogrammierung und viel einfacher zu debuggen.
Ich möchte mal die Umgebung kennen lernen, der Makros sinnvoll debuggen kann (Zumindest unter den mir bekannten Umgebungen gilt dies nicht [Visual Studio, C++ Builder u.a.]).
-
LordJaxom schrieb:
SeppJ schrieb:
und viel einfacher zu debuggen.
Äh?
Bei Templates kann man ohne Optimierungen wenigstens alle Schritte nachvollziehen. Bei Makros sieht man im Debugger nur das Makro, und nicht die evtl. zig Zeilen Code, die sich dahinter verbergen.
Um Makros zu debuggen muss ich den Code zur Hilfe nehmen, den der Präprozessor ausspuckt.
Ähm, das war eigentlich genau mein Punkt: Wenn ich mit Makros arbeite, kann ich zwecks Debugging mit der Präprozessorausgabe arbeiten. Somit ist Makrometaprogrammierung zur Laufzeit auch nicht viel schwerer als mit Templates. Aber den großen Vorteil sehe ich zur Compilezeit (und das ist ja das eigentlich Interessante bei Metaprogrammierung). Hier bekommt man bei Templatemetaprogrammierung 10 Seiten kryptische Fehler, wenn etwas nicht passt. Bei Makros kann man ganz genau gucken, welches Makro wie ersetzt wird und was dabei eventuell schief läuft.
-
Dass Präprozessormetaprogrammierung einfacher zu erlernen ist, würde ich mal bezweifeln. Compilezeit-Debugging kann sich allerdings wirklich als recht mühsam erweisen. Da muss man die Regeln der Sprache gut kennen und selbst versuchen, den Fehler nachzuvollziehen, was besonders bei kryptischen Fehlermeldungen nicht immer ganz einfach ist. Beim Präprozessor sieht es nicht zwingend besser aus - bei komplexeren expandierten Ausdrücken verliert man schnell einmal die Übersicht und beginnt mit der Suche nach der Nadel im Heuhaufen.
Aber grundsätzlich sind es zwei verschiedene Techniken mit verschiedenen Anwendungsfällen, von daher würde ich nicht unbedingt 1:1 vergleichen. Template-Magie ist stark, wenn es um Typen oder integrale Konstanten geht. Solche Dinge sind mit Makros teilweise recht hässlich bis gar nicht herzustellen. Dafür hat man bei Präprozessor-Metaprogrammierung den Vorteil bei textueller Ersetzung (z.B. Aufzählungen). Diese Art von Magie wird in Zukunft aber auch an Einfluss verlieren, da einige Situationen durch Variadic Templates eleganter gelöst werden können.
-
Das Beispiel
#define Increase(a) (a * a) ... int k = 4; cout << Increase(k+1); //Kommt hier wirklich 25 raus? --> Nein!finde ich unpassend. k+1 hat nämlich keinen Seiteneffekt.
Und es lässt sich auch leicht fixen:
#define Increase(a) ((a) * (a)) // zusätzliche Klammern, und das Problem ist gegessen ... int k = 4; cout << Increase(k+1); //Kommt hier wirklich 25 raus? --> Ja, jetzt schon.Ein besseres Beispiel wäre vielleicht:
#define mat4x4f_init(a, \ a00, a01, a02, a03, a10, a11, a12, a13, \ a20, a21, a22, a23, a30, a31, a32, a33) \ do{ a[0][0]=a00; a[0][1]=a01; a[0][2]=a02; a[0][3]=a03; \ a[1][0]=a10; a[1][1]=a11; a[1][2]=a12; a[1][3]=a13; \ a[2][0]=a20; a[2][1]=a21; a[2][2]=a22; a[2][3]=a23; \ a[3][0]=a30; a[3][1]=a31; a[3][2]=a32; a[3][3]=a33; }while(0) typedef int my_matrix[4][4]; void mat4x4f_init_function(my_matrix& m, int a00, int a01, int a02, int a03, int a10, int a11, int a12, int a13, int a20, int a21, int a22, int a23, int a30, int a31, int a32, int a33) { // OK, "m" hat keine seiteneffekte mat4x4f_init(m, a00, a01, a02, a03, a10, a11, a12, a13, a20, a21, a22, a23, a30, a31, a32, a33); } void test() { my_matrix three_matrices[3]; // alle drei matrizen in three_matrices initialisieren (OK): my_matrix* p1 = &three_matrices[0]; mat4x4f_init_function(p1++, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); mat4x4f_init_function(p1++, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); mat4x4f_init_function(p1++, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); // alle drei matrizen in three_matrices initialisieren (falsch): my_matrix* p2 = &three_matrices[0]; // NICHT OK: "(p2++)" hat einen Seiteneffekt, nämlich dass der Zeiger p2 zum nächsten Element weitergerückt wird. // Da "(p2++)" in dem Makro 16x ausgewertet wird, wird der Zeiger auch // 16x pro mat4x4f_init "Aufruf" weitergerückt. // Hier insgesamt also 48x statt der gewünschten 3x. // Der Effekt ist, dass das Programm Unfug baut, da Speicher überschrieben wird, der "nicht uns gehört" mat4x4f_init((p2++), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); mat4x4f_init((p2++), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); mat4x4f_init((p2++), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); }Das wäre ein Seiteneffekt.
(Und dieses Beispiel lässt sich auch durch Klammerung nicht fixen.)
-
Zunächst mal guter Punkt, ich habe Debugging mehr auf die Laufzeit bezogen. Da mit Metaprogrammierung gewissermassen ein Programm im Compiler abläuft, muss ich den Begriff natürlich auch auf diese Phase ausdehnen.
SeppJ schrieb:
Ähm, das war eigentlich genau mein Punkt: Wenn ich mit Makros arbeite, kann ich zwecks Debugging mit der Präprozessorausgabe arbeiten. Somit ist Makrometaprogrammierung zur Laufzeit auch nicht viel schwerer als mit Templates.
Du musst aber zusätzliche Schritte einleiten (Präprozessorausgabe auffangen und separat übersetzen), um Makros debuggen zu können.
Aber den großen Vorteil sehe ich zur Compilezeit (und das ist ja das eigentlich Interessante bei Metaprogrammierung). Hier bekommt man bei Templatemetaprogrammierung 10 Seiten kryptische Fehler, wenn etwas nicht passt.
Das ist finde ich lediglich eine Sache der Übung. Denn so kryptisch sind die Fehlermeldungen garnicht. Der gcc meldet z.B. zuerst die Instanzierung, in der der Fehler auftritt (inkl. aller Templateargumente), dann die Instanzierungsliste, dann die Fehlermeldung. Mit etwas Erfahrung blickt man auch da durch.
Bei Makros kann man ganz genau gucken, welches Makro wie ersetzt wird und was dabei eventuell schief läuft.
Nur, dass Dir das nicht der Compiler sagt, sondern Du es selbst ermitteln musst. Dabei passiert im Grunde das gleiche: Du siehst die Fehlermeldung, gehst mit Hilfe des Präprozessors manuell durch die Instanzierungsliste und siehst dann, wo der Fehler wirklich auftritt.