c++-gtk-utils
Classes | Typedefs
fdstreams

Classes

class  Cgu::basic_fdoutbuf< charT, Traits >
 Output stream buffer for unix file descriptors. More...
 
class  Cgu::basic_fdostream< charT, Traits >
 Output stream for unix file descriptors. More...
 
class  Cgu::basic_fdinbuf< charT, Traits >
 Input stream buffer for unix file descriptors. More...
 
class  Cgu::basic_fdistream< charT, Traits >
 Input stream for unix file descriptors. More...
 

Typedefs

typedef basic_fdinbuf< char > Cgu::fdinbuf
 Input stream buffer for file descriptors for char type. More...
 
typedef basic_fdoutbuf< char > Cgu::fdoutbuf
 Output stream buffer for file descriptors for char type. More...
 
typedef basic_fdistream< char > Cgu::fdistream
 Input stream for file descriptors for char type. More...
 
typedef basic_fdostream< char > Cgu::fdostream
 Output stream for file descriptors for char type. More...
 
typedef basic_fdinbuf< wchar_t > Cgu::wfdinbuf
 Input stream buffer for file descriptors for wchar_t type. More...
 
typedef basic_fdoutbuf< wchar_t > Cgu::wfdoutbuf
 Output stream buffer for file descriptors for wchar_t type. More...
 
typedef basic_fdistream< wchar_t > Cgu::wfdistream
 Input stream for file descriptors for wchar_t type. More...
 
typedef basic_fdostream< wchar_t > Cgu::wfdostream
 Output stream for file descriptors for wchar_t type. More...
 

Detailed Description

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

The c++-gtk-utils library contains classes providing streambuffers and stream objects for unix file descriptors.

By default, like the fstream::fstream(int fd) and fstream::attach(int fd) extensions in libstdc++-v2, the destructors of these classes close the file descriptors concerned, which helps exception safety (the attach() method will also close any previous file descriptor). If this behaviour is not wanted, pass 'false' as the second argument of fdostream/fdistream constructor or of the attach() method (the same applies to the wide stream classes). This will enable the same file descriptor to be used successively for, say, reading and writing, or to be shared between fdistream and fdostream objects (but if the file descriptor represents a device providing random access, such as a local file on the filesystem, which has been opened for both reading and writing, the special precautions described under fdstreams and random access are required).

Here are some examples of use:

// the safe creation of a temporary file with standard iostreams
char filename[] = "/tmp/myprog-XXXXXX";
int fd = mkstemp(filename);
if (fd != -1) {
ostrm.attach(fd); // take ownership of the file descriptor
ostrm << "Temporary file text" << std::endl;
}
else {
std::cerr << "Can't open temporary file " << filename
<< ", please check permissions" << std::endl;
}
--------------------------------------------------------------------
// mimic std::cout but explicitly use UNIX stdout
Cgu::fdostream out(1, false); // don't take ownership of the file descriptor
out << "Hello" << std::endl;
--------------------------------------------------------------------
// read line delimited text from a pipe until it is closed by the
// writer: assume 'fd' is the read file descriptor of the pipe
Cgu::fdistream istrm(fd); // take ownership of the read file descriptor
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 fdostream/fdistream 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.

Buffering

The streambuffer classes provide buffering for both input and output, although from version 1.2.6 output buffering can be switched off using the set_buffered() method.

The streambuf classes provide 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)fdistream and (w)fdostream from std::basic_istream and std::basic_ostream. They operate (after appropriately vacating and resetting the buffers) by doing a block read and write by calling Unix read() and write() 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 FDSTREAM_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_buffered() method of the output streambuffer or stream object.)

Note however that if FDSTREAM_USE_STD_N_READ_WRITE is to be defined, it is best to do this by textually amending the installed fdstream.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 fdstream.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)fdistream (inherited from std::basic_ios) to procure flushing of an output stream before extraction from an input stream is made by (w)fdistream::read(). Such flushing might not occur where a call to (w)fdistream::read() is made unless FDSTREAM_USE_STD_N_READ_WRITE is defined, because an implementation is permitted to defer such flushing until underflow() occurs, and the block read by (w)fdistream::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 FDSTREAM_USE_STD_N_READ_WRITE is not defined, then a call to fdinbuf::xsgetn() via (w)fdistream::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).

fdstreams and random access

For file descriptors representing files which offer random access, from version 1.2.6 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 file descriptor is only opened for reading or only opened for writing. However, it presents complications if a fdistream object and a fdostream object (or their wide stream equivalents) reference the same file descriptor on a file which offers random access and which is opened for both reading and writing. To prevent the file pointer getting out of sync with the buffers maintained by the (w)fdistream and (w)fdostream objects, if the last operation carried out on the (w)fdostream/(w)fdistream pair was a write then, if the output stream is set as buffered (the default), before the first read operation thereafter is made on the pair or a call to seekg() is made, the (w)fdostream object must be flushed by calling std::(w)ostream::flush() or by using the std::flush manipulator (or setting the std::ios_base::unitbuf flag), and likewise the wide stream classes. If the last operation on the pair (having, say, the names 'ostr' and 'istr') was a read, then before the first write operation thereafter is made on the pair, or a call to seekp() is made, the user must call istr.seekg(istr.tellg()) 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() on 'istr' which does not comprise only seekg(0, std::ios_base::cur). This requirement to call seekg() when moving from reading to writing applies whether or not the output stream is buffered.

Note that the tie() method of (w)fdistream (inherited from std::basic_ios) cannot reliably to used to procure output flushing of a (w)fdostream object before a read is made, unless FDSTREAM_USE_STD_N_READ_WRITE is defined before fdstream.h is #include'd, for the reason mentioned under "Buffering" above.

Where a file is to be opened for both reading and writing and more automatic tying of input and output is wanted, the Cgu::giostream and Cgu::wgiostream classes can be used in conjunction with GIO streams.

None of these restrictions applies to file descriptors opened for reading and writing which represent devices for which the operating system does not maintain file pointers, such as sockets. They can be attached to a fdostream and fdistream object or their wide stream equivalents without any special precautions being taken, other than the normal step of calling fdostream::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_buffered() method). In summary, on a socket, a read does not automatically flush the output buffer: it is for the user to do that. Note also that only one of the stream objects should be set to manage the file descriptor, and this should normally be the output stream as it may have characters to flush when closing.

A (w)fdostream and (w)fdistream object should not reference the same file descriptor on any file on a file system which permits read-write opening of files and reports itself as not supporting random access, but which in fact maintains a file position pointer which is shared for reading and writing. This might apply to some network file systems. The best rule to apply is not to reference the same file descriptor on a (w)fdostream and (w)fdistream object if the device is not a socket, unless can_seek() returns true.

Wide streams and endianness

With the wide character ostream class and wide character output streambuffer class, wide characters are written out in the native endian format of the writing machine. Special steps need to be taken if the text which is sent for output might be read by machines with a different endianness.

No such special steps are required where the wfdostream and wfdistream 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 fdostream/fdistream with the converted text. Alternatively, from version 1.2.6, the wgostream, wgistream and wgiostream classes can be used for the purposes of attaching a UTF-8 converter directly to a GIO stream. (Those classes also enable a UTF-32LE to UTF-32BE converter, and vice versa, to be attached to an output stream for the purpose of writing out UTF-32 in other than native endianness, and similarly as regards UTF-16.)

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), 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, from version 1.2.5 the wfdistream and wfdinbuf 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 fdistream or fdinbuf 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:

int fd = open("filename", O_RDONLY);
if (fd != -1)
input.attach(fd); // take ownership of the file descriptor
else {
std::cerr << "Can't open file 'filename', "
<< "please check permissions" << 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_fdostream, basic_fdoutbuf, basic_fdistream and basic_fdinbuf 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. Thus, with compilers supporting the C++11 char16_t and char32_t types, streams and streambuffers of type basic_fdostream<char16_t>, basic_fdoutbuf<char16_t>, basic_fdistream<char16_t>, basic_fdinbut<char16_t>, basic_fdostream<char32_t>, basic_fdoutbuf<char32_t>, basic_fdistream<char32_t> and basic_fdinbut<char32_t> can be instantiated (typdef'ed to, say, u16fdostream, u16fdoutbuf, u16fdistream, u16fdinbuf and so forth) to provide UTF-16 support on platforms where sizeof(wchar_t) is 4, and UTF-32 on platforms where sizeof(wchar_t) is 2. The integer type could in fact have any size, but the set_byteswap() methods for basic_fdistream and basic_fdinbuf will only have an effect if its size is either 2 or 4.

Memory slices

If the library is compiled with the --with-glib-memory-slices-compat or --with-glib-memory-slices-no-compat configuration option, basic_fdoutbuf constructs its output buffer using glib memory slices. In such a case, although it is safe in a multi-threaded program if glib < 2.32 is installed to construct a static basic_fdoutbuf/basic_fdostream object in global namespace (that is, prior to g_thread_init() being called) by means of the default constructor and/or a file descriptor argument of -1, it is not safe if it is constructed with a valid file descriptor. If glib >= 2.32 is installed, global objects with memory slices are safe in all circumstances. (Having said that, it would be highly unusual to have global output stream objects.) This issue does not affect basic_fdinbuf/basic_fdistream objects, which do not construct their buffers dynamically.

Typedef Documentation

Input stream buffer for file descriptors for char type.

Input stream for file descriptors for char type.

Output stream for file descriptors for char type.

Output stream buffer for file descriptors for char type.

Input stream buffer for file descriptors for wchar_t type.

Input stream for file descriptors for wchar_t type.

Output stream for file descriptors for wchar_t type.

Output stream buffer for file descriptors for wchar_t type.