Motivation:

As you can see from my earlier posts I got over the last couple of month a little bit in contact with Perl as a script language being used as a substitution for the old Dos-/Windows batch processor for some general script based tasks and in special for creating an automatic and full customizable build system for a larger Visual C++ project with a bunch of targets and some resulting distribution packages (installers and zip-archives).

For practicing purposes I invested a bit time in investigating about the OOP-features of Perl. When you read about it in the internet a lot of people share the opinion that Perl’s default OOP-support works well, but it requires a lot of tedious work for the programmer to set up a class. They recommend you to give Moose a try, which is a relatively new module of Perl. It promises to make OOP under Perl easy and is already deployed in production by some real companies.

To learn Moose by doing I decided to program a little script which mimics in a smaller feature subset the possibilities of the UNIX console program “find” as I know it from my earlier days. It should give me under Windows (and of course under other OSes where Perl is available too) the possibility to do a recursive file search using some include- and exclude-filters and let on the result set happen some actions like “print”, “put to an archive” or “clean the matching” files.

ffind.pl:

The resulting script has the name ffind.pl (file find).  Here are some examples for using it on the Windows command line:

1
2
3
4
5
6
7
8
9
10
11
REM Match in users temp dir all items whose name contains 
REM substring ‘pic’ except the gifs and jpgs:
perl ffind.pl C:\Users\daniel\Temp -ni "*pic*" -ne "*.gif,*.png"

REM Create a zip-archive of all text files w. automatic current 
REM timestamp name in dir ‘c:\users\daniel\backups’:
perl ffind.pl ./ -ni “*.txt” –zip “c:\users\daniel\backups”

REM Delete / clean all object files. Don’t display file deletion
REM warning / confirmation:
perl ffind.pl ./ -ni “*.o,*.obj” -fclean

View / download the files and the final conclusion:

I wrote a little blog post report about the implementation of ffind.pl as a pdf. The addressed topics can best be described by the following tags:

  • Perl
  • OOP
  • Moose
  • Software Tests
  • Test::More
  • Command line Parsing
  • Options
  • Getopt::Long

You can view/download it here plus the script (of course) and a little directory tree of empty files and folders for testing the script:

My final conclusion about Moose & Perl:

Finally it’s a nice addition to Perl to have the possibility to leverage now the powers of OOP more easily once you have managed to overcome the first obstacles of Moose. In detail if you have made a mistake it’s not easy to get known from the error message the real cause of the error. But so far I guess that’s more a general problem of Perl, not Moose 😉 So if you have the choice you might want to give Python a try as it is object oriented by design and a lot easier to learn without being less powerful than Perl.

Distorted Harmony: Utopia

Wow, bin ich über Facebook drauf gestoßen. Gibt es wohl erst seit drei Jahren und ist das Debüt-Album. Die brauchen den Vergleich mit echt großen Namen nicht zu scheuen. Das ganze Album kann man sich hier anhören:

https://www.facebook.com/distortedharmony?sk=app_129128463812401

Wem es gefällt kann es für 0.99$ von hier downloaden:

http://distortedharm0ny.bandcamp.com/

Related Artists: Symphony X, Dream Theater.

DG

Wieder da!   November 11th, 2012

Hallo liebe Blog-Reader,

da ein wichtiges Großprojekt meine restliche freie Zeit in Anspruch genommen hat, bin ich seit Juni letzten Jahres eigentlich nicht mehr zum bloggen gekommen. Das Projekt wurde erfolgreich abgeschlossen und ich hoffe, dass ich jetzt wieder ein bisschen mehr Zeit für sinnige und unsinnige Posts habe!

VG,

Daniel.

Posted in General | No Comments »

Hier eine Rezension zu dem Buch Einführung in Perl:

Einführung in Perl

Einführung in Perl

Die Programmiersprache Perl bietet kurze Konstrukte für schnelle Lösungen von Problemen, die in der Text- und Skriptverarbeitung häufig vorkommen. Dazu zählen auch, aber nicht nur, dass Arbeiten mit Verzeichnissen und Dateien und das Lesen und Schreiben von Textdateien. Eine besondere Stärke von Perl ist hierbei, dass reguläre Ausdrücke bereits in der Sprache integriert sind. So effizient sich mit dieser Sprache derartige Probleme lösen lassen, so schwierig ist jedoch für den Einsteiger, das Erlernen der Sprache, da viele einzelne Zeichen eine Bedeutung haben und dadurch die Grammatik der Sprache komplexer als zum Beispiel von Java, Javascript oder PHP ist. Erschwerend kommt noch hinzu, dass es in Perl vordefinierte Variablen gibt. Wenn man diese nicht kennt, ist der Code noch schwieriger zu verstehen. Außerdem kann eine Variable im Listenkontext oder im Datenkontext benutzt werden. Aus den genannten Gründen ist das Erlernen der Sprache ohne ein Buch wie dem vorliegenden sehr schwierig. Ich habe bereits einige Erfahrung in C++ und auch ein paar Jahre PHP programmiert. Mit diesem Buch war ich nach vier Tagen in der Lage produktiven Code in Perl  für ein automatisiertes Buildsystem für ein C++ Projekt zuschreiben. Softwareentwickler, die bereits in anderen Sprachen Erfahrung haben, sollten mit diesem Buch gut über die Runden kommen. Neben der Beschreibung der Perl-Syntax und den Bibliotheken von Perl, die man wohl immer braucht ist auch eine Einführung zu regulären Ausdrücken enthalten. Dieses Wissen ist dann auch jenseits von Perl von großem Nutzen. Zu jedem Kapitel gibt es Übungsaufgaben mit Lösungen. Ich kann dieses Buch sehr empfehlen und würde es mir wieder kaufen. Dieses Buch ist jedoch kein Kompendium, sondern nur eine Hilfe zum Einstieg in die Sprache, den Grundlagen und den Aufgaben, die wohl mehr oder weniger zu jedem Skript gehören.

Gruß, Daniel.

Hallo zusammen,

in diesem Post möchte ich euch etwas erzählen, über das Entkoppeln von Objekten durch Callbacks mit C++-Interfaces oder boost::function und boost::bind. Der Post ist diesmal als PDF Datei eingebunden. Hintergrund ist, dass das PDF einem Workshop entstammt, den ich im November 2010 zu eben diesem Thema in unserer Firma gehalten habe. Als Beispiel sind zwei Klassen vorgegeben: eine Klasse A und eine Klasse B. Klasse A inkludiert die Klasse B und benutzt diese. Im ersten Szenario muss die Klasse B von der Klasse A nichts wissen. Im zweiten Szenario muss die Klasse B auch die Klasse A zum Zwecke eines Rückrufs kennen. Dazu benutzt sie einen Zeiger auf A, was diverse Probleme mit sich bringt. In den folgenden Lösungsansätzen wird aufgezeigt, wie mithilfe von C++ – Interfaces oder boost::bind und boost::function die Klasse B von der Klasse A entkoppelt werden kann. In einem weiteren Szenario wird gezeigt, wie man mithilfe von Vorwärtsdeklaration, dem Verwenden eines Zeigers in der Deklaration (anstatt einer kompletten Klasse) dem inkludieren der Klassendeklaration in der CPP-Datei und dem Anlegen des verwendeten Objekts im Konstruktor und dem Zerstören des Objekts im Destruktor, die Include-Abhängigkeiten nach oben terminiert werden können, was sich wiederum positiv auf die Übersetzungszeiten auswirkt. Im Anschluss daran folgen noch ein paar weitere Beispiele zu verzögerten Funktionsaufrufen mithilfe von boost::function und boost::bind. Für Tipps, Korrekturen und konstruktive Kritik (, natürlich aber auch für Zuspruch :-)) bin ich sehr dankbar. Viel Spaß mit dem PDF:

c++_entkoppeln_durch_interfaces_oder_boost_funktions_zeiger

Gruß,

Daniel

Tour auf die Hintere Goinger Halt   December 15th, 2010

Hallo zusammen,

obwohl die Tour schon ein Weilchen her ist, möchte ich doch noch über sie berichten, da es sich um eine sehr schöne Bergtour handelt. Letzten Sommer war ich im August im Kaisergebirge unterwegs. Ich entschied mich für die Bergtour zur Hinteren Goinger Halt (2192 m). Ausgangspunkt war die Wochenbrunner Alm auf 1085 m Höhe. Um kurz vor 7:00 Uhr brach ich von der Wochenbrunner Alm aus auf und marschierte durch das Waldgebiet ca. 1 h lang zur Gruttenhütte auf 1619 m.

Blick von der Wochenbrunner Alm aus.

Blick von der Wochenbrunner Alm aus.

Die Gruttenhütte im Visier.

Die Gruttenhütte im Visier.

Dort genoss ich erstmal die schöne Aussicht auf den gegenüberliegenden Großvenediger und genehmigte mir ein kleines Frühstück.

Frühstück auf der Gruttenhütte.

Frühstück auf der Gruttenhütte: Blick zum Kaiser ...

Blick auf den Großvenediger.

... und Blick auf den Großvenediger.

Nach dieser kurzen Stärkung, ging es dann weiter zum Jubiläumssteig. Der Jubiläumssteig ist ein einfacher gesicherter Klettersteig, der sich gut für Anfänger eignet und so konnte ich auch hier zum ersten Mal mein Klettersteig-Set ausprobieren. Nichtsdestotrotz ist es ein sehr schöner Klettersteig, den ich nur empfehlen kann.

Im Jubiläumssteig.

Im Jubiläumssteig.

Im Jubiläumssteig.

Leiter im Jubiläumssteig

Im Jubiläumssteig.

Immer schön gesichert!

Nach dem Jubiläumssteig, kommt man ins Kübelkar, einem Kar aus Schutt.

Vom Jubiläumssteig ins Küblkar.

Vom Jubiläumssteig ins Küblkar.

Während man durch das Kar wandert, kann man auf der linken Seite eine steile Felswand sehen, an der echte Felskletterer klettern. Allein beim zuschauen wird einem schwindlig.

Felskletterer in luftigen Höhen.

Felskletterer in luftigen Höhen.

Am Ende des Kars angekommen, hat man das Ellmauer Tor erreicht. Dieser Sattel ist eine beliebte Pausestation für die Wanderer. Das haben auch die Vögel bemerkt, die auf den einen oder anderen Brocken Brot oder Wurst warten.

Blick auf Wochenbrunner Alm. Links: Ein (sympatischer) Mittesser.

Pause im Ellmauer Tor

Nach einer kurzen Verpflegungspause, ging es rechter Hand hinauf zur Hinteren Goinger Halt. Diese ist nur an einer kurzen Stelle gesichert, ansonsten handelt es sich um leichtere Felskletterei, die man aber nicht auf die leichte Schulter nehmen sollte. Vorsicht ist hier geboten, daran erinnert auch ein Kreuz eines hier herunter gefallenen. Nach ca. einer Dreiviertelstunde hatte ich den Gipfel erreicht und genoss vorsichtig die fantastische Aussicht. Vorsichtig, weil mittlerweile ein leichter Wind aufkam. Schwindel erregend ist auch die Aussicht vom Gipfel auf das Stripsenjochhaus. Bereits auf dem Aufstieg, traf ich ein Pärchen aus Riedenburg, mit denen ich im losen Verbund den Aufstieg und den Abstieg gemeinsam durchführte. Leider habe ich ihren Namen vergessen. Trotzdem richte ich hier ihnen einen schönen Gruß aus, falls Sie zufällig auf diesen Post stoßen sollten. Vielen Dank auch für das Gipfelfoto!

Gipfelblick auf die Stripsenjochhütte.

Gipfelblick auf die Stripsenjochhütte.

Schwindelerregender Gipfelblick (ins Griesener Kar?)

Schwindelerregender Gipfelblick (ins Griesener Kar?)

Erste Hälfte gut überstanden. Jetzt noch der Abstieg ...

Erste Hälfte gut überstanden. Jetzt noch der Abstieg ...

Auf dem Weg nach unten machte sich dann die abgelaufene Sohle meiner Wanderschuhe negativ bemerkbar. Im Schutt und Geröll, bin ich ein paar Mal hin gefallen, Gott sei Dank jedoch ohne größere Verletzungen. Ich werde mir wohl ein paar neue Wanderschuhe kaufen müssen.

Im Abstieg: Meine Riedenburger Mitwanderer.

Im Abstieg: Meine Riedenburger Mitwanderer.

Rutschpartie abwärts ins Küblkar.

Rutschpartie abwärts ins Küblkar.

Immer schön vorsichtig!

Immer schön vorsichtig!

Ich stieg nicht wieder zur Gruttenhütte hinunter, sondern diesmal direkt zur Gaudeamushütte. Dort genehmigte ich mir zwei Getränke, da mein Wasservorrat mittlerweile aufgebraucht war.

Nach 7 h Tour auf der Gaudeamus-Hütte.

Nach 7 h Tour auf der Gaudeamus-Hütte.

Nach einem kurzen Abstieg war ich wieder am Ausgangspunkt meiner Tour, der Wochenbrunner Alm angekommen.

Insgesamt war ich 8 h unterwegs und muss sagen, dass es eine sehr schöne und abwechslungsreiche Bergtour war, die ich gerne noch einmal wiederholen werde. Meine nächste Tour im Kaisergebirge, soll jedoch auf die Ellmauer Halt auf 2344 m gehen.

Bis zum nächsten Post,

Daniel

In meinem heutigen Post möchte ich euch etwas erzählen über den shared_ptr und seine Fähigkeit allozierte Ressourcen abweichend von delete mit einer benutzerdefinierten Funktion oder einer benutzerdefinierten Methode zu entfernen. Nehmen wir einmal an, wir hätten eine Klasse, welche Speicher verwaltet und auf Anfrage Handles auf Speicher herausgibt und über die vergebenen Handles den Speicher wieder löscht. Um Speicherlöcher zu finden zählt die Klasse den vergebenen und wieder beseitigten Speicher mit. Eine solche Klasse könnte wie folgt aussehen:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include "stdafx.h"
#include <assert.h>
#include <boost\shared_ptr.hpp>
#include <boost\function.hpp>
#include <boost\bind.hpp>
#include <iostream>
 
typedef unsigned char MemChunk;
typedef unsigned char* MemHandle;
 
class MemResourceFabric
{
private:
  MemResourceFabric():count_(0){}
public:
  static MemResourceFabric* instance(){static MemResourceFabric i;return &i;}
  ~MemResourceFabric()
  {
    assert(count_==0);
  }
  MemHandle openMemory()
  {
    count_++;
    return new unsigned char[100];
  }
  void closeMemory(MemHandle mem_handle)
  {
    count_--;
    delete [] mem_handle;
  }
private:
  int count_;
};

Über die Funktion openMemory() wird der Speicher angefordert, über die Funktion closeMemory(MemHandle mem_handle) wieder freigegeben. Wenn nun innerhalb einer Funktion eine Ausnahme auftritt, könnte es sein, dass der angeforderter Speicher nicht mehr freigegeben wird, da unter C++ die Funktion closeMemory nicht von selber aufgerufen wird. Hier kann uns das unter C++ sehr weit verbreitete Idiom RAII (Resource Alocation Is [Resource] Initialization) mithilfe von boost::shared_ptr und boost::bind helfen. Normalerweise gibt der shared_ptr verwaltete Ressourcen in seinen Destruktor per delete wieder frei. Man kann ihm aber auch einen Funktor übergeben, welcher die verwaltete Resource anstelle von delete wieder freigeben soll. Im vorliegenden Beispiel soll der angeforderter Speicher mithilfe der Funktion closeMemory wieder freigegeben werden. Als Instanz soll unser Singleton, MemResourceFabric verwendet werden:

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
int _tmain(int argc, _TCHAR* argv[])
{
  boost::shared_ptr<MemChunk> shp_mem_1(
    MemResourceFabric::instance()->openMemory(),
    boost::bind(
      &MemResourceFabric::closeMemory,
      MemResourceFabric::instance(),
      _1)
  );
 
  strcpy(
    (char*)shp_mem_1.get(),
    "Hello memory. Memory wird auch bei Ausnahmen deleted.");
 
  std::cout << (char*)shp_mem_1.get() << std::endl;
 
  return 0;
}

Diese Technik ist in einer Reihe von Szenarios sehr hilfreich, bei denen die Ressourcen abseits von delete verwaltet werden: Datenbank öffnen, Netzwerkverbindung öffnen, Datei öffnen, Synchronisation von Threads, et cetera…

Viel Spaß mit RAII und bis zum nächsten Post,

Daniel.

In einem vorherigen Post wurden callbacks mit Hilfe von verzögerten Funktionionsaufrufen vorgestellt. Reallisiert wurden diese mit Funktionsobjekten (boost::function), die mit boost::bind gebaut wurden.

Mit Hilfe von std::for_each und boost::bind kann man Methoden von nicht nur einer, sondern von mehreren Instanzen einer Klasse in STL-Collections aufrufen. Der Algorithmus std::for_each erwartet als drittes Argument eine Referenz auf einen Funktor.  In der Iteration über alle Elemente der Collection übergibt er dem Funktor eine Referenz auf das aktuelle Element. boost::bind baut uns diesen Funktor. Dieser Funktor wiederrum ruft dann für jede Instanz in der Collection die übergebene Methode mit den übergebenen Parametern in der angegebenen Reihenfolge auf. Die Instanzen können in der Collection abgelegt sein als Kopie, Pointer oder Shared Pointer.

Beispielklasse A

Betrachten wir dazu eine Beispielklasse A mit 3 Methoden, die je eine unterschiedliche Signatur haben:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
namespace classes
{
  class A
  {
  public:
    void f_1()
    {
      boost::format f(
        "%1% called. instance-addr: 0x%2%");
      f % __FUNCTION__ % this;
      std::cout << f.str() << std::endl;
    }
    int  f_2(double d, const std::string& s)
    {
      const int result = 1;
      boost::format f(
        "%1% called. instance-addr: 0x%2%. d: %3%, s: %4%. return: %5%");
      f % __FUNCTION__ % this % d % s % result;
      std::cout << f.str() << std::endl;
      return result;
    }
    void f_3(int a1, int a2, int a3)
    {
      boost::format f(
        "%1% called. instance-addr: 0x%2%. a1: %3%, a2: %4%, a3: %5%.");
      f % __FUNCTION__ % this % a1 % a2 % a3;
      std::cout << f.str() << std::endl;
    }
  };
}

Für Kopien

Hier ein Beispiel für Kopien von Instanzen in Containern. Aufgerufen wird die Methode A::f_1:

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// Instances of classes::A for common use in case of objects and raw ptrs to
// objects:
classes::A a, b, c;
 
// Put object-copies in vector. Use boost::bind to build functor
// std::for_each can use. Provide functor with the required args in the
// right order for being able to call the indicated member function of the
// objects properly:
 
// Create vector. Create instances of A. Put copies of em in vector:
std::vector<classes::A> vec_copies;
vec_copies.push_back(a);
vec_copies.push_back(b);
vec_copies.push_back(c);
 
// Call f_1(): No return, no params
std::for_each(
  vec_copies.begin(),
  vec_copies.end(),
  boost::bind( // Builds functor of type '<void (classes::A&)>' ...
    &classes::A::f_1, // ... for call of type '<void (void)>'
    _1 // place holder for instance. Is each element of vec.
));

Für Pointer

Das ganze geht auch mit gleicher Syntax dank boost::bind für Pointer auf Instanzen.
Hier ein Beispiel für einen Methodenaufruf (A::f_2) mit Argumenten. Ein Rückgabewert der Methode wird von std::for_each nicht ausgewertet:

54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
std::vector<classes::A*> vec_ptrs;
vec_ptrs.push_back(&a);
vec_ptrs.push_back(&b);
vec_ptrs.push_back(&c);
 
// Call f_2():
std::for_each(
  vec_ptrs.begin(),
  vec_ptrs.end(),
  boost::bind(
	&classes::A::f_2,
	_1, // place holder for instance. If used for member-functions,
	    // always the first param is the instance-address. Is each
	    // element of vec.
	0.42,			     // 1. (bound) arg
	"call of obj-ptrs with bind" // 2. (bound) arg
  ));

Für shared_ptr

Das ganze geht auch mit gleicher Syntax dank boost::bind für shared_ptr auf Instanzen.
Hier ein Beispiel für einen Methodenaufruf (A::f_2) mit Argumenten. Ein Rückgabewert der
Methode wird von std::for_each nicht ausgewertet:

71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
std::vector<boost::shared_ptr<classes::A> > vec_shps;
vec_shps.push_back(boost::shared_ptr<classes::A>(new classes::A()));
vec_shps.push_back(boost::shared_ptr<classes::A>(new classes::A()));
vec_shps.push_back(boost::shared_ptr<classes::A>(new classes::A()));
 
// Call f_2():
std::for_each(
  vec_shps.begin(),
  vec_shps.end(),
  boost::bind(
  &classes::A::f_2,
  _1, // place holder for instance. If used for member-functions,
      // always the first param is the instance-address. Is each
      // element of vec.
  0.666,            // 1. (bound) arg
  "with shared_ptr!" // 2. (bound) arg
  ));

Direkter Aufruf mit BOOST_FOREACH

Etwas unsportlicher, weil leichter zu lesen ist der folgende direkte Aufruf
einer Methode (A::f_3) mehrerer Instanzen in einer STL-Collection mit BOOST_FOREACH:

88
89
90
91
92
boost::shared_ptr<classes::A> shp_a;
BOOST_FOREACH(shp_a,vec_shps)
{
  shp_a->f_3(1,2,3);
}

Selbstverständlich funktioniert dieser Aufruf ganz boost-like auch wieder mit Kopien und Raw-Pointern.

Für eine Map ist der Aufruf etwas anders, da hier Elemente vom Typ std::pair
gespeichert werden:

93
94
95
96
97
98
99
100
101
102
103
// direct walk with BOOST_FOR_EACH: map, shared_ptr to fn with 3 int args:
std::map<std::string,boost::shared_ptr<classes::A> > map_shps;
map_shps["0"] = vec_shps[0]; // see line 71 and following ...
map_shps["1"] = vec_shps[1];
map_shps["2"] = vec_shps[2];
 
std::pair<std::string,boost::shared_ptr<classes::A> > element;
BOOST_FOREACH(element,map_shps)
{
  element.second->f_3(1,2,3);
}

Ein vc++2008-projekt mit dem Beispielprogramm kann hier downgeloded werden.
Boost-Version: 1.42 aufwärts. Proj.C++.Additional Include Directories:
Pfad zu boost angeben, Proj.Linker.General.Additional Library Directories:
Pfad zu boost\stage\lib angeben.

play_with_boost_function_vc++2008_proj

Bis zum nächsten Post,

Daniel

Pandorum   November 14th, 2010

Gestern Pandorum angeschaut. Wow, klasse SF-Film. Positiv: Man weis am Anfang noch nicht alles, und Stück für Stück setzt sich dann das Puzzle zusammen, wobei es bis zum Schluß sehr spannend bleibt. Die Atmosphäre ist sehr düster und beklemmend. Die Story hat meiner Meinung nach wenig Haken und ist im Sinne von SF eingermaßen als möglich einzustufen, also nicht ganz abgedreht … 🙂 Der Film hat Parallelen zu Cargo und Sunshine, die ich beide auch sehr empfehlen kann. Über die Handlung will ich eigentlich gar nichts verraten, da eine sich entwicklende Story ihre Wirkung um so mehr entfalltet, je weniger man schon vorher weis. 5/5 Sternen.

Pandorum FSK 16 Version, keine SE

Tags: , ,
Posted in Movies | No Comments »

Callbacks sind nützlich, um Module voneinander zu entkoppeln. Die Module können miteinander kommunizieren, ohne voneinander zuviel wissen zu müssen. Kennen sich kolaborierende Klassen, leidet die Veränderbarkeit und Wartungsfreundlichkeit des Codes. Die Komplexität steigt und mit ihr dann auch die Zahl der Programmfehler. Auch die Wiederverwendbarkeit leidet unter “verzahntem” Code. Eine Möglichkeit der Entkoppelung besteht in der Verwendung von C++-Interfaces. Die Module kennen dann nur das zu verwendende Interface und die zu benutzede Addresse, sind aber nicht abhängig von dem dahinter stehenden Implementierer (der konkreten Klasse). Bei Verwendung der hier vorgestellten Methodik kennen die Module voneinder nur die zu verwendenden Signaturen und die Addresse, wo diese aufzurufen sind. Nicht abhängig sind diese Module dann von dem gerade zu benutzenden Besitzer, der diese Signatur zur verfügung stellt.

Mit boost::function und boost::bind kann man relativ einfach verzögerte Funktionsaufrufe realisieren. Nützlich sind diese für die gerade genannten Callbacks. Als Vorlage habe ich mir die mir am wichtigsten erscheinenden Fälle hier zusammengetragen:

Freie Funktionen

1
2
3
4
5
6
7
8
namespace free_functions
{
  void f_1()
  {
    boost::format f("%1%: called with args: void. It returned: void.");
    f % __FUNCTION__;
    std::cout << f.str();
  }

Diese freie Funktion kann man mit einem Objekt vom Type boost::function zu einem späteren Zeitpunkt (Callback) dann wie folgt aufrufen:

9
10
11
12
// Anlegen. Hier auf dem Stack. Für Objekte dann als member:
boost::function<void (void)> fn(boost::bind(&free_functions::f_1));
// Später zum Event-Zeitpunkt dann der Aufruf:
fn();

Als Template-Parameter (Zeile 11) übergibt man die Signatur der aufzurufenden Funktion. Hier: Return=void, 1.Argument=void. bind wird als Argument die Addresse der aufzurufenden Funktion übergeben (Zeile 11). Es baut dann den Funktor, den das function-object speichert. Über den überladenen Klammer-Operator kann dann die Funktion indirekt aufgerufen werden (Zeile 12).

Für eine freie Funktion mit Rückgabewert und Parameter der Art …

13
14
15
16
17
18
19
20
21
22
namespace free_functions
{
  int  f_2(double d, const std::string& s)
  {
    const int result = 1;
    boost::format f("%1%: called w. args: %2%, %3%. Returned: %4%");
    f % __FUNCTION__ % d % s % result;
    std::cout << f.str();
    return result;
  }

… sieht der Code zum Erzeugen und Aufrufen dann so aus:

13
14
15
16
17
18
19
20
21
22
23
// Erzeugen
boost::function<int (double, std::string)>
fn(
  boost::bind(
    &free_functions::f_2,
    _1, // bind-Platzhalter für ersten Parameter
    _2  // bind-Platzhalter für zweiten Parameter
));
 
// Der Aufruf:
const int result = fn(42.0,"hello world");

_1 ist ein Platzhalter für bind. Er weist bind an für die letzendlich aufzurufende Funktion als ersten Parameter den ersten Parameter des Funktor-Aufrufes, also dem überladenen Klammer-Operator des boost::function-Objektes, zu benutzen. Ist im vorliegenden Beispiel der double-Wert 42.0. Entsprechendes gillt für den zweiten Parameter. Die Reihenfolge bei bind entspricht der Reihenfolge der Parameter der auzurufenden Funktion, die Nummer des bind-Platzhalters der Reihenfolge der Parameter bei Aufruf des boost::function-Objektes. Definiert sind die Platzhalter _1 – _9. Ein Ändern der Reihenfolge oder das Nichtbeachten von Parametern ist möglich, so lange die Typen der Argumente zumindest konvertierbar sind:

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
namespace free_functions
{
 // Argumente in der Reihenfolge austauschbar:
 void f_3(int a1, int a2, int a3)
  {
    boost::format f("%1% called. a1: %2%, a2: %3%, a3: %4%.");
    f % __FUNCTION__ % a1 % a2 % a3;
    std::cout << f.str() << std::endl;
  }
}
 
...
 
boost::function
fn_normal_order(
  boost::bind(
  &free_functions::f_3,
  _1,_2,_3));
 
boost::function
fn_mixed_order(
   boost::bind(
   &free_functions::f_3,
   _3,_2,_1));
 
fn_normal_order(1,2,3); // 1 2 3
fn_mixed_order(1,2,3);  // 3 2 1
fn_normal_order(1,2,"3");// (!) Fehler beim kompilieren

Member Funktionen

All das gerade Gesagte gillt auch für Member Funktionen von Objekten. Hier muß zusätzlich (natürlich) noch die Adresse des Objektes, bzw. die zu verwendende Instanz mitangegeben werden:

52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
namespace classes
{
  class A
  {
  public:
    ...
    // Definition der Methode:
    int  f_2(double d, const std::string& s)
    {
      const int result = 1;
      boost::format f(
        "%1% called. instance-addr: 0x%2%. d: %3%, s: %4%. return: %5%");
      f % __FUNCTION__ % this % d % s % result;
      std::cout << f.str() << std::endl;
      return result;
    }
 
    ...
 
// Anlegen der Instanz:
classes::A a;
 
...
 
// Definition des Callback-Objektes:
boost::function<int (double,std::string)> fn(
  boost::bind(
    &classes::A::f_2, // Methoden-Adresse
    &a, // Zusätzlich hier die zu verwendende Instanz mit angeben.
    _1,
    _2
));
 
...
 
// Irgendwann der indirekte Aufruf von a.f_2(...):
int result = fn(1.0,"hello member function");

In anderen Sprachen nennt man solche Funktionszeiger auf Memberfunktionen bestimmer Instanzen “Delegates”.

Source

Ein vc++2008-projekt mit dem Beispielprogramm kann hier downgeloded werden.
Boost-Version: 1.42 aufwärts. Proj.C++.Additional Include Directories:
Pfad zu boost angeben, Proj.Linker.General.Additional Library Directories:
Pfad zu boost\stage\lib angeben.

play_with_boost_function_vc++2008_proj

Ausblick

In einem weiteren Post werde ich noch aufzeigen, wie man mit boost::bind eine Member-Funktion von verschiedenen Instanzen einer Klasse in einer STL-Collection aufrufen kann.

Bis zum nächsten Post,

Daniel