Ein bisschen Makro-Magie
-
Hi!
Ich will nur mal was Praktisches zeigen, für den der's noch nicht kennt

Folgende Ausgangssituation: Ich bastele zur Zeit aus Spaß an der Sache an einem kleinen HTTP-Webserver. Es kommen natürlich ständig Anfragen in Textform von Browsern rein. Diese Anfragen will ich parsen und in einem "Request"-Objekt speichern.
Eine typische Anfrage sieht z.B. so aus:GET /bla.html HTTP/1.1 Host: localhost User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; de; rv:1.9.0.1) Gecko/2008070208 Firefox/3.0.1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: de-de,de;q=0.8,en-us;q=0.5,en;q=0.3 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-aliveIn der ersten Zeile steht das Verb (hier "GET") und der auszuliefernde Pfad. Die Zeilen danach sind das Interessante: Sie bestehen jeweils aus Schlüssel (z.B. "Accept") plus Wert (das hinterm Doppelpunkt).
Da mein Webserver-Gerüst möglichst cool sein soll, soll der Benutzer natürlich so eine Abfrage machen können:Request bla; // Anfrage schneit herein vector< pair<double,string> > accepted_languages = requ.getHeaderAcceptLanguage().getAcceptedLanguages();(Ich merke grad, es wäre schöner ohne das
getHeaderAcceptLanguagedazwischen, aber dann müsste ich nochmal alles ändern und könnte das gar nicht mehr benutzen, was ich hier zeigen will.)Gut, zu jedem möglichen (es gibt >40) Schlüssel brauche ich eine Klasse, da der jeweilige Header-Wert oft verschieden geparst werden muss. Alle dieser Header-Klassen haben ein z.T. ähnliches Interface, Standardkonstruktor + Konstruktor(const char*).
Irgendwo in der Request-Klasse wird auch das obige Text-Paket geparst. Dabei gehe ich natürlich jede Zeile durch, gucke mir den Schlüssel an und muss die entsprechende Variable in der Request-Klasse setzen (eine von >40, mit
m_header_accept = HeaderAccept(line);). So, da ich, wenn ich den Schlüssel-Namen habe, nicht ein 40-erifmachen will (ich muss ja auch auf die Performance achten), hab ich mir eine Art Factory (ich weiß, es ist keine wirklich Factory, aber wie soll ich's nennen?) gebaut:class Request { private: void setHeaderAccept( const char* value ) { m_header_accept = HeaderAccept( value ); } // Wird beim Parsen aufgerufen, für jede Schlüssel-Wert-Zeile. void setHeaderInfo( const char* key, const char* value ) { typedef void(Request::*SetHeaderFunc)( const char* ); static map<string,SetHeaderFunc> bla; if ( ! bla.size() ) // Map einmalig initialisieren { bla["accept"] = &setHeaderAccept; // ... } map<string,SetHeaderFunc>::const_iterator i = bla.find( key ); if ( i != bla.end() ) ((*this).*(i->second))( *this, value ); } };Ich hoffe das ist soweit verständlich, die map ordnet jedem vorkommenden Schlüssel einen Funktionszeiger zu, bei dessen Aufruf das entsprechende Header-Member-Variablen-Objekt mit seinem Wert initialisiert wird.
Um's zusammenzufassen: Es gibt eine Klasse Request und >40 Klassen, die jeweils eine Header-Art repräsentieren. Die Request-Klasse hat von fast jeder Header-Art-Klasse eine Variable.
Oh, und eine Sache hatte ich noch vergessen: Die Header-Klassen lassen sich in 4 Kategorien unterteilen, General-Header/Entity-Header/Response-Header/Request-Header. Die Request-Klasse hat nur Variablen von den General-, Entity- und Request-Header-Klassen.
-
So, mein Problem an diesem Punkt war: Es wäre unheimlich viel zu schreiben, ich skizziere mal:
class Request { public: HeaderClassXY& getHeaderClassXY() { return m_header_class_xy; } // ..., ziemlich oft private: void setHeaderClassXY( const char* value ) { m_header_class_xy = HeaderClassXY(value); } // ..., auch ziemlich oft // Wird beim Parsen aufgerufen, für jede Schlüssel-Wert-Zeile. void setHeaderInfo( const char* key, const char* value ) { static map<string,SetHeaderFunc> bla; // ... bla["accept"] = &setHeaderAccept; // ..., auch ziemlich oft } HeaderClassXY m_header_class_xy; // auch ziemlich oft };Ein Gräuel das zu schreiben, aber noch grausamer, falls mal irgendwas umgestellt werden soll..
Zum Glück hab ich mich dann aber an was geniales (hey, ich find's zumindest genial!) erinnert, was ich irgendwo irgendwann mal gesehen hatte:
// Header-Liste, General/Entity/... sind die 4 verschiedenen Arten der Header-Klassen. #define HEADER_LIST( General, Entity, Request, Response ) \ Request( Host, host ) \ Request( UserAgent, user_agent ) \ General( CacheControl, cache_control ) \ Entity( Accept, accept ) \ // Steht für "HeaderAccept" und die Kleinschreibweise, für Variablen z.B.. // ... // Ein define, was in's Leere geht: #define HEADER_EMPTY_LIST_ITEM(a,b)So, und damit lass ich mir jetzt alles generieren:
// Für HeaderClassXY& getHeaderClassXY() { return m_header_class_xy; } #define REGISTER_HEADER_GETTER( a, b ) Header##a& getHeader##a() { return m_header_##b; } HEADER_LIST( REGISTER_HEADER_GETTER, REGISTER_HEADER_GETTER, REGISTER_HEADER_GETTER, EMPTY_LIST_ITEM ) #undef REGISTER_HEADER_GETTER // Für void setHeaderClassXY( const char* value ) { m_header_class_xy = HeaderClassXY(value); } #define REGISTER_HEADER_HANDLER( a, b ) void setHeader##a( const char* v ) { m_header_##b = Header##a(v); } HEADER_LIST( REGISTER_HEADER_HANDLER, REGISTER_HEADER_HANDLER, REGISTER_HEADER_HANDLER, EMPTY_LIST_ITEM ) #undef REGISTER_HEADER_HANDLER // Um die Map zu füllen mit bla["accept"] = &setHeaderAccept; #define ADD_MAP_ENTRY(a,b) factory[#b] = &setHeader##a; HEADER_LIST( ADD_MAP_ENTRY, ADD_MAP_ENTRY, ADD_MAP_ENTRY, EMPTY_LIST_ITEM ) #undef ADD_MAP_ENTRY // Und zu guter Letzt für die Membervariablen: #define ADD_MEM_VAR(a,b) Header##a m_header_##b; HEADER_LIST( ADD_MEM_VAR, ADD_MEM_VAR, ADD_MEM_VAR, EMPTY_LIST_ITEM ) #undef ADD_MEM_VAR // Der letzte Makro-Parameter ist immer leer, damit die Header-Klassen der Kategorie Response nicht mit reingenommen werden, sie werden vom Prä-Prozessor durch Nichts ersetzt.Genial, oder?

-
Typlisten nach Alexandrescu wären hier bestimmt ein wenig eleganter, aber OK.
Und der Zugriff wäre auch nicht so leicht, aber mit Loki wäre das wahrscheinlich noch ein wenig einfacher.
-
Wie? Zeig doch mal!
-
Badestrand schrieb:
Genial, oder?

Was genau? Die Makro-Magie? Das ist doch uralt. Wird exessiv in Boost benutzt und auch mindestens in einem C++ Artikel im Magazin.
Ich begreif nicht ganz was eigentlich der Zweck dieses Threads sein soll ...Grüssli
-
Dravere schrieb:
Was genau? Die Makro-Magie? Das ist doch uralt. Wird exessiv in Boost benutzt und auch mindestens in einem C++ Artikel im Magazin.
Ich hatt's erst einmal in irgendeinem non-boost-Sourcecode gesehen, fand's toll, hatte es wieder vergessen und heut beim Programmieren ist's mir eingefallen als ich's gebraucht hab. Hab mich gefreut und find's genial, das muss dich ja nicht tangieren, irgendwer wird's schon noch nicht kennen.
Dravere schrieb:
Ich begreif nicht ganz was eigentlich der Zweck dieses Threads sein soll ...
War der erste Satz: Vielleicht interessant für jemanden der's nicht kennt.
-
Badestrand schrieb:
Wie? Zeig doch mal!
Schau doch mal in Loki.
Sollte in etwa das bieten, was du suchst:
http://loki-lib.sourceforge.net/index.php?n=Main.GenericProgrammingGenauer: Hierarchy generators.
-
Badestrand schrieb:
Hab mich gefreut und find's genial, das muss dich ja nicht tangieren, irgendwer wird's schon noch nicht kennen.
Och, es tangiert mich nicht wirklich, fand den Thread nur etwas seltsam. Ich freu mich ja auch immer wieder, wenn ich etwas auf eine einfache Art lösen kann, deswegen mache ich dafür aber nicht einen Thread auf.
Aber weisst du was, es wäre eigentlich wirklich super, wenn dazu mal jemand einen Artikel oder zumindest einen FAQ Beitrag schreiben würde. Könntest zusätzlich noch weitere Magie von Boost anwenden:
http://www.boost.org/doc/libs/1_36_0/libs/preprocessor/doc/index.htmlAllgemein ein Artikel über den C++ Präprozessor wäre wirklich eine super Sache. Man sagt eigentlich immer, man soll den Präprozessor meiden, was für Anfänger sicher auch das Beste ist. Wer ihn aber sinnvoll einsetzt, hat ein super zusätzliches Tool. Und wie man ihn einsetzen kann, bzw. wofür alles, das wäre mal ein echt geiler Artikel, bzw. Thread

Grüssli
-
Ich mach ja auch nicht jedesmal einen Thread auf wenn ich mich freue und vor allem sollte es eigentlich gar nicht so ein langer Text werden. Klar erscheint der Thread seltsam, es ist ja auch ausnahmsweise einmal kein "Hab da mal ne Frage"-Thread sondern ein "Kanntet ihr schon?"-Thread.
Das mit dem FAQ-Eintrag/Artikel ist eine gute Idee, mach du doch, scheinst dich doch gut auszukennen

~Und in der Annahme, dass du "tangieren" evtl falsch verstanden hast: http://lexikon.meyers.de/meyers/Tangieren~drakon, da gibt's zufällig nicht eine Doku (außer DoxyGen) zu? Die Headerdatei versteh ich nicht, keine Ahnung wie ich das benutzen soll

-
Badestrand schrieb:
drakon, da gibt's zufällig nicht eine Doku (außer DoxyGen) zu? Die Headerdatei versteh ich nicht, keine Ahnung wie ich das benutzen soll

Naja. Sein Buch.

Auch wenn Loki bereits mehr beinhaltet, als das, was im Buch gezeigt wird, versteht man den Sinn dahinter, wenn man die lib anschaut.
-
Badestrand schrieb:
Ich mach ja auch nicht jedesmal einen Thread auf wenn ich mich freue ...
Das ist klar, sonst hättest dich ja bisher noch nie gefreut

Badestrand schrieb:
... und vor allem sollte es eigentlich gar nicht so ein langer Text werden.
Ist es aber geworden

Badestrand schrieb:
Klar erscheint der Thread seltsam, es ist ja auch ausnahmsweise einmal kein "Hab da mal ne Frage"-Thread sondern ein "Kanntet ihr schon?"-Thread.
Ok, also, ja, kannte ich schon

Badestrand schrieb:
Das mit dem FAQ-Eintrag/Artikel ist eine gute Idee, mach du doch, scheinst dich doch gut auszukennen

So weit reisse ich meine Klappe nun aber auch nicht auf, oder? Kenne mich zwar etwas damit aus, aber gut wäre wohl bereits übertrieben. Ich habe zum Beispiel schon erkannt, dass man IntelliSense und VAX damit ziemlich verwirren kann

Nicht ohne Grund, wäre ich an einem Artikel durchaus interessiert, deutlich mehr als nur an einem: "Hey, schaut! Sowas kann man mit Makros machen, unglaublich!"
Zudem ... hab eher wenig Zeit für sowas. Erst recht, jetzt wo das Studium wieder weitergeht. Sollte daher auch endlich ins Nest
Und nicht zu letzt, schreib ich nicht gerne Artikel, Pro-/Seminararbeiten usw. Bin halt kein Schreiberling
Badestrand schrieb:
~Und in der Annahme, dass du "tangieren" evtl falsch verstanden hast: http://lexikon.meyers.de/meyers/Tangieren~
Ich kannte das Wort tatsächlich nicht, habe es aber richtig interpretiert. Also insofern nichts falsch verstanden. Ich hatte schliesslich auch einmal Geometrie. Da weiss man was eine Tangente ist, daher konnte ich darauf schliessen, was es wohl bedeuten mag.

Grüssli
-
Dravere schrieb:
Badestrand schrieb:
... und vor allem sollte es eigentlich gar nicht so ein langer Text werden.
Ist es aber geworden

Ich wollte halt das Drumherum erklären und das auch noch verständlich - und du ahnst gar nicht wie lange man auf die Vorschau warten muss

Dravere schrieb:
Kenne mich zwar etwas damit aus, aber gut wäre wohl bereits übertrieben.
Muss ja nicht alles drin sein was es gibt auf der Welt, aber wenn's noch ein paar solche Sachen sind wie meine coole Makro-Magie (:)), ist's für Anfänger (oder ungebildete Fortgeschrittene wie mich) ne super Sache. Und guck mal im Infos-zum-Magazin-Forum, wieviele Artikel noch auf einen Autor warten, wenn du's nicht machst, dann wahrscheinlich keiner..
Dravere schrieb:
Nicht ohne Grund, wäre ich an einem Artikel durchaus interessiert, deutlich mehr als nur an einem: "Hey, schaut! Sowas kann man mit Makros machen, unglaublich!"

Mach dich nicht lustig, ich find das echt cool! Und abgesehen davon fände ich auch mehr solche kanntet-ihr-schon-Threads gut, dieses raffinierte C++-Draft-nullptr-Ding kenn ich ja auch erst seit einem viertel Jahr. Wenn vorher mal jemand einfach so informiert hätte, wäre das schon lange in meinen Sourcen drin; und man kann ja nicht zu jedem und allem einen Artikel machen, kostet einfach zu viel Zeit.
Apropos Zeit, wieso gehen deine Semesterferien schon zu Ende? Ich hab noch über 'nen halben Monat.. Und deine zweite Ausrede zählt auch nicht, eine Bachelor-/Master-/Diplom-/Sonstwas-Arbeit musst du sowieso irgendwann schreiben, da kannst du jedes bisschen vorheriger Übung gebrauchen
Oh, und so ein Artikel verschlingt ja auch keine Wochen sondern Stunden oder höchstens ein paar halbe Tage/Nächte 
Dravere schrieb:
Ich kannte das Wort tatsächlich nicht, habe es aber richtig interpretiert.
Ich bin mir da nicht so sicher
Ich kenne "tangiert mich nicht" als "ist mir egal", was bei deinem Post nicht zu "nur" gepasst hat, hätte doch dann eher "..und fand den Thread..." heißen müssen?
Aber ist auch wurst..Dravere schrieb:
Ich hatte schliesslich auch einmal Geometrie. Da weiss man was eine Tangente ist, daher konnte ich darauf schliessen, was es wohl bedeuten mag.

Jou, Mathematik im Alltag und lebensnah

