Die Boost C++ Bibliotheken

Devices

Devices sind Klassen, die Schreib- oder Lesezugriff auf Geräte bieten. Geräte sind üblicherweise externe Objekte wie Dateien. Es ist jedoch auch möglich, interne Objekte wie beispielsweise Arrays als Gerät zu verwenden.

Ein Device ist nicht mehr als eine Klasse, die eine Methode read() oder write() anbietet. Um nicht direkt mit diesen Methoden arbeiten zu müssen, sondern Daten formatiert ein- und ausgeben zu können, kann ein Device mit einem Stream verknüpft werden.

Beispiel 34.1. Ein Array mit boost::iostreams::array_sink als Device verwenden
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/stream.hpp>
#include <iostream>

using namespace boost::iostreams;

int main()
{
  char buffer[16];
  array_sink sink{buffer};
  stream<array_sink> os{sink};
  os << "Boost" << std::flush;
  std::cout.write(buffer, 5);
}

Im Beispiel 34.1 wird mit boost::iostreams::array_sink ein Device verwendet, um Daten in ein Array zu schreiben. Das Array wird als Parameter an den Konstruktor übergeben. Anschließend wird das Device mit einem Stream vom Typ boost::iostreams::stream verknüpft. Dazu ist es nicht nur notwendig, dem Konstruktor von boost::iostreams::stream eine Referenz auf das Device zu übergeben. Da es sich bei boost::iostreams::stream um ein Template handelt, muss außerdem der Typ des Devices als Parameter übergeben werden.

Im Beispiel wird über operator<< Boost in den Stream geschrieben. Der Stream leitet die Daten an das Device weiter. Da das Device mit dem Array buffer verknüpft ist, wird Boost in den ersten fünf Elementen im Array gespeichert. Zur Kontrolle werden diese mit write() auf die Standardausgabe ausgegeben. Führen Sie das Programm aus, wird Boost ausgegeben.

Beispiel 34.2. Ein Array mit boost::iostreams::array_source als Device verwenden
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/stream.hpp>
#include <string>
#include <iostream>

using namespace boost::iostreams;

int main()
{
  char buffer[16];
  array_sink sink{buffer};
  stream<array_sink> os{sink};
  os << "Boost" << std::endl;

  array_source source{buffer};
  stream<array_source> is{source};
  std::string s;
  is >> s;
  std::cout << s << '\n';
}

Beispiel 34.2 baut auf dem vorherigen auf. So wird der String, der mit boost::iostreams::array_sink in ein Array geschrieben wird, mit boost::iostreams::array_source gelesen.

boost::iostreams::array_source wird genauso wie boost::iostreams::array_sink verwendet. Während boost::iostreams::array_sink ausschließlich Schreiboperationen unterstützt, kann mit boost::iostreams::array_source nur gelesen werden. Mit boost::iostreams::array bietet Boost.IOStreams auch ein Device an, das Schreib- und Leseoperationen unterstützt.

Beachten Sie, dass boost::iostreams::array_source und boost::iostreams::array_sink eine Referenz auf ein Array erhalten. Das Array darf nicht zerstört werden, solange die Devices verwendet werden sollen.

Beispiel 34.3. Einen Vektor mit back_insert_device als Device verwenden
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/device/back_inserter.hpp>
#include <boost/iostreams/stream.hpp>
#include <vector>
#include <string>
#include <iostream>

using namespace boost::iostreams;

int main()
{
  std::vector<char> v;
  back_insert_device<std::vector<char>> sink{v};
  stream<back_insert_device<std::vector<char>>> os{sink};
  os << "Boost" << std::endl;

  array_source source{v.data(), v.size()};
  stream<array_source> is{source};
  std::string s;
  is >> s;
  std::cout << s << '\n';
}

Beispiel 34.3 verwendet anstatt boost::iostreams::array_sink ein Device vom Typ boost::iostreams::back_insert_device. Dieses Device kann verwendet werden, um Daten in beliebige Container zu schreiben. Dies geschieht über die Methode insert(), die vom Device aufgerufen wird und vom Container angeboten werden muss.

Im Beispiel wird boost::iostreams::back_insert_device verwendet, um Boost in einen Vektor zu schreiben. Boost wird anschließend mit Hilfe von boost::iostreams::array_source ausgelesen. Dazu werden ein Zeiger auf den Anfang des Vektors und die Größe des Vektors als Parameter an den Konstruktor übergeben. Wenn Sie das Beispielprogramm ausführen, wird wie zuvor Boost ausgegeben.

Beispiel 34.4. Dateien mit boost::iostreams::file_source als Device verwenden
#include <boost/iostreams/device/file.hpp>
#include <boost/iostreams/stream.hpp>
#include <iostream>

using namespace boost::iostreams;

int main()
{
  file_source f{"main.cpp"};
  if (f.is_open())
  {
    stream<file_source> is{f};
    std::cout << is.rdbuf() << '\n';
    f.close();
  }
}

Im Beispiel 34.4 wird das Device boost::iostreams::file_source verwendet, mit dem Dateien gelesen werden können. Während die bisher vorgestellten Devices neben dem Konstruktor keine weiteren Methoden anbieten, kann für boost::iostreams::file_source is_open() aufgerufen werden, um zu überprüfen, ob die gewünschte Datei geöffnet werden konnte. Mit close() steht außerdem eine Methode zur Verfügung, um das Device explizit zu schließen. Ein Aufruf von close() ist nicht zwingend notwendig, da boost::iostreams::file_source automatisch im Destruktor geschlossen wird.

Neben boost::iostreams::file_source bietet Boost.IOStreams mit boost::iostreams::mapped_file_source ein Device an, um eine Datei ganz- oder teilweise in den Speicher zu laden. boost::iostreams::mapped_file_source stellt hierzu eine Methode data() zur Verfügung, um einen Zeiger auf den entsprechenden Speicherbereich zu erhalten. So kann direkt auf beliebige Daten in einer Datei zugegriffen werden, ohne sie sequentiell lesen zu müssen.

Neben boost::iostreams::file_source und boost::iostreams::mapped_file_source bietet Boost.IOStreams die Devices boost::iostreams::file_sink und boost::iostreams::mapped_file_sink an, um schreibend auf Dateien zuzugreifen.

Beispiel 34.5. file_descriptor_source und file_descriptor_sink in Aktion
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
#include <iostream>
#include <Windows.h>

using namespace boost::iostreams;

int main()
{
  HANDLE handles[2];
  if (CreatePipe(&handles[0], &handles[1], nullptr, 0))
  {
    file_descriptor_source src{handles[0], close_handle};
    stream<file_descriptor_source> is{src};

    file_descriptor_sink snk{handles[1], close_handle};
    stream<file_descriptor_sink> os{snk};

    os << "Boost" << std::endl;
    std::string s;
    std::getline(is, s);
    std::cout << s;
  }
}

Im Beispiel 34.5 kommen die Devices boost::iostreams::file_descriptor_source und boost::iostreams::file_descriptor_sink zum Einsatz. Diese Devices ermöglichen Lese- und Schreiboperationen auf systemspezifische Objekte. Unter Windows sind dies Handles, unter POSIX-Betriebssystemen File Descriptoren.

Im Beispiel wird auf die Windows-Funktion CreatePipe() zugegriffen, um eine Pipe zu erstellen. Die Lese- und Schreib-Enden der Pipe werden im Array handles erhalten. Das Lese-Ende wird einem Device vom Typ boost::iostreams::file_descriptor_source, das Schreib-Ende einem Device vom Typ boost::iostreams::file_descriptor_sink übergeben. Alles, was daraufhin in den Stream os geschrieben wird, der mit dem Schreib-Ende verknüpft ist, kann über den Stream is, der mit dem Lese-Ende verknüpft ist, gelesen werden. So wird im Beispiel 34.5 Boost durch die Pipe geschickt und auf die Standardausgabe ausgegeben.

Beachten Sie, dass die Konstruktoren von boost::iostreams::file_descriptor_source und boost::iostreams::file_descriptor_sink zwei Parameter erwarten. Der erste Parameter ist ein Windows-Handle oder – wenn ein Programm für POSIX-Betriebssysteme erstellt wird – ein File Descriptor. Als zweiter Parameter muss boost::iostreams::close_handle oder boost::iostreams::never_close_handle übergeben werden. Dieser Parameter gibt an, ob der Destruktor der Devices den Windows-Handle oder File Descriptor automatisch schließen soll.