Sunday, June 22. 2008
Learned basic C++ from an inadequate tutorial.
#include <iostream.h> using namespace std; // thanks, joe void main(int argc, char** argv) { cout << "Hello, world!" << endl; }
Learned basic C++ from a slightly better tutorial.
#include <iostream> using namespace std; int main(int argc, char* argv[]) { cout << "Hello, world!" << endl; return 0; }
Learned about namespace pollution.
#include <iostream> int main(int argc, char* argv[]) { std::cout << "Hello, world!" << std::endl; return 0; }
Learned about classes.
#include <iostream> #include <string.h> class Hello { private: char* msg; public: Hello() { this->msg = strdup("Hello, world!"); } ~Hello() { delete this->msg; } char* hello() { return this->msg; } }; int main(int argc, char* argv[]) { Hello* hello = new Hello(); std::cout << hello->hello() << std::endl; delete hello; return 0; }
Learned about the C++ string container.
#include <iostream> #include <string> class Hello { private: std::string msg; public: Hello() { this->msg = "Hello, world!"; } std::string hello() { return this->msg; } }; int main(int argc, char* argv[]) { Hello hello; std::cout << hello.hello() << std::endl; return 0; }
Found out about the STL.
/usr/lib/gcc/x86_64-unknown-linux-gnu/4.3.1/../../../../include/c++/4.3.1/bits/ios_base.h: In copy constructor ‘std::basic_ios<char, std::char_traits<char> >::basic_ios(const std::basic_ios<char, std::char_traits<char> >&)’: /usr/lib/gcc/x86_64-unknown-linux-gnu/4.3.1/../../../../include/c++/4.3.1/bits/ios_base.h:783: error: ‘std::ios_base::ios_base(const std::ios_base&)’ is private /usr/lib/gcc/x86_64-unknown-linux-gnu/4.3.1/../../../../include/c++/4.3.1/iosfwd:52: error: within this context /usr/lib/gcc/x86_64-unknown-linux-gnu/4.3.1/../../../../include/c++/4.3.1/iosfwd: In copy constructor ‘std::basic_ostream<char, std::char_traits<char> >::basic_ostream(const std::basic_ostream<char, std::char_traits<char> >&)’: /usr/lib/gcc/x86_64-unknown-linux-gnu/4.3.1/../../../../include/c++/4.3.1/iosfwd:61: note: synthesized method ‘std::basic_ios<char, std::char_traits<char> >::basic_ios(const std::basic_ios<char, std::char_traits<char> >&)’ first required here test.cpp: In function ‘int main(int, char**)’: test.cpp:7: note: synthesized method ‘std::basic_ostream<char, std::char_traits<char> >::basic_ostream(const std::basic_ostream<char, std::char_traits<char> >&)’ first required here test.cpp:7: error: initializing argument 3 of ‘_Funct std::for_each(_IIter, _IIter, _Funct) [with _IIter = __gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, _Funct = std::basic_ostream<char, std::char_traits<char> >]’ /usr/lib/gcc/x86_64-unknown-linux-gnu/4.3.1/../../../../include/c++/4.3.1/bits/stl_algo.h: In function ‘_Funct std::for_each(_IIter, _IIter, _Funct) [with _IIter = __gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, _Funct = std::basic_ostream<char, std::char_traits<char> >]’: test.cpp:7: instantiated from here /usr/lib/gcc/x86_64-unknown-linux-gnu/4.3.1/../../../../include/c++/4.3.1/bits/stl_algo.h:3791: error: no match for call to ‘(std::basic_ostream<char, std::char_traits<char> >) (char&)’
Mastered the basic containers of the STL.
#include <iostream> #include <string> #include <vector> int main(int argc, char* argv[]) { std::string hello = "Hello, world!"; std::vector<char> v; for (unsigned int j = 0; j < hello.length(); ++j) { v.push_back(hello[j]); } std::vector<char>::iterator i; for (i = v.begin(); i != v.end(); ++i) { std::cout << <strong>i; } std::cout << std::endl; return 0; }
Mastered the STL.
#include <iostream> #include <string> #include <algorithm> class printc { public: void operator() (char c) { std::cout << c; } }; int main(int argc, char</strong> argv[]) { std::string hello = "Hello, world!"; std::for_each(hello.begin(), hello.end(), printc()); std::cout << std::endl; return 0; }
Read the C++ FAQ Lite and realized C++ mastery is impossible.
#include <iostream> int main(int argc, char* argv[]) { std::cout << "Hello, world!" << std::endl; return 0; }
Read the C++ FQA Lite.
#include <stdio.h> int main(int argc, char* argv[]) { printf("Hello, world!\n"); return 0; }
Heard of 4chan and wound up in /b/.
HAI
CAN HAS STDIO?
VISIBLE "HAI WORLD!"
KTHXBYE
Found a replacement for /b/ after finding it overrun by ``cancer'' and ``newfags.''
One word, the forced indentation of code. Thread over. Also, read SICP!
Read SICP after months of telling people to read SICP.
(display "Hello, world!") (newline)
Achieved Satori after re-reading SICP every day for many years.
void main() { puts("Hello, world!"); }
(gefunden irgendwo auf 4chan, Dank an nion)
Monday, January 28. 2008
David Tanzer recently blogged about comparing performance of equivalent C and C++ programs. He did so in order to be able to decide which of the two languages he should use as implementation language for his diploma thesis. In C, he used a classical dispatching mechanism, while in C++, he used inheritance and virtual methods. His result was that C++ was about 7 times slower with his test program. I couldn't believe this, and so I conducted my own tests, with a slightly modified source which can be downloaded here. I conducted tests of binaries with different optimization levels, all done with gcc/g++ 4.2.3 as provided by Debian unstable: Optimization | C (average of 3 runs; [s]) | C++ (average of 3 runs; [s]) |
---|
-Os | 38.778 | 34.001 | -O3 | 27.951 | 32.722 | -O2 -fomit-frame-pointer | 27.985 | 31.562 | -O0 | 33.271 | 34.192 |
Thursday, May 31. 2007
Es tut sich wieder was bei newsbeuter. Und zwar hab ich in den letzten Tagen eine Filtersprache entwickelt, um dynamische Abfragen für theoretisch alles zu ermöglichen, primär aber, um Feeds und Artikel in Feeds zu filtern, nach Kriterien wie Autor, Titel, Inhalt, Veröffentlichungsdatum, Anzahl der ungelesenen Artikel, URL des RSS-Feeds, usw. usf. Derzeit ist es so implementiert, dass man in der Feedübersicht die Taste F drückt, eine Abfrage eingibt, die gefilterte Feedübersicht präsentiert kriegt, und diese Filterung wieder löschen kann. Ein Abfrage sieht etwa so aus: unread_count > 0 and unread_count <= 20 Mit dieser Abfrage erhält man alle Feeds, die mindestens einen, aber 20 oder weniger ungelesene Artikel enthalten. Bald werden auch solche Abfragen möglich sein: ( author =~ "Frank" or author =~ "John" ) and ( title =~ "Linux" or title =~ "FreeBSD" ) Das würde alle Artikel der Autoren "Frank" oder "John" (als Text innerhalb der Quotes sind übrigens Regular Expressions möglich) mit "Linux" oder "FreeBSD" im Titel. Eine freie Gestaltung der Abfragen ist möglich, "the sky is the limit" wie es im Englischen so schön heisst. Die Abfrage rssurl =~ "^https:" filtert übrigens alle Feeds mit HTTPS-URL heraus. Wie man merkt, kommt man mit diesen Mitteln schnell zu vorher ungeahnten Möglichkeiten.
Implementiert habe ich den Parser für diese Abfragesprache übrigens mit Coco/R für C++, ein Compilergenerator, der an der Uni Linz entwickelt wird. Das ging ziemlich schnell und intuitiv, und mit den verfügbaren Beispielen für z.B. dem Compiler für die Minisprache Taste war es auch mir Compilerbau-Unerfahrenen möglich, schnell zu einem funktionierenden Parser zu gelangen. Die Code-Templates für die Generierung der Klassen rundherum (die "frame files") musste ich etwas adaptieren, etwa um aus einem std::istream statt aus einem FILE* zu parsen (was den Vorteil hat, über std::ifstream Dateien und über std::istringstream auch Strings anbinden zu können). Die Anbindung an newsbeuter selbst habe ich so gestaltet, dass ich einen Matcher implementiert habe, der einen geparsten Ausdruck gegen jedes beliebige Objekt prüfen kann, solange dieses nur von der Basisklasse matchable (enthält lediglich zwei pure virtual functions zum überprüfen, ob ein Attribut vorhanden ist, bzw. um dessen Wert abzurufen) abgeleitet ist. Damit erhalte ich die größtmögliche Flexibilität, und kann eben theoretisch jedes Objekt matchen (auch wenn das nicht wirklich bei allen Objekten innerhalb von newsbeuter Sinn macht). Die eigentliche Integration war mit diesen Vorarbeiten dann ein Kinderspiel, und mittlerweile ist die Funktion zum Verstecken der bereits gelesenen Feeds sogar über die Filtersprache implementiert (über exakt den gleichen Mechanismus hängen sich dann auch die anderen Filterabfragen rein). Erstmals präsentieren werde ich dieses neue Feature übrigens übermorgen auf den Linuxwochen Wien. Kommt zahlreich.
Wednesday, March 21. 2007
Um 7 Uhr hat der Wecker geklingelt, ich drehe mich im Bett um, und der erste Gedanke, der mir durch den Kopf geht: "Ich habe in der CXyzFile-Klasse ein Memory Leak beim Anlegen von CFooIterator-Objekten". Was dann auch gestimmt hat, und das erste, was ich heute in der Arbeit gefixt habe. Sehr schräg.
Friday, February 16. 2007
Nachdem meine verehrte Leserschaft über meine Berichte über die Entwicklung von newsbeuter das ncurses-UI-Toolkit STFL zumindest vom Namen her kennt, möchte ich ein wenig mehr über die Eigenschaften und Features dieser genialen Library schreiben, da sie mir bisher sehr beim Erstellen von Konsolen-UIs geholfen hat.
Die Basis von STFL ist die Layout-Beschreibungssprache, mit der man die grundsätzliche Ausrichtung und Positionierung sämtlicher Controls inklusive (optional) des Inhalts und der Farbgebung. Diese Beschreibungssprache wird in eine Datenstruktur geparsed, die dann später zur Laufzeit einfach modifiziert und abgefragt werden kann. Ein wichtiges Prinzip des Layouting bei STFL sind dabei Horizontal und Vertical Boxes. Man stelle sich z.B. den gesamten Bildschirm vor, den man zuerst in mehrere Boxen "untereinander" teilt. In so eine Box kann man dann ein Control platzieren, oder eine weitere vbox (macht nicht sehr viel Sinn), oder aber eine hbox, welche die Box wiederum in mehrere Boxen nebeneinander teilt, usw. usf. Zusätzlich kann man jeder Box noch die Information mitgeben, in welche Richtung sie expandieren soll. Mit diesem simplen Konzept hat man ein mächtiges Werkzeug bei der Hand, um brauchbare und komplexe Layouts für Konsolen-UIs zu designen.
Zur Laufzeit wird diese Layoutbeschreibung dann eingelesen (entweder aus einer Zeichenkette oder aus einem externen File, ich persönlich verwende dieses Skript um STFL-Layouts direkt in Programme einzubinden, aber trotzdem die Layouts in schönen externen Files im Sourcetree liegen zu haben), und die resultierende Datenstruktur wird dem Programmierer dann als opakes "stfl_form *" zur Verfügung gestellt. Dieses Handle kann dann verwendet werden, um den aktuellen Inhalt von Controls auszulesen, zu verändern, den aktuellen Fokus zu bestimmen und zu ändern, und auch um die Datenstruktur selbst zu modifizieren (damit ließe sich theoretisch zur Laufzeit das Layout umstellen, auch wenn das in der Praxis i.A. wenig zielführend ist). Und natürlich kann man das Formular starten und kriegt Benutzereingaben weitergereicht, auf die man entsprechend reagieren kann.
Eine derartige Layoutbeschreibung sieht übrigens so aus:
vbox @style_normal:bg=black,fg=white @info#style_normal:bg=blue,fg=yellow,attr=bold label#info text[head]:"Your feeds" .expand:h list[feeds] style_normal:bg=black,fg=white style_focus:fg=yellow,bg=blue,attr=bold .expand:vh pos_name[feedposname]: pos[feedpos]:0 label#info text[help]:"q:Quit ENTER:Open r:Reload R:Reload All A:Mark All Read C:Catchup All" .expand:h label text[msg]:"" .expand:h
Das ist das Layout der Feedlist, wie sie bei newsbeuter beim Programmstart erscheint. Es besteht aus einer vbox, welches von oben nach unten ein Label für die Überschrift, eine Liste mit den Feeds, ein Label mit Keyboard Shortcuts sowie ein Label für Information und Fehler enthält, alles gespickt mit diversen Details wie Farbgebung und der Information für die Labels, dass sie horizontal expandieren sollen, sowie für die Liste, dass sie horizontal und vertikal expandieren soll (und damit den meisten Platz ausfüllt).
Insgesamt ist das Interface von STFL sehr kompakt (14 API-Funktionen). Die Konstruktion eines Programms erfolgt meistens nach dem gleichen Prinzip: Initialisierung - Event Loop - Ressourcenfreigabe. Die Initialisierung ist bei STFL eine ganz simple:
char * my_form = "..."; struct stfl_form * f1 = stfl_create(my_form); struct stfl_form * f2 = stfl_create("<form.stfl>");
Der erste Schritt ist immer, ein Formular anzulegen, und zwar mit stfl_create(). Dabei gibt es zwei Varianten, und zwar, einen normalen String mitzugeben, dann wird der String als Layoutbeschreibung geparsed, oder aber, man übergibt einen String, der mit '<' anfängt, und mit '>' endet, dann wird der Inhalt der Datei, deren Name zwischen '<' und '>' steht, als Layoutbeschreibung verwendet. Damit kann man auch Layouts aus externen Dateien laden, ohne diese Datei selbst öffnen und auslesen zu müssen, und man muss auch keinen extra API-Call bedienen.
Der nächste Schritt ist dann, das Formular zu starten, und optional auch auf Eingaben des Users zu warten. Und zwar funktioniert das mit stfl_run().
char * event = NULL; event = stfl_run(f1, 0); stfl_run(f1, -1);
Der zweite Parameter gibt dabei, wie lange auf Benutzereingabe gewartet werden soll, welche dann zurückgegeben wird. Der Wert "-1" bedeutet, dass keine Benutzereingaben eingelesen werden (wenn man z.B. lediglich das Formular neu zeichnen will), und 0 bedeutet, dass unendlich lange auf Benutzereingabe gewartet wird. Bei einem Wert > 0 wird soviele Sekunden gewartet. Findet bis dahin keine Benutzereingabe statt, so wird von stfl_run() NULL zurückgeliefert. Hierbei sollte man allerdings auf jeden Fall beachten, dass es auch sein kann, dass auch bei einer Wartezeit von 0 NULL als Rückgabewert zurückkommen kann, weil z.B. ein Tastendruck von dem Widget, das den Fokus hat, "konsumiert" wurde.
Die zurückgegebene Eingabe kann folgendes sein: - "TIMEOUT": bei einem Timeout
- "ENTER": wenn die Eingabetaste gedrückt wurde
- "ESC": wenn die Escape-Taste gedrückt wurde
- "F0" .. "F63": wenn eine F-Taste gedrückt wurde
- "CHAR(%u)" (wobei %u einem unsigned integer entspricht): eine andere Taste wurde gedrückt, und der numerische Wert dieser Taste befindet sich zwischen den Klammern. Der numerische Werte für die Taste "a" ist z.B. 97. Und auch Ctrl-Tastenkombination sind damit abfragbar, so ist etwa Ctrl-A der numerische Wert 1, Ctrl-B der Wert 2, usw.
Am Ende eines Programmes ist es dann noch zweckmäßig, die Funktion stfl_reset() aufzurufen, um den Bildschirm entsprechend zu resetten. Mit diesem Wissen kann man schon ein nettes kleines Programm schreiben, das so eine typische Event Loop demonstriert, und zusätzlich führe ich auch noch ein paar weitere Funktionen ein:
static char form_str[] = "vbox\n" " @style_normal:bg=black,fg=white\n" " @info#style_normal:bg=blue,fg=yellow,attr=bold\n" " list[results]\n" " style_normal:bg=black,fg=white\n" " style_focus:fg=yellow,bg=blue,attr=bold\n" " pos_name[listposname]:\n" " pos[listpos]:0\n" " label#info\n" " .expand:h .height:1\n" " text[help]:\"q:Quit\"\n" " label\n" " .expand:h .height:1\n" " text[msg]:\"\"\n"; #include <stfl.h> #include <stdio.h> int main(void) { int quit = 0; struct stfl_form *f = stfl_create(form_str); do { const char * event = stfl_run(f, 0); if (!event) continue; if (strncmp(event,"CHAR(",5)==0) { unsigned int key; if (sscanf(event,"CHAR(%u)",&key)==1) { switch (key) { case 'q': quit = 1; break; case 'a': stfl_set(f, "msg", "you just pressed a"); break; case 'b': stfl_set(f, "msg", "you just pressed b"); break; case 'c': stfl_modify(f, "results", "replace_inner", "{list{listitem[item1] text:\"list item 1\"}{listitem[item2] text:\"list item 2\"}}"); stfl_set(f, "msg", "you just pressed c and replaced the content of the list"); break; case 'd': stfl_modify(f, "results", "replace_inner", "{list{listitem[line1] text:\"line 1\"}{listitem[line2] text:\"line 2\"}{listitem[line3] text:\"line 3\"}}"); stfl_set(f, "msg", "you just pressed d and replaced the content of the list"); break; case 'e': { const char * listpos = stfl_get(f, "listpos"); const char * listposname = stfl_get(f, "listposname"); char line[1024]; snprintf(line,sizeof(line),"listpos = %s listposname = %s", listpos, listposname); stfl_set(f, "msg", line); } } } else { char line[1024]; snprintf(line,sizeof(line),"you pressed: %s", event); stfl_set(f, "msg", line); } } } while (!quit); stfl_free(f); stfl_reset(); return 0; }
Was man hier sieht, ist eine klassische Event Loop, wobei auf die Tasten a bis e sowie q reagiert wird. Es werden ja nach Tastendruck ein Label bzw. die Liste modifiziert. Mit der Taste e enthält man zusätzlich noch Informationen über die aktuelle Auswahl in der Liste.
Man kann noch wesentlich mehr mit der STFL machen, und dieser Artikel soll nur einen kleinen Einblick in die grundsätzliche Funktionsweise von STFL bringen, um dem einen oder anderen unter euch das Entwickeln von eigenen, neuen Texttools schmackhaft zu machen. Für mehr Informationen sowie andere Widgets wie Eingabefelder oder mehrzeige Text Views ist es ratsam, in die STFL-Dokumentation sowie die existierenden Beispielprogramme reinzuschauen. Es lohnt sich.
Wer übrigens die STFL "C++iger" verwenden will, den kann ich nur auf meinen "stflpp"-Wrapper hinweisen, den man hier (Header) und hier (Source) findet (MIT/X-Lizenz, BTW). Die STFL selbst unterstützt neben C übrigens auch SPL, Ruby, Python und Perl.
Sunday, December 10. 2006
Dieses Wochenende hab ich mir Zeit genommen, um weiter an noos, meinem RSS-Feedreader zu arbeiten. Die Features werden mehr, und mittlerweile ist noos schon in einem Zustand, wo ich sagen muss, dass ich den schon gerne verwende. Und um meine werten Leser up-to-date zu halten, zeige ich eine kleine "Fotostrecke" mit den neuesten Screenshots.
Wenn man die Screenshots mit vergangenen noos-Screenshots vergleicht, so fallen sicher ein paar kleine Unterschiede auf. Neu dazugekommen ist, dass man Artikel nun auch als Textdatei abspeichern kann, inklusive schönem Save-Dialog. Was man nicht so gut sehen kann, ist, dass mittlerweile das Keymapping über die Konfiguration frei einstellbar ist, und die aktuelle Einstellung auch in Keymap-Hints unten angezeigt wird. An diversen Stellen sind Verbesserungen des HTML-Rendering vorgenommen worden, und auch ansonsten ist etliches umstrukturiert und refactored worden. Es gibt auch einen einfachen Help-Dialog (kein Screenshot vorhanden), und Dokumentation ist ebenfalls schon ausgearbeitet. Vor dem ersten Release will ich ein paar Codeteile allerdings noch sauberer und kompakter machen, dann wird's aber voraussichtlich auch gleich Debian-Pakete etc. zum Download geben.
Sunday, December 3. 2006
Wie berichtet, arbeite ich ja an einem RSS-Feedreader für die Konsole. Die letzten paar Tage habe ich nun wieder etwas mehr Arbeit reingesteckt. Ein großes Manko bisher war ja das absolut schlechte HTML-Rendering. Inspiriert durch Martin habe ich einen minimalistischen XML Pull Parser in C++ gebaut, der gerade soviel kann, um das in RSS-Feeds transportierte (X)HTML brauchbar aufbereiten und darstellen zu können. Auf Basis von diesem habe ich dann einen minimalistischen HTML-Renderer entwickelt, der bisher zwar nur mit relativ wenig Tags was anfangen kann (a, br, img, blockquote, p), aber bei den getesteten Feeds ein ganz ansehnliches Ergebnis gebracht hat. Nebenbei hab ich dann auch noch die Darstellungsprobleme von Umlauten behoben (libidn macht die Arbeit, für die, die sich nicht mit iconv, wcstombs und Konsorten herumschlagen wollen) und den Konfigurationsparser in einen verwendbaren Zustand gebracht. noos entwickelt sich, und ich hoffe, zur Zeit des 23C3 bei den anwesenden Texttool-Evangelisten erfolgreich missionieren zu können.
Update: ich hab mich jetzt noch ein wenig hingesetzt, und schnell mal ordered und unordered lists implementiert. Yee-ha.
Tuesday, November 21. 2006
In den letzten 2 Wochenenden hab ich an einem neuen Tool gebastelt, und zwar an noos, einem RSS-Feedreader für die Konsole. Zwar gibt es sowas schon, wie etwa snownews und raggle. Mit beiden Readern war ich aber nicht sehr zufrieden, snownews etwa wirkte auf mich immer etwas krude und das Interface etwas plump zusammengehackt, und raggle verhielt sich immer äußerst behäbig. Also hab ich mich mal rangesetzt, und was eigenes geschrieben. Und somit kann die Version 0.1 von noos präsentieren, die allerdings noch eher ein pre-alpha-Version ist, mit lediglich den rudimentärsten Features.
noos 0.1 hier runterladen
Die Abhängigkeiten sind etwas umfangreicher. Als UI-Toolkit habe ich STFL verwendet, da ich damit schon ein paar Prototypen und halbfertige Projekte realisiert habe. Zum Downloaden und Parsen der RSS-Feeds hab ich auf libmRss zurückgegriffen, welche wiederum abhängig ist von libnxml, die ich für den OPML-Import verwendet habe. Und als Storage-Backend für den Item-Cache hab ich mich für das bewährte SQLite entschieden. Zwei Wochenende und 1000 Zeilen C++ später steht also eine erste verwendbare Version. Die Features sind noch etwas dürftig, und das Rendering des HTML in den Descriptions ist noch total zum vergessen, aber es ist schon ein guter Ansatz sichtbar.
Und hier noch ein Screenshot:
Feedback welcome.
Saturday, September 2. 2006
Nach mittlerweile fast 4 Jahren bei WebDynamite habe ich mich entschlossen, beruflich neue Wege zu gehen und neue Bereiche kennenzulernen. Fündig geworden bin ich bei Borland, die seit kurzem praktischerweise ein Development Lab in meiner Heimatstadt Linz haben. Bisher habe ich mich im Bereich Web und mobile Applikationen bewegt, und so ziemlich alles von Webprogrammierung über WAP, WAP-Push, Symbian-OS-Programmierung bis hin zur Mitentwicklung des WAP-Frontends einer Online-Musikdownload-Lösung gemacht, und nun war es für mich einfach an der Zeit, mich neuen Herausforderungen zu stellen. Meine voraussichtlichen Tätigkeiten bei Borland wird im Bereich Netzwerkprogrammierung liegen, vorwiegend mit C++, also auf jeden Fall eine Möglichkeit, die Dinge, mit denen ich mich privat schon beschäftigt habe, nun auch beruflich umsetzen zu können. Am 2. Oktober beginnt der neue Job, und ich freu mich schon drauf.
Sunday, June 25. 2006
Today I finally sat down to remodel my HTTP stack/server from a forking model to a multithreaded model using the pthread library. Actually, this is my first project where I use POSIX threads, and I was delighted how easy it was to integrate them into my relatively well-designed program.
The server now works as follows: the "main" thread first creates a number of worker threads, and then accepts connections, puts them into a queue and then notifies the worker threads. The next worker thread that is ready takes the connection from the queue and handles the incoming requests. This is definitely a very simple design, which was also very easy to implement. The results can be found in httpstack/branches/pthread in the SVN repository. As usual, comments are welcome.
The next thing is where I will move that project. One idea I had was to integrate a database connection pool, and in addition probably a simple object-relational mapper, then an HTTP management interface for the whole thing and then some CMS.
Friday, June 9. 2006
In the last few days, I was working hard to finish a first working version of my "cwapd" (totally uninspired name, I know, it's supposed to mean "C++ Web Application Platform Daemon"). It is based on my previously-mentioned HTTP stack.
The basic concept of cwapd is that one or several base URLs can be mapped to so-called "contexts". As soon as a URL that begins with one of the registered base URLs is called, the "action" (i.e. the part of the URL after the base URL) is handed over to the registered context. This context then handles the request, and returns the result. As a feature, methods can be easily associated with actions (see my previous posting), which means that no manual dispatching is necessary. Alternatively, actions can also be handled manually, e.g. for cases where the "action" actually represents something like a filename (think of a context that delivers static files like a "normal" web server).
As a simple example, I also created a "wallpaper_context" class that automatically scales images to the optimal wallpaper size of the respective client, employing Magick++ and WURFL. This could be e.g. useful for people who want to deliver mobile phone wallpapers via WAP. The code itself is really simple, the wallpaper_context class contains nothing more than 77 SLOCs, showing that writing custom contexts is extremely simple.
The only "big" thing that is still missing from it is proper handling of configuration. Currently, many things that are actually supposed to be stored in configuration files are hard-coded in the source code. So, if anybody could recommend me a flexible configuration engine with an interface to C++, I'd be happy about it.
Anyway, if you're interested in the code, simply have a look at the SVN repository.
Wednesday, June 7. 2006
I just created a pretty neat callback mechanism for C++: template<typename T> class callback { public: callback(T* obj, unsigned int (T::*func)(string, ostream&, header_container&, cgiparam_container& )) : callee(obj), cb(func) { } ~callback(); unsigned int run(string actionstr, ostream& os, header_container& hdrs, cgiparam_container& params) { return (callee->*cb)(actionstr,os,hdrs,params); } private: T* callee; unsigned int (T::*cb) (string,ostream&, header_container&, cgiparam_container& ); }; class context { public: context(); ~context(); unsigned int dispatch_action(string actionstr, ostream& os, header_container& hdrs, cgiparam_container& cgiparams); protected: void register_action(string,callback<context>*); private: map<string,callback<context>*> actions; };
The implementation of register_action() looks like this: void context::register_action(string action, callback<context>* cb) { actions[action] = cb; }
And this is used in the following way (called from within the context class): register_action("foobar",new callback<context>(this,&context::some_method));
Now, the problem that I have is that that adding callbacks that are not actually within the context class but within a derived class is only possible with lots of ugly casting (but it seems to work afterwards): #define REGISTER_ACTION(name,action) do { register_action((name),new callback<context>((context*)this, (unsigned int (context::*)(std::string, std::ostream&, net::header_container&, net::cgiparam_container&))&action)); } while(0)
Do I see correctly that C++ seems to have certain problems with derived classes and templates, especially when it comes to using pointers to methods? Any input is welcome on how to remove this ugly casting while keeping the same functionality.
Tuesday, May 9. 2006
Although probably not quite finished yet, I decided to make my HTTP server stack for C++ publicly available (use Subversion to check out). Running "make" first compiles the stack itself an then simplehttpd, a simple HTTP server example only able to serve static files, and implementing simplehttpd was pretty easy.
And that leads me to a topic that I discussed before here. During implementation of simplehttpd, I thought about embedding some interpreter, in order to make it in some way possible to have dynamic content. During the research about this topic, I came across NJS, an embeddable, LGPL-licensed JavaScript interpreter. According to the examples delivered with it, it seems to be pretty easily usable, and even features cool stuff like compilation to byte code before execution. When I saw this, it immediately triggered my wildest fantasies of bytecode caching mechanisms.
Now I have two options: I could simply embed NJS into the existing simplehttpd (whenever such a server-side JS file is requested, it is executed by the interpreter and its output is sent back to the client), or I could build a servlet container around NJS. What do you think?
Wednesday, May 3. 2006
What a productive night... after several months, I just again wrote some lines of code. What did I do? I took the HTTP server stack that I built a few months ago in my effort to create a fast Ruby servlet container (which failed due to technical limitations in the Ruby interpreter), streamlined it in a few points, and used it to build a simple HTTP server that delivers static files, only. So far, the HTTP server is really very simple, but the size of both the stack and the server are quite nice: the stack contains of about 1000 lines of C++ (employing the C++ standard library, btw), while the server consists of about 200 lines. The latter one is still quite buggy in a few places (the prevention of /../../ URLs doesn't work quite correctly yet, nor does the directory index listing), but in its basic functionality, it works pretty good. Expect a release of both in a few days, when everything has been freed from the obvious bugs and cleaned up.
Friday, December 23. 2005
This is probably already well-known to some of you, but nevertheless, I played a little bit with C++ templates, and wrote a piece of C++ template code that lets the compiler computer a number's factorial for you. template <int N> struct fact { enum { value = N * fact<N-1>::value }; }; template <> struct fact<1> { enum { value = 1 }; };
Not very useful, but nevertheless fun. You use it the following way: std::cout << "5! = " << fact<5>::value << std::endl;
|