Sign in to follow this  
T1Oracle

Figuring out Boost IO Streams

Recommended Posts

I've been trying to use more and more of Boost libraries in my code, but I every time I use a new part of it I run into trouble searching through the documentation to figure out how I can apply it to my needs. Some parts are well documented, but most seem to only explain the most elementary things. At least the filesystem, and iostreams library seemed to. Regardless, I read through the tutorial and I still cannot figure out how I can make an input, output, and i/o stream that can load binary files from my pack file system just as files are loaded from disk. Before trying to use boost iostreams, I created my own classes that did the same an utilized polymorphic stream classes to hide the background details from the user. The objects supported functions such as:
template <typename T>
void Read(T &d) { ... } // reads 1 T from the stream

template <typename OutputIterator>
inline void Read(OutputIterator &dest, uint32 count)  { ... } // reads count objects into output iterator dest

template <typename T>
void Write(const T &d)  { ... } // writes 1 T to the stream

template <typename InputIterator> // writes objects from begin to end to the stream
void Write(InputIterator &begin, InputIterator &end)  { ... }


Since my system only needs to read and write binary data I felt confident that I would not need something that follows all of the iostreams methods. But then I thought about the possibility that the boost library would be more robust, flexible, and it may allow me to work with 3rd party libraries (which I'll need). So I decided that it would be good to create an input stream, and output stream, and an iostream (my system just had read only flag that could allow for error reporting if a write was attempted). After reading the documentation I saw that the concept of a Device fit decently with the i/o stream idea, but the Sinks and Sources were not adequate. I needed Sinks and Sources that were seekable. Unfortunately the documentation does not give much hint in how to implement such a thing so I looked through the header files until I came up with this:
/* This example reads and writes chars to and from a vector as a proof of concept */

// this is a guess at the kind of tag I need
struct is_tag
	: virtual io::input_seekable,
      virtual io::device_tag,
      virtual io::peekable_tag
    { };

// this also is a guess at the kind of tag I need
struct os_tag
	: virtual io::output_seekable,
      virtual io::device_tag,
      virtual io::peekable_tag
    { };

class myIS // input stream class
{
public:
    typedef char	char_type;
	typedef is_tag	category;

	myIS() : pos(0) { c.assign(10,'~'); };

    std::streamsize read(char* s, std::streamsize n)
    {
		// if at or past end, return EOF
		if (pos>=c.size())
			return -1;
		// prevent overruns
		n=std::min(n,static_cast<std::streamsize>(c.size())-pos);
		// copy n bytes
		std::copy(c.begin()+pos,c.begin()+pos+n,s);
		// increment read position
		pos+=n;
		// return # of bytes copies
		return n;
    }

    io::stream_offset seek(io::stream_offset off, std::ios_base::seekdir way)
    {
        // Determine new value of pos_
		io::stream_offset next;
		if (way == std::ios_base::beg) {
            next = off;
        } else if (way == std::ios_base::cur) {
            next = pos + off;
        } else if (way == std::ios_base::end) {
            next = c.size() + off - 1;
        }
        // Check for errors
        if (next < 0 || next > c.size())
            throw std::ios_base::failure("bad seek offset");
        pos = next;
        return pos;
    }

private:
	std::streamsize pos; // current read position
	std::vector<char> c; // data vector
};

class myOS
{
public:
    typedef char	char_type;
	typedef is_tag	category;

	myOS() : pos(0) { };

    std::streamsize write(char* s, std::streamsize n)
    {
		c.assign(s,s+n);
		pos=c.size();
		return n;
    }

    io::stream_offset seek(io::stream_offset off, std::ios_base::seekdir way)
    {
        // Determine new value of pos_
		io::stream_offset next;
		if (way == std::ios_base::beg) {
            next = off;
        } else if (way == std::ios_base::cur) {
            next = pos + off;
        } else if (way == std::ios_base::end) {
            next = c.size() + off - 1;
        }
        // Check for errors
        if (next < 0 || next > c.size())
            throw std::ios_base::failure("bad seek offset");
        pos = next;
        return pos;
    }
private:
	std::streamsize pos; // current read position
	std::vector<char> c;
};

int _tmain(int argc, _TCHAR* argv[])
{
	std::string output;
	myIS cc; // don't know why I have to do this, but io::stream<myIS> in2; causes an assert failure :\
	io::stream<myIS>  in2(cc);

	in2.seekg(3);
	std::getline(in2, output);
	std::cout<<output<<std::endl;

	myOS co;
	io::stream<myOS>  out2(co);
	out2<<output;

	return 0;
}


The myIS class compiles and runs fine, but the myOS class does fails to compile with: ...\documents\visual studio 2005\projects\abs class\abs class\abs class.cpp(186) : error C2678: binary '<<' : no operator found which takes a left-hand operand of type 'boost::iostreams::stream<Device>' (or there is no acceptable conversion) 1> with 1> [ 1> Device=myOS 1> ] 1> ...\documents\visual studio 2005\projects\abs class\abs class\abs class.cpp(18): could be 'std::ostream &operator <<<std::string>(test &,T &)' 1> with 1> [ 1> T=std::string 1> ] 1> ...\documents\visual studio 2005\projects\abs class\abs class\abs class.cpp(55): or 'std::ostream &operator <<(std::ostream &,What &)' 1> while trying to match the argument list '(boost::iostreams::stream<Device>, std::string)' 1> with 1> [ 1> Device=myOS 1> ] I don't know if I am even approaching this correctly, I see no other such examples online. My pack file system can place multiple files into one file while maintaining directory structure (not in any physical way, it cheats using the path string as a key to identify entries into the file). With my classes it have been working flawlessly inserting and removing binary data from the file without error. [Edited by - T1Oracle on May 31, 2007 4:36:23 PM]

Share this post


Link to post
Share on other sites
Silly me, I fixed it just by looking a little closer.

I was using is_tag in the myOS class. I should've been using the os_tag.

I still don't understand Boost IO Streams but at least my code compiles and works now. Hopefully I'll have it working in my pack file system tomorrow.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this