Multiline Makrofunktionen
-
Hallo an alle.
Eventuell gehört dieser beitrag nicht hierher, da es eher Vichtelstudio spezifisch ist.Vorweg die Definition der später benutzen Strukturen und Methoden:
/** * */ typedef void* item; /** * */ struct node { item value; struct node *next; }; /** * */ typedef struct node *link; /** * */ struct queue { struct node *head; struct node *tail; int size; }; /** * */ extern void queue_init(struct queue*); /** * */ extern void queue_free(struct queue*); /** * */ extern void queue_push(struct queue*, item); /** * */ extern item queue_pop(struct queue*); /** * */ extern item queue_top(struct queue*);
Ich habe mir folgende Makros definiert, um eine Art c template zu haben:
/** * */ #define QUEUE_POP(pQueue, QueueType) \ ({ \ QueueType returnedValue; \ QueueType *pStoredValue = 0; \ pStoredValue = (QueueType*) queue_pop(pQueue); \ returnedValue = *pStoredValue; \ free(pStoredValue); \ returnedValue; \ }) /** * */ #define QUEUE_PUSH(pQueue, QueueType, value) \ ({ \ QueueType *pValueToBeStored = (QueueType*) malloc(sizeof(QueueType)); \ (*pValueToBeStored) = value; \ queue_push(pQueue, pValueToBeStored); \ }) /** * */ #define QUEUE_TOP(pQueue, QueueType) \ ({ \ QueueType *pStoredValue = 0; \ pStoredValue = (QueueType*) queue_top(pQueue); \ *pStoredValue; \ })
diese benutzen meine queue implementierung, die jedoch nur pointer speichert (oben zu sehen). Damit ich auch mit kopien, bzw einfacheren Datentypen arbeiten kann, greife ich auf die queue auf folgene Art zu:
Bsp:
int i = QUEUE_TOP(q, int);
Nun zu dem Problem. Der GCC nimmt das hin und es funktioniert unter linux/unix einwandfrei. Unter windoof mit Wichtelstudio läuft es nicht; der schmeißt mir ein Haufen von Fehlermldungen, die alle mit den Makros zu tun haben. Da ich jedoch nicht verstehe wieso er überhaupt mekkert, wollte ich euch bitten einen Blick druaf zu werfen.
Benutzt wird Visual Studio .NET 2003
Als fehlermeldung bekomme ich:... Projects\v42ext\src\v42ext.c(11) : warning C4100: 'lpReserved' : unreferenced formal parameter ... Projects\v42ext\src\v42ext.c(9) : warning C4100: 'hModule' : unreferenced formal parameter ... Projects\v42ext\src\v42ext.c(111) : error C2275: 'BYTE' : illegal use of this type as an expression c:\Programme\Microsoft Visual Studio .NET 2003\Vc7\PlatformSDK\Include\WinDef.h(143) : see declaration of 'BYTE' ... Projects\v42ext\src\v42ext.c(111) : error C2061: syntax error : identifier 'returnedValue' ... Projects\v42ext\src\v42ext.c(111) : error C2065: 'returnedValue' : undeclared identifier ... Projects\v42ext\src\v42ext.c(112) : error C2143: syntax error : missing '{' before '->' ... Projects\v42ext\src\v42ext.c(112) : error C2059: syntax error : '->' ........
für den folgenden Abschnitt:
void process_char(struct v42ext *instance) { BYTE b = QUEUE_POP(instance->rcv, BYTE); instance->send_byte(b); if(b == SOH) { . . .
Zeile 111 ist diejenige mit BYTE b = ...
Danke in Voraus
Grüße
Dimitrij
-
welchen grund hast du, makros fuer sowas zu verwenden?
-
hätt eich nicht am anfang "Vichtelstudio" gelesen, würde ich mir die mühe machen, eine sinnvolle antwort zu schreiben.
-
was mir aufgefallen ist: in deinem QUE_POP() makro versuchst du aus dem {} einen wert herauszugeben (returnedValue). ich glaub' das geht nicht. ist mir auch nicht klar, warum gcc sowas frisst (ich bin aber auch nicht so der super makro experte...)
btw: lustig: 'Wichtelstudio'
-
Das geht definitiv nicht (höchsten bei irgendwelchen Exoten-Compilern), Makros machen per Definition nur eine reine textuelle Ersetzung.
Hallo @net, da waren wir uns doch einig. Ich dachte auch schon, dass wir jetzt die Lösung haben
-
jox schrieb:
Hallo @net, da waren wir uns doch einig. Ich dachte auch schon, dass wir jetzt die Lösung haben
für die STRLEN() semantik? naja, so geht's nicht. man könnte es so schreiben
#define STRLEN(x) returnedvalue;{...irgendein_code...;...;}
aber leider wird dabei der code nach der zuweisung ausgeführt
ich wüsste jetzt auch nix für:x = {...code...}
aber vielleicht gibt's da was nur kommen wir nicht drauf...
-
ich habe die Lösung mittlerweile.
Der Grund wieso VS (damit volkard sich nicht beleidgt fühlt) mit meiner Makrodefinition nicht zurecht kommt ist, weil Microsoft sich immer noch keine Mühe gemacht hat den C99 Standard zu implementieren, was bei GCC schon längst der Fall ist. Vielleicht nicht komplett, aber immerhin viel mehr als bei VS.
Eine kleine Bemerkung am Rande - Microsoft und Borland haben offensichtlich nicht vor, C99 jemals zu unterstützen (Zitat von http://www.schellong.de/better_c99.htm).Dieser Standard erlaubt es unter Anderem Makros zu schreiben, die man wie eine stink normale nich-void Funktion benutzen kann (s.o).
Bei C89 ist es nicht der Fall, und solche Konstrukte, wie#define foo(a) \ ({ \ ..... \ })
nicht gehen.
Damit kann man auch (dreist) behaupten, dass (bezogen auf jox Aussage über Exoten-Compiler) solche Makros nur von veralteten Compilern nicht geschluckt werden.
Apropos - in Anlehnung an C89 und somit auch an VS .NET müsste man die von mir oben gepostete Makros auf folgende weise umschreiben:
/** * */ #define QUEUE_POP(pQueue, QueueType, Variable) \ do { \ QueueType *pStoredValue = 0; \ pStoredValue = (QueueType*) queue_pop(pQueue); \ Variable = *pStoredValue; \ free(pStoredValue); \ } while(0) /** * */ #define QUEUE_PUSH(pQueue, QueueType, value) \ do { \ QueueType *pValueToBeStored = (QueueType*) malloc(sizeof(QueueType)); \ (*pValueToBeStored) = value; \ queue_push(pQueue, pValueToBeStored); \ } while(0) /** * */ #define QUEUE_TOP(pQueue, QueueType, Variable) \ do { \ QueueType *pStoredValue = 0; \ pStoredValue = (QueueType*) queue_top(pQueue); \ Variable *pStoredValue; \ } while(0)
wobei die Benutzung dieser z.B. bei Schleifen sehr unbequem ist:
Bsp für C99:
// q ist eine pointer auf struct queue while(QUEUE_POP(q, int) != 0) { // mache etwas }
und das selbe mit C98
// q ist eine pointer auf struct queue int i; QUEUE_POP(q, int, i); while(i != 0) { // mache etwas QUEUE_POP(q, int, i); }
c.rackwitz schrieb:
welchen grund hast du, makros fuer sowas zu verwenden?
Das was ich schreibe darf ich leider nicht in cpp schreiben, wo ich um generisch zu bleiben templates benutzen würde. Mit Hilfe der oben angegebenen Makros kann ich auch unter C generisch bleiben.
-
dadrus schrieb:
Dieser Standard erlaubt es unter Anderem Makros zu schreiben, die man wie eine stink normale nich-void Funktion benutzen kann (s.o).
Bei C89 ist es nicht der Fall, und solche Konstrukte, wie#define foo(a) \ ({ \ ..... \ })
nicht gehen.
Damit kann man auch (dreist) behaupten, dass (bezogen auf jox Aussage über Exoten-Compiler) solche Makros nur von veralteten Compilern nicht geschluckt werden.
Ähm, nein, das geht auch durch keinen C99-kompatiblen Compiler.
#define QUEUE_POP(pQueue, QueueType, Variable) \ do { \ QueueType *pStoredValue = 0; \ pStoredValue = (QueueType*) queue_pop(pQueue); \ Variable = *pStoredValue; \ free(pStoredValue); \ } while(0) [...]
wobei die Benutzung dieser z.B. bei Schleifen sehr unbequem ist:
Bsp für C99:
// q ist eine pointer auf struct queue while(QUEUE_POP(q, int) != 0) { // mache etwas }
Nein, das ist kein erlaubtes C99, weil ein compound statement (also irgendwas, was mit { anfängt und mit } wieder aufhört) keine Werte zurückgibt. ({ ... }) ist eine GNU-Erweiterung, mit C hat das nichts zu tun.
[Es gibt in C99 coumpound literals, wo auch { und ( involviert sind, aber die sehen etwas anders aus.]
-
Hm...
Dann habe ich was flasches gehört, bzw. verstanden.
Danke für die Belehrung.
-
dadrus schrieb:
Apropos - in Anlehnung an C89 und somit auch an VS .NET müsste man die von mir oben gepostete Makros auf folgende weise umschreiben:
/** * */ #define QUEUE_POP(pQueue, QueueType, Variable) \ do { \ QueueType *pStoredValue = 0; \ pStoredValue = (QueueType*) queue_pop(pQueue); \ Variable = *pStoredValue; \ free(pStoredValue); \ } while(0) ... ...
...und wozu ist das 'do{...}while(0)' gut ?
-
... damit der Optimierungslauf beim Compiler seine Daseinsberechtigung unter Beweis stellen kann...
----
Hi @net, ich dachte schon, jetzt hätten wir die Lösung, war wohl wieder nix
-
net schrieb:
...und wozu ist das 'do{...}while(0)' gut ?
Das hat schon seinen Sinn. Damit kann man dann das Makro (fast) so behandeln, wie einen Funktionsaufruf,
das Unterbringen in if-Verschachtelungen ist kein Problem mehr.
if(bed1) if(bed2) QUEUE_POP(a,b,c); else something_different();
Wenn man jetzt QUEUE_POP nur in einen Block gepackt hätte, dann würde das else hier dem falschen if zugeordnet werden (beachte das Semikolon).
-
Daniel E. schrieb:
...(beachte das Semikolon).
ach so, ein kleiner trick
naja, ich hab mir sowieso angewöhnt, solche makros nie direkt hinter ein 'if' zu schreiben bzw. wenn's nicht anders geht alles im 'if' in {} einzupacken. gerade wenn man makros für debugausgaben hat, die im release build wech sind, bezieht sich das 'if' auf die übernächste zeile. solche fehler sind lustig: im debug modus geht alles - aber die releaseversion zickt gewaltig rum