views:

136

answers:

1

I have a Visual Studio 2008 C++ application where I would like to treat a stream as a set of iterators.

For example, if I were to receive an array of WIN32_FIND_DATA structures over the stream, I would like to be able to do something like this:

IStreamBuf< WIN32_FIND_DATA > sb( stream );
std::vector< WIN32_FIND_DATA > buffer;
std::copy( std::istreambuf_iterator< WIN32_FIND_DATA >( &sb ), 
           std::istreambuf_iterator< WIN32_FIND_DATA >(),
           std::back_inserter( buffer ) );

To accomplish this, I've defined a class derived from std::basic_streambuf<>:

template< typename T >
class IStreamBuf : public std::basic_streambuf< byte >
{
public:

    IStreamBuf( IStream* stream ) : stream_( stream )
    {        
    };

protected:

    virtual traits_type::int_type underflow()
    {
        DWORD bytes_read = 0;
        HRESULT hr = stream_->Read( &buffer_, sizeof( T ), &bytes_read );
        if( FAILED( hr ) )
            return traits_type::eof();

        traits_type::char_type* begin = 
            reinterpret_cast< traits_type::char_type* >( &buffer_ );
        setg( begin, begin, begin + bytes_read );   
        return traits_type::to_int_type( *gptr() );
    };

private:

    // buffer to hold current item of type T
    T buffer_;

    // stream used to receive data
    IStream* stream_;
}; // class IStreamBuf

What I can't figure out is how to gracefully go from an array of bytes to an array of WIN32_FIND_DATAs. Because std::basic_streambuf<> requires a std::char_traits<> template parameter, I'm under the impression that it can only use built-in types like char or byte, not a structure like WIN32_FIND_DATA. Correct?

Any suggestions on how to make this work?

Thanks, PaulH

+1  A: 

An istreambuf_iterator works at the buffer level, where it's just a stream of bytes. If you want to deal with structures, you probably want to use an istream_iterator instead, and create an operator>> to read the WIN32_FIND_DATA structures. You might also consider creating/using a proxy for the WIN32_FIND_DATA.

Jerry Coffin
The reason I went with the `std::istreambuf_iterator`s is the `std::istream_iterator`s operate on formatted input. So, I would have a bit of additional overhead (sentry objects, stream-flag checking, etc..)Can you elaborate on what you meant by "a proxy for the WIN32_FIND_DATA". Preferably a small example?
PaulH
@PaulH: you only have to deal with sentry objects if you read from the underlying buffer yourself -- reading from the stream using existing stream reading functions doesn't require a sentry object. An example proxy is at: http://stackoverflow.com/questions/2531874/how-might-i-wrap-the-findxfile-style-apis-to-the-stl-style-iterator-pattern-in-c
Jerry Coffin
@Jerry Coffin - I just tried it using `istream_iterator`. It filtered whitespace characters out of the filename.
PaulH
@PaulH:you'll (probably) want to do the reading with `istream::read`, not with something like `operator>>`. You could do `your_stream.skipws(false);`, but `read()` is probably better.
Jerry Coffin