gstreams

## Classes

class | Cgu::basic_gstreambuf< charT, Traits >
| C++ stream buffer for GIO streams. More...
class | Cgu::basic_gostream< charT, Traits >
| C++ output stream for GIO streams. More...
class | Cgu::basic_gistream< charT, Traits >
| C++ input stream for GIO streams. More...
class | Cgu::basic_giostream< charT, Traits >
| C++ input-output stream for GIO streams. More...

## Typedefs

typedef basic_gstreambuf< char > | Cgu::gstreambuf
| C++ stream buffer for GIO streams for char type. More...
typedef basic_gistream< char > | Cgu::gistream
| C++ input stream for GIO streams for char type. More...
typedef basic_gostream< char > | Cgu::gostream
| C++ output stream for GIO streams for char type. More...
typedef basic_giostream< char > | Cgu::giostream
| C++ input/output stream for GIO streams for char type. More...
typedef basic_gstreambuf< wchar_t > | Cgu::wgstreambuf
| C++ stream buffer for GIO streams for wchar_t type. More...
typedef basic_gistream< wchar_t > | Cgu::wgistream
| C++ input stream for GIO streams for wchar_t type. More...
typedef basic_gostream< wchar_t > | Cgu::wgostream
| C++ output stream for GIO streams for wchar_t type. More...
typedef basic_giostream< wchar_t > | Cgu::wgiostream
| C++ input/output stream for GIO streams for wchar_t type. More...
typedef basic_gstreambuf < char16_t > | Cgu::u16gstreambuf
| C++ stream buffer for GIO streams for char16_t type. More...
typedef basic_gistream< char16_t > | Cgu::u16gistream
| C++ input stream for GIO streams for char16_t type. More...
typedef basic_gostream< char16_t > | Cgu::u16gostream
| C++ output stream for GIO streams for char16_t type. More...
typedef basic_giostream< char16_t > | Cgu::u16giostream
| C++ input/output stream for GIO streams for char16_t type. More...
typedef basic_gstreambuf < char32_t > | Cgu::u32gstreambuf
| C++ stream buffer for GIO streams for char32_t type. More...
typedef basic_gistream< char32_t > | Cgu::u32gistream
| C++ input stream for GIO streams for char32_t type. More...
typedef basic_gostream< char32_t > | Cgu::u32gostream
| C++ output stream for GIO streams for char32_t type. More...
typedef basic_giostream< char32_t > | Cgu::u32giostream
| C++ input/output stream for GIO streams for char32_t type. More...

## Detailed Description

#include <c++-gtk-utils/gstream.h>

The c++-gtk-utils library contains C++ classes providing a streambuffer and stream objects interfacing with GIO streams.

Normally 'true' would be passed as the second (manage) argument of the gostream/gistream/giostream constructors or of the attach() methods, so that the destructors of these classes close the GIO streams concerned, which helps exception safety (the attach() method will also close any previous GIO stream). If this behaviour is not wanted, pass 'false' instead to that argument.

C++ stream objects are not suitable for asynchronous input and output. On sockets and pipes or other special devices, they may block on a read or write until the read or write request has been satisfied. In circumstances where asynchronous input and output is wanted, it will be necessary to start a new thread in which to carry out the input and output operations (which is what GIO does behind the scenes on its asynchronous operations) or use the GIO interfaces directly.

Here are some examples of use:

// open file for input, another for output, and make the
// output file a gzipped copy of the input (gzipping a
// file doesn't get much easier than this)
Cgu::gistream input;
Cgu::gostream output;
Cgu::GobjHandle<GFile> file_in(g_file_new_for_path("filename"));
GFileInputStream* is = g_file_read(file_in, 0, 0);
if (is)
input.attach(Cgu::GobjHandle<GInputStream>(G_INPUT_STREAM(is)), // takes ownership of 'is'
true);
else {
std::cerr << "Can't open file 'filename'" << std::endl;
return;
}
Cgu::GobjHandle<GFile> file_out(g_file_new_for_path("filename.gz"));
GFileOutputStream* os = g_file_replace(file_out, 0, false,
G_FILE_CREATE_REPLACE_DESTINATION,
0, 0);
if (os) {
output.attach(Cgu::GobjHandle<GOutputStream>(G_OUTPUT_STREAM(os)), // takes ownership of 'os'
true,
Cgu::GobjHandle<GConverter>(
G_CONVERTER(g_zlib_compressor_new(G_ZLIB_COMPRESSOR_FORMAT_GZIP, -1)))
);
}
else {
std::cerr << "Can't create file 'filename.gz'" << std::endl;
return;
}
// this does the copying, and is shorthand for creating your own buffer
// and calling std::istream::read() and std::ostream::write() on it
output << input.rdbuf();
--------------------------------------------------------------------
// establish a TCP socket on localhost, listen for connections on port
// 1200 and receive whitespace-separated words for processing
using Cgu::GobjHandle;
GobjHandle<GInetAddress> i(g_inet_address_new_loopback(G_SOCKET_FAMILY_IPV4));
GobjHandle<GSocketAddress> a(g_inet_socket_address_new(i, 1200));
GobjHandle<GSocketListener> l(g_socket_listener_new());
gboolean success = g_socket_listener_add_address(l,
a,
G_SOCKET_TYPE_STREAM,
G_SOCKET_PROTOCOL_TCP,
0, 0, 0);
if (!success) {
std::cerr << "Can't bind socket on localhost" << std::endl;
return;
}
GSocketConnection* c = g_socket_listener_accept(l, 0, 0, 0);
if (!c) {
std::cerr << "Can't listen on localhost" << std::endl;
return;
}
Cgu::giostream sock_strm(GobjHandle<GIOStream>(G_IO_STREAM(c)), true); // takes ownership of c
sock_strm.set_output_buffered(false);
std::cout << "Connection accepted" << std::endl;
std::string str;
while (sock_strm >> str) {
[ ... do something with the word in str ... ]
// acknowledge the client
sock_strm << "ACK\n";
}
--------------------------------------------------------------------
// read line delimited text from a pipe until it is closed by the
// writer: assume 'fd' is the read file descriptor of the pipe
// (in real life you would probably want to use a Cgu::fdistream
// object in this usage and bypass GIO)
Cgu::gistream istrm(Cgu::GobjHandle<GInputStream>(g_unix_input_stream_new(fd, true)), true);
if (!istrm.get_gio_stream().get()) {
std::cerr << "Can't create gio file-descriptor input stream" << std::endl;
return;
}
std::string line;
while (std::getline(istrm, line)) {
[ ... do something with the read text ... ]
}

Note
  1. Users cannot (except by derivation) use the virtual protected methods of the streambuffer classes, including xsgetn() and xsputn(). Instead, if they want direct access to the streambuffer other than through the gostream/gistream/giostream methods (or their wide stream equivalents), they should use the public forwarding functions provided by std::streambuf base class.
  2. These streambuffers and stream objects are not copiable.
  3. The base glib requirement for the c++-gtk-utils library is glib >= 2.10.0, but the gstreams component will only be available if glib >= 2.16.0 is installed.

## Buffering

The classes implement buffering on input streams and (unless output buffering is switched off) on output streams. They implement the buffering internally and do not use GBufferedInputStream and GBufferedOutputStream. This has a number of efficiency advantages and also retains random access, on devices that support it, for buffered streams (GBufferedInputStream and GBufferedOutputStream do not do so). So far as concerns random access on GIOStream objects, which are opened for both read and write, see giostream and random access

The streambuf class provides a block read and write in xsgetn() and xsputn(), which will be called by the read() and write() methods (and some other output operators) inherited by (w)gistream, (w)gostream and (w)giostream from std::basic_istream and std::basic_ostream. They operate (after appropriately vacating and resetting the buffers) by doing a block read and write directly to and from the target, and are very efficient for large block reads (those significantly exceeding the buffer size). If users want all reads and writes to go through the buffers, by using std::basic_streambuf<>::xsputn() and std::basic_streambuf<>::xsgetn() then the symbol CGU_GSTREAM_USE_STD_N_READ_WRITE can be defined. (libstdc++-3 provides efficient inbuilt versions of these std::basic_streambuf functions for block reads not significantly larger than the buffer size, provided output buffering has not been turned off by the set_output_buffered() method of these classes.)

Note however that if CGU_GSTREAM_USE_STD_N_READ_WRITE is to be defined, it is best to do this by textually amending the installed gstream.h header file rather than by defining the symbol in user code before that file is included. This will ensure that all source files in a program which include the gstream.h header are guaranteed to see the same definitions so that the C++ standard's one-definition-rule is complied with.

One possible case for defining that symbol is where the user wants to use the tie() method of (w)gistream or (w)giostream (inherited from std::basic_ios) to procure flushing of an output stream before extraction from an input stream is made by (w)gistream::read() or (w)giostream::read(). Such flushing might not occur where a call to read() is made unless CGU_GSTREAM_USE_STD_N_READ_WRITE is defined, because an implementation is permitted to defer such flushing until underflow() occurs, and the block read by read(), as forwarded to xsgetn(), will never invoke underflow() if that symbol is not defined. (Having said that, any basic_istream implementation which does defer output flushing until underflow() is called makes tie() unusable anyway for a number of purposes, because the time of flushing would become dependent on whether a read request can be satisfied by what is already in the buffers.)

4 characters are stored and available for putback. However, if the symbol CGU_GSTREAM_USE_STD_N_READ_WRITE is not defined, then a call to (w)gstreambuf::xsgetn() via (w)gistream::read() or (w)giostream::read() with a request for less than 4 characters will result in less than 4 characters available for putback (if these block read methods obtain some characters but less than 4, only the number of characters obtained by them is guaranteed to be available for putback).

## giostream and random access

For GIO objects which implement GSeekable (which are GFileIOStream, GFileInputStream, GFileOutputStream, GMemoryInputStream and GMemoryOutputStream), the classes in this c++-gtk-utils library implement the tellg(), tellp(), seekg() and seekp() random access methods.

The presence of buffering does not impede this where a stream is only opened for reading or only opened for writing. However, it presents complications if the giostream or wgiostream classes are used with a GFIleIOStream object (GFileIOStream objects are opened for both read and write). Because the classes employ buffering of input, and optional buffering of output, the logical file position (the file position expected by the user from the reads and writes she has made) will usually differ from the actual file position seen by the underlying operating system. The gstreambuf class provided by this library implements intelligent tying between input and output streams for GFileIOStream objects which means that if output has been made unbuffered by a call to set_output_buffered(false) and no converter has been attached, all reads and writes onto the file system from the same giostream object will be made at the expected logical position.

This cannot be done by the gstreambuf class where the output stream is set as buffered (the default). In that case, if the last operation on a giostream or wgiostream object 'strm' was a read, before the first write operation thereafter is made on it, the user should call strm.seekg(strm.tellg()) or strm.seekp(strm.tellp()) (the two have the same effect), in order to synchronise the logical and actual file positions, or if the user does not want to maintain the current logical file position, make some other call to seekg() or seekp() which does not comprise only seekg(0, std::ios_base::cur) or seekp(0, std::ios_base::cur). Many std::basic_iostream implementations, as inherited by Cgu::giostream, will synchronise positions automatically on seekable streams via their sentry objects in order to provide support for buffered random access on their std::basic_fstream class (gcc's libstdc++ library does this, for example), making these steps unnecessary, but following these steps will provide maximum portability. The same applies to the equivalent wide stream classes.

If a GFileIOStream object attached to a giostream or wgiostream object is not seekable (that is, can_seek() returns false), say because an input or output converter has been attached or the filesystem is a network file system, no random access may be attempted. In particular, the tellg(), tellp(), seekg() and seekp() methods will not work (they will return pos_type(off_type(-1))). Furthermore, if a giostream or wgiostream object which manages a GFileIOStream object (as opposed to a socket) has a converter attached or is not seekable for some other reason, then after a read has been made no further write may be made using the same GFileIOStream object, so the use of converters with giostream or wgiostream objects should generally be restricted to use with sockets (GSocketConnection objects) only. Where converters are used with files on a filesystem, it is best to use the gostream and gistream classes (or their wide stream equivalents), and to close one stream before opening the other where they address the same file.

None of these restrictions applies to GSocketConnection objects obtained by a call to g_socket_listener_accept() or g_socket_client_connect(), or obtained in some other way, as these do not maintain file pointers. They can be attached to a giostream or wgiostream object (with or without a converter) without any special precautions being taken, other than the normal step of calling giostream::flush() (or using the std::flush manipulator) to flush the output buffer to the socket if the user needs to know that that has happened (or setting output buffering off with the set_output_buffered() method). In summary, on a socket, a read does not automatically flush the output buffer: it is for the user to do that.

## Wide streams and endianness

This library provides typedef'ed instances of the template classes for wchar_t, char16_t and char32_t characters. Unless a converter is attached (for, say, UTF-32LE to UTF-32BE, or vice versa), with these wide character classes wide characters are written out in the native endian format of the writing machine. Whether or not a converter from one endianness to another is attached, special steps need to be taken if the text which is sent for output might be read by machines of unknown endianness.

No such special steps are required where the wide character classes are used with temporary files, pipes, fifos, unix domain sockets and network sockets on localhost, because in those cases they will be read by the same machine that writes; but they are required where sockets communicate with other computers over a network or when writing to files which may be distributed to and read by other computers with different endianness.

Where wide characters are to be exported to other machines, one useful approach is to convert to and from UTF-8 with Utf8::uniwide_from_utf8(), Utf8::uniwide_to_utf8(), Utf8::wide_from_utf8() or Utf8::wide_to_utf8(), and to use gostream/gistream/giostream with the converted text, or to attach a converter for UTF-8, generated by GIO's g_charset_converter_new(), directly to a wgostream, wgistream or wgiostream object (or their char16_t and char32_t equivalents).

Instead of converting exported text to UTF-8, another approach is to use a byte order marker (BOM) as the first character of the wide stream output. UCS permits a BOM character to be inserted, comprising static_cast<wchar_t>(0xfeff), static_cast<char16_t>(0xfeff) or static_cast<char32_t>(0xfeff), at the beginning of the output to the wide character stream. At the receiving end, this will appear as 0xfffe (UTF-16) or 0xfffe0000 (UTF-32) to a big endian machine with 8 bit char type if the text is little endian, or to a little endian machine with big endian text, so signaling a need to undertake byte swapping of text read from the stream. Another alternative is to label the physical medium conveying the file as UTF-16LE, UTF-16BE, UTF-32LE or UTF-32BE, as the case may be, in which case a BOM character should not be prepended.

Where it is established by either means that the input stream requires byte swapping, the wide character input stream and wide character input streambuffer classes have a set_byteswap() member function which should be called on opening the input stream as soon as it has been established that byte swapping is required. Once this function has been called with an argument of 'true', all further calls to stream functions which provide characters will provide those characters with the correct native endianness. Calling set_byteswap() on the narrow stream gistream, giostream and gstreambuf objects has no effect (byte order is irrelevant to narrow streams).

Here is an example of such use in a case where sizeof(wchar_t) is 4:

using Cgu::GobjHandle;
Cgu::wgistream input;
GobjHandle<GFile> file_in(g_file_new_for_path("filename"));
GFileInputStream* is = g_file_read(file_in, 0, 0);
if (is)
input.attach(GobjHandle<GInputStream>(G_INPUT_STREAM(is)), true); // takes ownership of 'is'
else {
std::cerr << "Can't open file 'filename'"
<< std::endl;
return;
}
wchar_t item;
input.get(item);
if (!input) {
std::cerr << "File 'filename' is empty" << std::endl;
return;
}
if (item == static_cast<wchar_t>(0xfffe0000))
input.set_byteswap(true);
else if (item != static_cast<wchar_t>(0xfeff)) {
// calling set_byteswap() will manipulate the buffers, so
// either call putback() before we call set_byteswap(), or
// call unget() instead
input.putback(item);
// the first character is not a BOM character so assume big endian
// format, and byte swap if the local machine is little endian
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
input.set_byteswap(true);
#endif
}
[ ... do something with the input file ... ]

## Other wide stream issues

basic_gostream, basic_gistream, basic_giostream and basic_gstreambuf objects can be instantiated for any integer type which has an appropriate traits class provided for it which has the copy(), eof(), eq_int_type(), move(), not_eof() and to_int_type() static member functions. The integer type could in fact have any size, but the set_byteswap() methods for basic_gistream, basic_giostream and basic_gstreambuf will only have an effect if its size is either 2 or 4. Typedef'ed instances of the classes are provided by the library for characters of type wchar_t, char16_t and char32_t.

## gtkmm users

gtkmm/giomm does not provide C++ streams for GIO objects: instead, it provides a literal function-for-function wrapping. However, giomm users can use the stream classes provided by this library by converting the relevant Glib::RefPtr object to a Cgu::GobjHandle object. This can be done as follows:

// Glib::RefPtr<Gio::InputStream> to Cgu::GobjHandle<GInputStream>
inline
Cgu::GobjHandle<GInputStream> giomm_input_convert(const Glib::RefPtr<Gio::InputStream>& in) {
return Cgu::GobjHandle<GInputStream>(static_cast<GInputStream*>(g_object_ref(in.operator->()->gobj())));
}
// Glib::RefPtr<Gio::OutputStream> to Cgu::GobjHandle<GOutputStream>
inline
Cgu::GobjHandle<GOutputStream> giomm_output_convert(const Glib::RefPtr<Gio::OutputStream>& out) {
return Cgu::GobjHandle<GOutputStream>(static_cast<GOutputStream*>(g_object_ref(out.operator->()->gobj())));
}
// Glib::RefPtr<Gio::IOStream> to Cgu::GobjHandle<GIOStream>
inline
Cgu::GobjHandle<GIOStream> giomm_io_convert(const Glib::RefPtr<Gio::IOStream>& io) {
return Cgu::GobjHandle<GIOStream>(static_cast<GIOStream*>(g_object_ref(io.operator->()->gobj())));
}
// example printing a text file to stdout with file opened with giomm
Glib::RefPtr<Gio::File> file = Gio::File::create_for_path("filename");
Glib::RefPtr<Gio::InputStream> is = file->read();
Cgu::gistream filein(giomm_input_convert(is), true);
std::string line;
while (std::getline(filein, line)) {
std::cout << line << '\n';
}

## Typedef Documentation

Cgu::giostream

C++ input/output stream for GIO streams for char type.

Cgu::gistream

C++ input stream for GIO streams for char type.

Cgu::gostream

C++ output stream for GIO streams for char type.

Cgu::gstreambuf

C++ stream buffer for GIO streams for char type.

Cgu::u16giostream

C++ input/output stream for GIO streams for char16_t type.

Cgu::u16gistream

C++ input stream for GIO streams for char16_t type.

Cgu::u16gostream

C++ output stream for GIO streams for char16_t type.

Cgu::u16gstreambuf

C++ stream buffer for GIO streams for char16_t type.

Cgu::u32giostream

C++ input/output stream for GIO streams for char32_t type.

Cgu::u32gistream

C++ input stream for GIO streams for char32_t type.

Cgu::u32gostream

C++ output stream for GIO streams for char32_t type.

Cgu::u32gstreambuf

C++ stream buffer for GIO streams for char32_t type.

Cgu::wgiostream

C++ input/output stream for GIO streams for wchar_t type.

Cgu::wgistream

C++ input stream for GIO streams for wchar_t type.

Cgu::wgostream

C++ output stream for GIO streams for wchar_t type.

Cgu::wgstreambuf

C++ stream buffer for GIO streams for wchar_t type.