BOOST Libraries

Top1 Installation

Top1.1 Benötigte Downloads

  • aktuelle Version der Boost-Libraries herunterladen, z.B. http://sourceforge.net/projects/boost/files/boost/1.39.0/boost_1_39_0.zip/download
  • Commandline-Tool für das Boost-Buildsystem in direkt ausführbarer Form (bjam.exe) herunterladen, z.B. unter http://sourceforge.net/projects/boost/files/boost-jam/3.1.9/bjam-3.1.9-1-ntx86.zip/download

Top1.2 Vorbereitende Schritte

  • heruntergeladene boost-libraries aus dem ZIP-File boost_1_39_0.zip z.B. nach C:\boost kopieren, spezifisches Verzeichnis (z.B. boost_1_39_0) als Unterverzeichnis beibehalten
  • bjam.exe aus dem ZIP-File boost-jam-3.1.13-1-ntx86.zip nach C:\boost\boost_1_39_0 kopieren
  • in Command-Prompt zu C:\boost\boost_1_39_0 bjam.exe aufrufen, alle Libraries und DLLs werden dadurch in spezifische Unterverzeichnisse unter C:\boost\boost_1_39_0\bin.v2 generiert
  • Suchen über Explorer in C:\boost\boost_1_39_0\bin.v2 nach "*.dll;*.lib" und gefundene Dateien in das neu anzulegende Verzeichnis C:\boost\boost_1_39_0\lib kopieren
Hinweis: Um Anpassungen an aktualisierte Library-Versionen in den Projekten zu vermeiden, kann die jeweils heruntergeladene Lib-Version in das Unterverzeichnis c:\boost\boost_actual kopiert werden. Das generierte Verzeichnis bin.v2 muss dabei nicht mitkopiert werden

Top1.3 Einstellungen im VC++ Projekt

Die aktuell installierte boost-Version muss in den Project-Properties eingestellt werden:
  • C/C++ \ Allgemein: zusätzliche Include-Verzeichnisse: C:\boost\boost_1_39_0 bzw. boost_actual
  • Linker \ Allgemein: zusätzliche Biobliotheksverzeichnisse: C:\boost\boost_1_39_0\lib bzw. boost_actual
Im Quellcode die benötigte Lib am besten über das Autolink-Feature anbinden:
// Use the boost auto link feature which automatically chooses
// the correct library version
#define BOOST_LIB_NAME boost_unit_test_framework
#include <boost/config/auto_link.hpp>

Top2 Synchronisation paralleler Abläufe - boost::condition

Top2.1 Warten auf Eintreten einer Bedingung (condition)

  • Problemstellung:
    Ein Thread will blockierend darauf warten, dass ein anderer Thread einen bestimmten Arbeitsschritt abschliesst.
  • Lösungsprinzip:
    ein Thread prüft eine Bedingung, der andere Thread setzt die Bedingung
  • der Zugriff auf die Bedingung muss synchronisiert werden

Top2.1.1 Elementare Lösung mit boost::condition

Wichtige Randbedingung: Das Setzen der Bedingung (notify_one/all()) darf erst dann durchgeführt werden, wenn sichergestellt ist, dass sich der wartende Thread bereits im blockierenden wait-Aufruf befindet. Ist das nicht der Fall, bleibt der notify-Aufruf ohne Wirkung! Siehe auch Boost documentation.

Anwendungsbeispiel boost::condition

//shared data definitions (e.g. class attributes)
boost::mutex     m_mutex;
boost::condition m_condition;


// within Thread A

boost::mutex::scoped_lock lock(m_mutex);

// start some action to be executed within another thread B

// now wait until the other thread is ready,
// i.e. the condition becomes true
m_condition.wait(lock); // the lock will be released before blocking
// when wait returns the mutex has been locked again


// within Thread B

// Before starting the action wait until Thread A has entered wait()
boost::mutex::scoped_lock lock(m_mutex);

// execute some action

// inform thread A that we are ready here
m_condition.notify_one();

// unlock mutex (e.g. automatically at end of function scope)

Top2.1.2 Beliebiger Zeitpunkt für Setzen der Bedingung (SyncUtils::WaitableCondition)

Kann aufgrund des Ablaufes nicht gewährleistet werden, dass sich Thread A bereits im Aufruf von wait() befindet, wenn Thread B die Bedingung auslöst, so ist ein zusätzliches Bool-Flag notwendig, das die Information über das Auslösen der Bedingung speichert:

Definition SyncUtils::WaitableCondition

// Possible implementation for class WaitableCondition
class WaitableCondition : private boost::noncopyable
{
public:
    // Constructor
    WaitableCondition (bool in_autoReset = true)
        :
        m_autoReset  (in_autoReset),
        m_condIsTrue (false)
    {}

    // Set and reset the condition
    void Set (bool in_state)
    { 
        boost::mutex::scoped_lock lock(m_mutex);
        m_condIsTrue = in_state;
        if (m_condIsTrue ) m_condition.notify_one();
    }

    // Check condition without waiting
    bool IsTrue (void)
    {
        boost::mutex::scoped_lock lock(m_mutex);
        return m_condIsTrue;
    }

    // Wait until condition becomes true
    // If the condition has already been set the function
    // will immediately return without calling boost-wait().
    void WaitUntilTrue (void)
    {
        boost::mutex::scoped_lock lock(m_mutex); 
        if (!m_condIsTrue)
        {
             // we really have to wait until someone calls
             // Set(true)
             m_condition.wait(lock);
        }
        if (m_autoReset) m_condIsTrue = false;
    }

    // Wait until condition becomes true (return true)
    // or the given timeout has elapsed (return false)
    bool TimedWaitUntilTrue (long in_timeoutMs)
    {
        boost::mutex::scoped_lock lock(m_mutex);
        if (!m_condIsTrue)
        {
            boost::xtime timeInterval = 
                ConvertFromMilliSec(in_timeoutMs);
            if (!m_condition.timed_wait(lock, timeInterval)
                return false;
        }
        if (m_autoReset) m_condIsTrue = false;
        return true;
    }


private:
    bool             m_autoReset;
    bool             m_condIsTrue;
    boost::mutex     m_mutex;
    boost::condition m_condition;
}

Anwendungsbeispiel SyncUtils::WaitableCondition

//shared data definitions (e.g. class attributes)
SyncUtils::WaitableCondition m_condition;


// within Thread A

// start some action to be executed within another thread B
// or assume that someone else has done this or will do this

// simply wait until the condition has become true
m_condition.WaitUntilTrue();


// within Thread B

// execute some action

// now set the condition 
m_condition.Set(true);

Top3 Allgemeine Libraries im Überblick

Top3.1 Smart_ptr

Top3.1.1 Überblick

Begriff intrusive:
ein intrusive SmartPtr erwartet von der verwalteten Klasse, dass sie den ReferenzCount verwaltet. non-intrusive Zeiger stellen dagegen keine Ansprüche an die verwalteten Klassen und stellen selbst einen Referenzzählmechanismus zur Verfügung.
  • scoped_ptr
    nicht übertragbarer Besitz an einer Ressource, delete bei Verlassen des Scopes
  • shared_ptr
    non-intrusive, unterstützt verteilte Ownership für Ressourcen, kann in STL-Containern verwaltet werden, bevorzugt zu verwendende SmartPtr-Klasse
  • weak_ptr
    ermöglicht Überwachung von shared resources, bei Bedarf Zugriff durch Umwandlung in shared_ptr
  • intrusive_ptr
    die verwaltete Klasse besitzt einen eigenen ReferenzCount, this kann wie ein SmartPtr verwendet werden

Top3.1.2 scoped_ptr

Ähnliches Verhalten wie std::auto_ptr (d.h. Destruktor ruft delete), Kopieren und Zuweisung wird aber verhindert, d.h. scoped_ptr ist der alleinige Besitzer einer Ressource.
{
    boost::scoped_ptr<std::string> sp(new std::string(“Hallo”));

    // Check for existence and print the value
    if (sp)
        std::cout << *sp << ‘\n’;

    // Get the size of the string
    size_t i = sp->size();

    // Change the value of the string
    *sp = “New text”;

} // sp destrucor deletes the string

Top3.1.3 shared_ptr

reference counted, non-intrusive SmartPtr, unterstützt das Abspeichern in STL-Containern.

Wichtig: die verwalteten Objekte müssen durchgängig als shared_ptr weitergegeben werden. Würde ein Objekt zwischendurch als raw pointer behandelt und an anderer Stelle wieder in einen (dann unabhängigen) neuen shared_ptr zur Verwaltung gegeben, so würde dieser SmartPtr von einem falschen Referenzcount ausgehen und unabhängig von den anderwo zu diesem Objekt existierenden SmartPointern das Objekt abbauen.

besondere Funktionalitäten:

  • Im Konstruktor kann optional ein Objekt als „custom deleter“ angegeben werden, das anstelle von delete aufgerufen wird. Custom deleter können auch verwendet werden um die Zugriffsrechte auf die zu verwaltende Klasse einzuschränken (z.B. versehentliches Erzeugen auf dem Stack oder Aufruf von delete ist dann nicht mehr möglich)
  • bool unique()
    gibt an, ob der shared_ptr der alleinige Besitzer der Ressource ist
  • long use_count()
    liefert denReferenzCount
  • static_pointer_cast
    für Abspeichern eines gecasteten Zeigers in einem shared_ptr notwendig, sonst geht die Verbindung zu dem ReferenzCount verloren
  • shared_ptr from this und weak_ptr
    Klasse ableiten von enable_shared_from_this<MyClass>, diese Basisklasse enthält einen weak_ptr als Klassenattribut, der den Referenzcount des shared_ptr beobachtet. Durch Aufruf von shared_from_this() kann dieser weak_ptr in einen shared_ptr umgewandelt werden. weak_ptr können allgemein zum Beobachten von Ressourcen eingesetzt werden, ohne dass sie dabei Besitz ergreifen. Wird die Ressource abgebaut, so sorgt der shared_ptr dafür, dass alle beobachtenden weak_ptr auf 0 gesetzt werden. Bei Bedarf kann ein weak_ptr Besitz von der Ressource ergreifen ( shared_ptr<T> weak_ptr::lock()). Damit können zyklische Abhängigkeiten vermieden werden.

Top3.1.4 intrusive_ptr

analog zu shared_ptr, erwartet aber, dass der Referenzcount von der Klasse selbst verwaltet wird. Es müssen die freien Funktionen intrusive_ptr_add_ref() und intrusive_ptr_release() überladen und mit dem eigenen ReferenzCounter verbunden werden. intrusive_ptr_release() muss auch das delete ausführen wenn der RefCount auf 0 fällt.

Vorteil: this kann wie ein SmartPtr behandelt werden.

Der ReferenzCount kann z.B. über folgende Basisklasse zur Verfügung gestellt werden:

class RefCount
{
    int m_refCount;
public:
    RefCount() : m_refCount(0){}
    virtual ~RefCount(){}

    // directly implement the needed free functions here in the scope
    // of this base class (multithreading not yet considered!)
    friend void intrusive_ptr_add_ref(RefCount* in_p)
    {++in_p_>m_refCount;}
    friend void intrusive_ptr_release(RefCount* in_p)
    {if (--in_p_>m_refCount==0) delete in_p;}
}

Top3.2 Conversion

Top3.2.1 Überblick

  • polymorphe Umwandlungen
  • numerische casts mit Überprüfung des Wertebereiches
  • lexical_cast
    lexikalische Umwandlungen von Strings in numerische Werte und umgekehrt, realisiert über stringstream, d.h. die Typen müssen Streamoperatoren unterstützen
Top3.2.2 Polymorphe Umwandlungen (dynamic_cast / polymorphic_cast) werden üblicherweise mit dynamic_cast durchgeführt. Es gibt dabei folgendes Verhalten:
  • Anwendung auf einen Referenztyp: Exception falls Umwandlung nicht möglich
  • Anwendung auf einen Zeigertyp: Rückgabe von 0 falls Umwandlung nicht möglich
Für die Anwendung auf Zeigertypen kann alternativ boost::polymorphic_cast eingesetzt werden, das im Fehlerfall eine Exception wirft. Es sind folgende Umwandlungen möglich:
  • downcast: von Basisklasse auf abgeleitete Klasse
  • crosscast: von einer Basisklasse auf eine andere Basisklasse
Motivation für den Einsatz von polymorphic_cast: einheitliche Fehlerbehandlung über Exceptions unabhängig von Zeiger/Referenztyp. Wird ein fehlgeschlagener cast nicht als Fehler betrachtet (z.B: weil man mehrere in Frage kommende Typen nacheinander testen will) so sollte dynamic_cast verwendet werden!

Top3.2.3 Downcast mit polymorphic_downcast (verwendet static/dynamic_cast)

dynamic_cast erfordert Laufzeitprüfungen und ist daher ineffizienter als static_cast. Ist der Zieltyp sicher bekannt, so kann das effizientere static_cast eingesetzt werden. boost::polymorphic_downcast kombiniert beide Methoden:
  • Debug-Build: sicheres Überprüfen mit dynamic_cast, danach Konvertierung mit static_cast, im Fehlerfall assert
  • Release-Build: nur Konvertierung mit static_cast
Motivation: mögliche Fehler in einer eigentlich als sicher betrachteten Konvertierung sollten in der Testphase durch Verwendung von polymorphic_downcast gefunden werden. In der Releasephase wird automatisch der effizientere Code eingesetzt.

Top3.2.4 Beispiele für numerical_cast, lexical_cast

numeric_cast (Überwachung des Wertebereiches)

long val = 7;
char c   = boost::numeric_cast<char>(val); // ok

val = 257;
c   = boost::numeric_cast<char>(val);
// exception bad_numeric_cast is thrown here

unsigned int ui;
int i = -12;
ui = boost::numeric_cast<int>(ui);
// exception bad_numeric_cast is thrown here

lexical_cast

// string to int
std::string s=”43”;
int i = boost::lexical_cast<int>(s);

// float to string
float f= 3.14;
s=boost::lexical_cast<std::string>(f);

// failed conversion
s=”Text without numbers”;
i=boostlexial_cast<int>(s);
// -> exception bad_lexical_cast


// A simple converter function to string which allows
// more readable calls
template <typename T>
std::string ToString (const T& arg)
{
    try {return boost::lexical_cast<std::string>(arg);}
    catch(boost::bad_lexical_cast& e)
    {return “”;}
}

// Usage
std::string s=ToString(412);

Top3.3 Utility

Top3.3.1 Überblick

  • compiletime assertions mit BOOST_STATIC_ASSERT
  • Verbot für Kopieren einer Klasse über Ableitung von noncopyable
  • checked_delete (delete auf nur forward definierte Klassendefinition)
    als Ersatz für delete führt zu einer Compiler-Fehlermeldung, wenn nicht die vollständige Definition der zu zerstörenden Klasse an der Aufrufstelle vorliegt (reguläres delete führt meist zu keiner Compiler-Warning, aber zu Laufzeitfehlern)
  • addressof
    liefert auch dann die tatsächliche Objektadresse, wenn der operator& überladen wurde. Insbesondere in generischem Code sollte niemals der operator& verwendet werden.
  • enable_if/disable_if
    Zum Auffinden der am besten passenden Template-Instanziierung wendet der Compiler das SFINAE-Prinzip (substitution failure is not an error) an, d.h. entsteht für einen Typ ein Compilerfehler bei Wahl eines bestimmten Templates (z.B. Returnwert einer Templatefunktion ist für den verwendeten Typ nicht definiert), so wird das Template aus der Menge der zur Verfügung stehenden Templates entfernt und andere Prinzipien wie automatische Typkonvertierung können zum Zug kommen. Über enable_if können Compiletime- Bedingungen geprüft werden, enable_if<false>::type liefert dabei void zurück. Als Bestandteil der Templatedefinition kann dies zur korrekten Auswahl des richtigen Templates ausgenutzt werden.

Top3.3.2 Beispiele: STATIC_ASSERT, noncopyable

BOOST_STATIC_ASSERT

// Restricted to compile time expressions, i.e., values and operators
// must be known to the compiler

// ensure that a template is instantiated only with integer types
template <typename T>
class MyClass
{
    BOOST_STATIC_ASSERT(boost::is_integral<T>::value);
};

// ensure that a template function is used only for a special
// range of constant values
template <int i>
MyFunction()
{
    BOOST_STATIC_ASSERT(i>=1 && i<=10);
}

boost::noncopyable

// The following class should not allow copies of itself
class MyClass : boost::noncopyable // private inheritance
{
    // declaring copy contructor and assignment operator as private
    // is no longer necessary
};

Top3.4 Operators

Top3.4.1 Überblick - Grundsätzliche Merkmale

  • unterstützt bei der Implementierung von arithmetischen Operatoren, Vergleichs-Operatoren sowie Iteratoren
  • In der Regel wird nicht ein einzelner Operator vom Anwender erwartet, sondern eine Menge miteinander verwandter Operatoren, die zu einem Konzept gehören (Beispiel Konzept less_than_comparable unterstützt <, >, <=, >=)
  • Realisierungsprinzip:
    • Ableiten von den benötigten Konzept-Basisklassen
    • Realisierung eines vorgeschriebenen Anteils der erforderlichen Operatoren
    • über die eingesetzten Basisklassen werden die restlichen Operatoren des Konzeptes auf der Grundlage der spezifisch realisierten Operatoren automatisch definiert

Top3.4.2 Details zur Verwendung

  • Einige Konzepte (fett markierte Operatoren müssen realisiert werden)
    • less_than_comparable: <, >, <=, >=
    • equality_comparable: ==, !=
    • equivalent: <,==
    • addable/subtractable: +, += / -, -=
    • orable/andable: |,|= / &,&=
    • incrementable T& operator++(T&), T operator++(T&,int)
  • es gibt übergeordnete composite-Konzepte, die mehrere Basiskonzepte zusammenfassen (Beispiel: totally_ordered: less_than_comparable + equality_comparable)
  • Barton-Nackman Trick (Curiously Recurring Template Pattern)
    Klasse A leitet sich von einer Template-Basisklasse ab, die als Templateargument die Klasse A erhält.
  • Base class chaining (Verkettung mehrerer Konzeptbasisklassen)
    werden mehrere Konzepte benötigt, so leitet man seine Klasse von mehreren Konzeptbasisklassen ab. Manche Compiler haben bei mehrfacher Vererbung jedoch Probleme damit Empty Base Optimization (= Basisklassen ohne Datenmember brauchen keinerlei Speicherplatz in der Klassenhierarchie) einzusetzen und erzeugen so zu grosse Objekte. Um dies zu vermeiden, können die Konzeptklassen verschachtelt werden um weiterhin einfache Vererbung zu verwenden:
    Beispiel:
    MyClass : boost::less_than_comparable<MyClass, bost::equivalent<MyClass>>
  • Probleme mit Equality and Equivalence
    Zur Verwendung der assoziativen Container wird mindestens Konzept LessThanComparible benötigt. Von verschiedenen Algorithmen/Containern wird dabei das Äquivalenzprinzip (a äquivalent zu b falls !(a<b) && !(b>a)) verwendet (z.B. std::set::insert/ find). Andere Algorithmen (z.B. std::find) arbeiten mit operator==. Die operator-Methoden < und == können grundsätzlich mit unterschiedlicher Logik realisiert werden (z.B. Berücksichtigung jeweils unterschiedlicher Datenanteile der Klasse). Es besteht dann die Gefahr von logischen Fehlern oder zumindest unerwarteten Effekten, dadurch dass je nach eingesetztem Such-Algorithmus ein anderes Element oder gar keines in einer Menge aufgefunden wird.

Top3.4.3 Beispiele

Konzept less_than_comparable

// concept requirs the implementation of bool operator<(const T&, const T&)

class MyClass : boost::less_than_comparable<MyClass> // Barton-Nackman
{
    std:string m_name;
public:
    friend bool operator<(const MyClass& lhs, const MyClass& rhs)
    {return lhs.m_name < rhs.m_name;}
};

Operatoren für verschiedene Typen

// The following string class should also work with the char* type

class MyString : boost::addable<MyString,
    bost::addable2<MyString, const char*> > 
{
public:
    explicit MyString (const char*);
    MyString(const MyString&);

    // Operators to implement
    MyString operator=(const MyString&);
    MyString& operator+=(const MyString&);
    MyString& operator+=(const char*);
};

// The base classes add implementation for following operators:
// MyString operator+(const MyString&, const MyString&);
// MyString operator+(const MyString&, const char*);
// MyString operator+(const char*, const MyString&);

Top3.5 Regex (regular expressions)

Top3.5.1 Überblick

Grundsätzliche Merkmalke:
  • Validierung von Eingaben
  • Formatierung, Suchen und automatisiertes Ersetzen von Text
Details zur Verwendung
  • bool regex_match(..)
    Validierung von Text, der untersuchte Text muss vollständig der regular expression entsprechen
  • bool regex_search(..)
    Suchen einer Teilsequenz im Text, die der regular expression entspricht
  • string regex_replace(..)
    Ersetzen von Textsequenzen
  • regex_iterator
    zum Iterieren über gefundene Subexpressions
  • regex_token_iterator
    zum Splitten von Strings entsprechend einem definierbarem Separator

Top3.5.2 Beispiele: Syntax, Searching, Replacing

Syntax für regular expressions

. = beliebiges Zeichen
* = 0 oder beliebig viele Wiederholungen
+ = beliebig viele Wiederholungen, aber mindestens einmal
? = keine oder genau eine Wiederholung
? = Ziel shortest possible match, als Zuatz zu * oder +
\d = Ziffer
\w = Wort (besser als [a-zA-Z], da Internationalisierung berücksichtigt
\1 = Referenz auf erste gefundene SubExpression
{n} = n Wiederholungen
{2,4} = 2 bis 3 Wiederholungen
{2,} = mindestens 2 Wiederholungen
[abc] = Alternativen entweder a oder b oder c
[a-c] = Alternativen, abgekürzt als Bereich
[^abc] = ungleich a,b,c
| = Trenner für Alternativen
\A = Beginn des Zeichenpuffers
\Z = Ende des Zeichenpuffers

Beispiele:

// 3 Ziffern, ein Wort aus Buchstaben, ein beliebiges Zeichen, 2 Ziffern
// oder „N/A“, ein Leerzeichen dann das erste Wort nochmals
boost::regex reg ("\\d{3}(a-zA-Z+).(\\d{2}|N/A)\s\1)");

Searching

// Calculate the number of occurences for new and delete

boost::regex reg („(new)|(delete)“);
boost::smatch m;
std::string s =”Any text containing new and delete..”;

int newCounter    = 0;
int deleteCounter = 0;

std::string::const_iterator it  = s.begin()=;
std::string::const_iterator end = s.end()=;

while (boost::regex_search(it,end,m,reg)
{
    // m[1/2] indicates whether new or delete was found
    m[1].matched ? ++new_counter : ++ deleteCounter;
    it = m[0].second; // m[0] is a reference to the submatch, continue
                      // searching at the end of that submatch
}

Replacing

// Replace colour with color and preserve the found case
// i.e. Colour and colour should be treated correctly

bost::regex reg (“(Colo)(u)(r)”,               // use three subexpressions
    boost::regex::icase | boost::regex::perl); // ignore case

std::string s = “Colour, colours, color, colourize”;

// the matched expression is replaced with the
// first and third subexpression
s=boost::regex_replace(s,reg,”$1$3”);

Top4 Datenstrukturen und Container

Top4.1 Any (speichert beliebige Typen)

  • typsicheres Speichern und Abfragen beliebiger Typen
  • Der Typ von any ändert sich nicht durch das Abspeichern unterschiedlicher Wertetypen, dadurch Verwaltung heterogener Typen in STL-Containern möglich

Elementarbeispiel Any

boost::any a;

// Any type can be stored
a = std::string(A string”);
a = 43;
a = 3.14;

// accessing the stored value requires to
// know the correct type
double d = boost::any_cast<double>(a);

// Reactions when assuming the wrong type:
// using pointer syntax
int* pVal = boost::any_cast<int>(&a); // in this case 0 is returned
// using const reference 
int val = boost::any_cast<int>(a); // exception boost::bad_any_cast

// Requesting the type and comparing it
if (typeid(int)==a.type())
{/* access as int type is possible */}

STL-Container mit heterogenen Elementen

class A{};
class B{};

std::vector<boost::any> vec;

// Store arbitrary objects within the vector
vec.push_back (A());
vec.push_back (B());
vec.push_back (boost::shared_ptr<B>(new B));
vec.push_back (std::string(“Hallo”));
vec.push_back (4711);
vec.push_back (3.14);

// Remark: You can build a function to process the elements stored
// within the vector. The function simply has to check for the types it
// is able to process (e.g. using pointer syntax see above) and leave the
// other types unhandled

// generic helper function to test the stored type
template <typename T>
bool Contains (const boost::any& a)
{return typeid(T)==a.type();}

Top4.2 Variant (benutzerdefinierte Typ-Menge)

  • typsicheres Speichern und Abfragen einer benutzerdefinierten Menge von Typen
  • Verwaltung heterogener Typen in STL-Containern möglich
  • compile-time check für Zugriff auf die Werte
  • Variant::which()
    liefert den Index des Typs in der benutzerdefinierten Menge von Typen

Elementarbeispiel variant

// allow three types for your variant
typedef boost::variant<int,std::string,double> MyVariant;

MyVariant myVar; // default constructed int = first type

MyVariant myVar2 (Hello”); // variant containing type string

// dynamically change the type
myVar2 = 2.53;
myVar2 = 47;

// accessing the stored values
assert(boost::get<int>(myVar2)==47);
// wrong type would cause exception boost::bad_get)

int* pVal=boost::get<int>(&myVar2);
assert(pVal &&(*pVal==47))
// wrong type would cause return of NULL pointer)

Zugriff über Visitor (mit compile time check)

// Define a visitor class containing a function call operator for each
// type within your variant you want to access.
// Remark: use reference syntax to avoid unwanted conversions (e.g.
// from char to int)
class PrintVisitor : public boost::static_visitor<void>
{
public:
    void operator()(int& i) const
    {std::cout << “int val “ << i << ‘\n’;}

    void operator()(double& d) const
    {std::cout << “double val “ << d << ‘\n’;}

};


PrintVisitor v;

myVar = 46;
boost::apply_visitor(v,myVar);
myVar = 4.5;
boost::apply_visitor(v,myVar);
myVar = “Any text”;
boost::apply_visitor(v,myVar); // Compile ERROR, because type string
                               // is not supported by PrintVisitor


// Possible extension to PrintVisitor for generically support
// of output for all types compatible with << operator
class PrintVisitor (see above)
{
    // specific operators for some types see above

    // generic operator for all other types
    template <typename T> void operator()(T& t) const
    {std::cout << t << '\n';}
}

Top4.3 Tuple (Vektor benutzerdefinierter Typen)

  • ein Tuple entspricht einer (fixed size) Kollektion spezifischer benutzerdefinierter Typen
  • ermöglicht mehrfache Returnwerte von Funktionen
  • einfachere Alternative zur expliziten Definition von structs
  • unterstützt generisches Programmieren
  • konfigurierbares Streamen (Definition von Separatoren und Delimitern) erlaubt einfache Ein- und Ausgabe von Tuples

tuple - Elementarbeispiel

boost::tuple<int,double,std::string> triple (42, 3.14, “Hello”);

// Access the values
int    i = boost::get<0>(triple);
double d = boost::get<1>(triple);

tuple als Returnwert

boost::tuple<int,int> Calculate (int x)
{return boost::make_tuple(x+3, x*x);}

boost::tuple<int,int> result = Calculate(17);
int resultX = result.get<0>();
int resultY = result.get<1>();

// directly connect to result variables
boost::tie(resultX, resultY) = Calculate(19);
// here the results will be available without further asking;
// instead of making copies references are used;
// is the same as make_tuple(ref(resultx), ref(resultY) = ..

tuple - generisches Programmieren, for_each

//--- Definition of for_each_element ---

// for the last element within the tuple (is always null_type)
// do nothing
template <typename Function>
void for_each_element (
    const boost::tuples::null_type&, Function) {}

// for the preceeding elements call the given function
template <typename Tuple, typename Function>
void for_each_element (Tuple& t, Function func)
{
    func(t.get_head()); // call func for the first element
    for_each_elemnt(t.get_tail(), func); // recursively process
                                         // the rest of the tuple
}

//--- FuncObjects for output ---

struct PrintAll
{
    template <typename T> void operator()(const T& t)
    {std::cout << t <<’\n’;}
}

template typename T>
struct PrintSelectedType
{
    // print the selected type
    void operator()(const T& t)
    {std::cout << t <<’\n’;}

    // ignore all other types (discarding overload)
    template <typename U>
    void operator()(const U&){}
}


//--- Example ---

boost::tuple<int,short,double> nums(1,2,3.14);

for_each_element(nums,PrintAll()); // writes all numbers
for_each_element(nums,PrintSelectedType<double>()); // writes only the
                                                    // last number