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.
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.
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.
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.
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.
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.