The Boost C++ Libraries

Devices

Devices are classes that provide read and write access to objects that are usually outside of a process – for example, files. However, you can also access internal objects, such as arrays, as devices.

A device is nothing more than a class with the member function read() or write(). A device can be connected with a stream so you can read and write formatted data rather than using the read() and write() member functions directly.

Example 34.1. Using an array as a device with boost::iostreams::array_sink
#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);
}

Example 34.1 uses the device boost::iostreams::array_sink to write data to an array. The array is passed as a parameter to the constructor. Afterwards, the device is connected with a stream of type boost::iostreams::stream. A reference to the device is passed to the constructor of boost::iostreams::stream, and the type of the device is passed as a template parameter to boost::iostreams::stream.

The example uses the operator operator<< to write Boost to the stream. The stream forwards the data to the device. Because the device is connected to the array, Boost is stored in the first five elements of the array. Since the array’s contents are written to standard output, Boost will be displayed when you run the example.

Example 34.2. Using an array as a device with boost::iostreams::array_source
#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';
}

Example 34.2 is based on the previous example. The string written with boost::iostreams::array_sink to an array is read with boost::iostreams::array_source.

boost::iostreams::array_source is used like boost::iostreams::array_sink. While boost::iostreams::array_sink supports only write operations, boost::iostreams::array_source supports only read. boost::iostreams::array supports both write and read operations.

Please note that boost::iostreams::array_source and boost::iostreams::array_sink receive a reference to an array. The array must not be destroyed while the devices are still in use.

Example 34.3. Using a vector as a device with boost::iostreams::back_insert_device
#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';
}

Example 34.3 uses a device of type boost::iostreams::back_insert_device, instead of boost::iostreams::array_sink. This device can be used to write data to any container that provides the member function insert(). The device calls this member function to forward data to the container.

The example uses boost::iostreams::back_insert_device to write Boost to a vector. Afterwards, Boost is read from boost::iostreams::array_source. The address of the beginning of the vector and the size are passed as parameters to the constructor of boost::iostreams::array_source.

Example 34.3 displays Boost.

Example 34.4. Using a file as a device with boost::iostreams::file_source
#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();
  }
}

Example 34.4 uses the device boost::iostreams::file_source to read files. While the previously introduced devices don’t provide member functions, boost::iostreams::file_source provides is_open() to test whether a file was opened successfully. It also provides the member function close() to explicitly close a file. You don’t need to call close() because the destructor of boost::iostreams::file_source closes a file automatically.

Besides boost::iostreams::file_source, Boost.IOStreams also provides the device boost::iostreams::mapped_file_source to load a file partially or completely into memory. boost::iostreams::mapped_file_source defines a member function data() to receive a pointer to the respective memory area. That way, data can be randomly accessed in a file without having to read the file sequentially.

For write access to files, Boost.IOStreams provides the devices boost::iostreams::file_sink and boost::iostreams::mapped_file_sink.

Example 34.5. Using file_descriptor_source and file_descriptor_sink
#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;
  }
}

Example 34.5 uses the devices boost::iostreams::file_descriptor_source and boost::iostreams::file_descriptor_sink. These devices support read and write operations on platform-specific objects. On Windows these objects are handles, and on POSIX operating systems they are file descriptors.

Example 34.5 calls the Windows function CreatePipe() to create a pipe. The read and write ends of the pipe are received in the array handles. The read end of the pipe is passed to the device boost::iostreams::file_descriptor_source, and the write end is passed to the device boost::iostreams::file_descriptor_sink. Everything written to the stream os, which is connected to the write end, can be read from the stream is, which is connected to the read end. Example 34.5 sends Boost through the pipe and to standard output.

The constructors of boost::iostreams::file_descriptor_source and boost::iostreams::file_descriptor_sink expect two parameters. The first parameter is a Windows handle or – if the program is run on a POSIX system – a file descriptor. The second parameter must be either boost::iostreams::close_handle or boost::iostreams::never_close_handle. This parameter specifies whether or not the destructor closes the Windows handle or file descriptor.