views:

129

answers:

1

I'm trying to update a random-access binary file using the std::iostream interface with separate get/put positions managed via seekg/seekp. Everything works fine with stringstream, but when I create a file descriptor-based stream using Boost.Iostream (specifically boost::iostreams::stream<boost::iostreams::file_descriptor>), the get/put positions are no longer independent.

I gather from the documentation on Modes that what I'm looking for is a "Dual-seekable" stream. The docs show that Mode is a template parameter of stream, "principally for internal use", but this seems to be (no longer?) correct. Instead, the mode (aka category?) is taken directly from the Device:

template< typename Device,
          typename Tr = ...,
          typename Alloc = ...>
struct stream : detail::stream_base<Device, Tr, Alloc> {
public:
    typedef typename char_type_of<Device>::type  char_type;
    struct category 
        : mode_of<Device>::type,
          closable_tag,
          detail::stream_traits<Device, Tr>::stream_tag
        { };

Primary question: Is there some way to get Dual-seekable behavior from a Device such as file_descriptor (which is tagged Seekable but not Dual-seekable)?

Secondary question: Are there any general guarantees about the independence of seekg/seekp? I gather from web searches that stringstream seems to be independent, but that fstream may not be. However, I can't find anything authoritative.

A: 

If you construct a bidirectional_seekable boost::iostreams::device it will support two separate get/put positions which can be modified with the help of the iostreams::seek function.

Roughly, this will look like:

struct binary_seekable_device 
 : boost::iostreams::device<boost::iostreams::bidirectional_seekable>
{
    explicit binary_seekable_device(int fd)
      : fd(fd), pos_read(0), pos_write(0) {}

    std::streamsize read(char *s, std::streamsize n);
    std::streamsize write(char const *s, std::streamsize n);
    std::streampos seek(boost::iostreams::stream_offset off,
        std::ios::seekdir way, std::ios::openmode which);

    int fd; 
    std::size_t pos_read;
    std::size_t pos_write;
};

You need to implement your stream logic by filling out the three functions (read, write, seek), see the examples and documentation for details. The important point for you is the parameter std::ios::openmode which giving you a clue which of the positions (read, write, or both) you need to update.

Now you use this device while instantiating an boost::iostreams::stream:

int fd = open(...);
boost::iostream::stream<binary_seekable_device> s(fd);

where s is your stream instance you can use to do the require file operations.

hkaiser
Based on your answer and further reading of the Boost.Iostream code, it sounds like I would need to roll my own version of boost::iostreams::file_descriptor. On one hand, there are only 3 methods to implement; on the other, I'd need to replicate all the "#ifdef BOOST_IOSTREAMS_WINDOWS" stuff for Windows/POSIX portability. (These streams are used to access temp files created using OS-specific APIs.) Anyway, thanks for the help!
Trevor Robinson