Die Boost C++ Bibliotheken

Adapter

Die Standardbibliothek bietet einige Algorithmen an, denen ein Prädikat übergeben werden kann. So können Sie beispielsweise bei std::count_if() angeben, welche Elemente gezählt werden sollen. Boost.Range bietet mit boost::count_if() eine ähnliche Funktion. Dies geschieht jedoch ausschließlich der Vollständigkeit halber. Denn Boost.Range bietet neben Algorithmen Adapter an, die viele Algorithmen mit Prädikaten überflüssig machen.

Adapter können Sie sich als Filter vorstellen. Sie geben eine neue Range basierend auf einer anderen zurück. Dabei werden nicht zwangsläufig Daten kopiert. Da eine Range letztendlich ein Paar Iteratoren ist, gibt ein Adapter ein neues Paar zurück, mit deren Hilfe über die ursprüngliche Range iteriert wird, aber zum Beispiel bestimmte Elemente übersprungen werden. Wenn boost::count() mit einem derartigen Adapter verwendet wird, wird boost::count_if() nicht mehr benötigt. Algorithmen müssen nicht mehr mehrfach definiert werden, nur um ohne und mit Prädikat aufgerufen werden zu können.

Der Unterschied zwischen Algorithmen und Adaptern ist: Algorithmen iterieren über eine Range und verarbeiten Daten. Adapter hingegen geben eine neue Range zurück – neue Iteratoren. Es hängt von diesen neuen Iteratoren ab, wie über die Range iteriert wird. Es findet aber keine Iteration statt. Dazu müssen Sie erst einen Algorithmus aufrufen.

Beispiel 30.4. Eine Range mit boost::adaptors::filter() filtern
#include <boost/range/algorithm.hpp>
#include <boost/range/adaptors.hpp>
#include <array>
#include <iterator>
#include <iostream>

int main()
{
  std::array<int, 6> a{{0, 5, 2, 1, 3, 4}};
  boost::copy(boost::adaptors::filter(a, [](int i){ return i > 2; }),
    std::ostream_iterator<int>{std::cout, ","});
}

Im Beispiel 30.4 kommt ein Adapter zum Einsatz, der Ranges filtern kann. Wie Sie sehen handelt es sich dabei schlichtweg um eine Funktion. So erwartet boost::adaptors::filter() als ersten Parameter die zu filternde Range und als zweiten Parameter ein Prädikat. Das Prädikat im Beispiel 30.4 entfernt alle Zahlen aus der Range, die nicht größer als 2 sind.

Beachten Sie, dass boost::adaptors::filter() die Range a nicht ändert. Eine Funktion wie boost::adaptors::filter() gibt eine neue Range zurück. Da eine Range letztendlich nichts anderes ist als ein Paar Iteratoren, verweist diese neue Range ebenfalls auf a. Die Iteratoren dieser neuen Range überspringen jedoch bei einer Iteratoren alle Zahlen, die nicht größer als 2 sind.

Führen Sie Beispiel 30.4 aus, wird Ihnen 5,3,4 auf die Standardausgabe ausgegeben.

Beispiel 30.5. keys(), values() und indirect() in Aktion
#include <boost/range/algorithm.hpp>
#include <boost/range/adaptors.hpp>
#include <array>
#include <map>
#include <string>
#include <utility>
#include <iterator>
#include <iostream>

int main()
{
  std::array<int, 3> a{{0, 1, 2}};
  std::map<std::string, int*> m;
  m.insert(std::make_pair("a", &a[0]));
  m.insert(std::make_pair("b", &a[1]));
  m.insert(std::make_pair("c", &a[2]));

  boost::copy(boost::adaptors::keys(m),
    std::ostream_iterator<std::string>{std::cout, ","});
  boost::copy(boost::adaptors::indirect(boost::adaptors::values(m)),
    std::ostream_iterator<int>{std::cout, ","});
}

Beispiel 30.5 stellt Ihnen mit boost::adaptors::keys() und boost::adaptors::values() nicht nur zwei Adapter vor, um auf Schlüssel und Werte eines Containers wie vom Typ std::map zuzugreifen. Sie sehen auch, dass Sie Adapter verschachteln können. Da die Werte im Container m Zeiger sind, jedoch die Zahlen ausgegeben werden sollen, auf die sie zeigen, wird die von boost::adaptors::values() zurückgegebene Range an boost::adaptors::indirect() übergeben. Diesen Adapter können Sie immer dann verwenden, wenn die ursprüngliche Range aus Zeigern besteht, sie aber bei einer Iteration auf das zugreifen wollen, worauf die Zeiger verweisen. So gibt Beispiel 30.5 a,b,c,0,1,2, auf die Standardausgabe aus.

Beispiel 30.6. boost::adaptors::tokenize() – ein Adapter für Strings
#include <boost/range/algorithm.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/regex.hpp>
#include <string>
#include <iostream>

int main()
{
  std::string s = "The Boost C++ Libraries";
  boost::regex expr{"[\\w+]+"};
  boost::copy(boost::adaptors::tokenize(s, expr, 0,
    boost::regex_constants::match_default),
    std::ostream_iterator<std::string>{std::cout, ","});
}

Beispiel 30.6 stellt Ihnen einen Adapter für Strings vor. Sie können boost::adaptors::tokenize() verwenden, um eine Range auf einen String mit Hilfe eines regulären Ausdrucks zu erhalten. Sie übergeben dazu einen String und einen regulären Ausdruck vom Typ boost::regex an diese Funktion. Außerdem müssen Sie eine Zahl für eine Gruppe im regulären Ausdruck sowie ein Flag übergeben. Verwenden Sie keine Gruppe, übergeben Sie 0. Übergeben Sie als Flag boost::regex_constants::match_default, wenn Sie die Standardeinstellungen für reguläre Ausdrücke verwenden möchten. Sie können auch zum Beispiel boost::regex_constants::match_perl übergeben, wenn der reguläre Ausdruck gemäß den Regeln der Programmiersprache Perl angewandt werden soll.

Aufgabe

Entwickeln Sie ein Programm, das alle ungeraden Zahlen zwischen 0 und 100 in aufsteigender Reihenfolge in die Standardausgabe schreibt. Verwenden Sie ausschließlich Algorithmen aus Boost.Range – keine selbstgeschriebenen Schleifen.