How do I implement seekg() for a custom istream/streambuf?
I used to be a C++ expert a decade ago, but for the past 10 years I've been programming Java. I just started a C++ project that uses a small third-party XML parser. The XML parser accepts an STL istream. My XML data is coming from a Windows COM IStream. I thought I'd do the Right Thing and create an adapter to take the IStream data and present it to the XML parser through an istream.
I followed the excellent tutorial at http://www.mr-edd.co.uk/blog/beginners_guide_streambuf and created a COMStreambuf that takes data from the underlying COM IStream, and used it as a buffer for a custom COMIstream. Everything looks good, but I get a read error from the parser.
Turns out the parser reads the whole file into memory by using seekg() on the istream to find out its size and then goes back to the beginning with seekg() to read it in one go. Unsurprisingly, the aforementioned tutorial decided to "save [instructions on implementing seeking] for another post" which was apparently never written.
Can someone tell me what I need to do to implement seekg() with my custom istream/streambuf? I would venture out doing it myself (my first inclination would be to override stuff in istream), but with my inexperience this deep in the STL and with my Java mentality I fear I would do something incomplete and have a brittle solution. (Without reading tutorials, for example, I never would have guessed that one makes a custom istream by writing a new streambuf, for example, or that I would need to call imbue() with a default locale, etc.)
Thanks for any help. I've been very impressed with this site---both with th开发者_运维技巧e knowledge of the participants and their friendly, honest nature in conceding who has the best answer. :)
I assume that by "seekg" you mean seekoff
and seekpos
.
The straightforward way to implement members seekoff
and seekpos
of your COMStreambuf
is to wrap the Seek
method of the IStream
interface. For example, something like this should work:
// COMStreambuf.cpp
COMStreambuf::pos_type COMStreambuf::seekoff(COMStreambuf::off_type off_, std::ios_base::seekdir way_, std::ios_base::openmode which_)
{
union {
LARGE_INTEGER liMove;
ULARGE_INTEGER uliMove;
};
liMove.QuadPart = off_;
DWORD dwOrigin = STREAM_SEEK_SET;
if (way_ == std::ios_base::cur) {
dwOrigin = STREAM_SEEK_CUR;
} else if (way_ == std::ios_base::end) {
dwOrigin = STREAM_SEEK_END;
} else {
assert(way_ == std::ios_base::beg);
dwOrigin = STREAM_SEEK_SET;
uliMove.QuadPart = off_;
}
ULARGE_INTEGER uliNewPosition;
if (which_ & std::ios_base::in) {
if (which_ & std::ios_base::out)
return pos_type(off_type(-1));
HRESULT hres = streamIn->Seek(liMove, dwOrigin, &uliNewPosition);
if (hres != S_OK)
return pos_type(off_type(-1));
setg(eback(), egptr(), egptr());
} else if (which_ & std::ios_base::out) {
HRESULT hres = streamOut->Seek(liMove, dwOrigin, &uliNewPosition);
if (hres != S_OK)
return pos_type(off_type(-1));
setp(pbase(), epptr(), epptr());
} else {
return pos_type(off_type(-1));
}
return pos_type(uliNewPosition.QuadPart);
}
COMStreambuf::pos_type COMStreambuf::seekpos(COMStreambuf::pos_type sp_, std::ios_base::openmode which_)
{
return seekoff(off_type(sp_), std::ios_base::beg, which_);
}
In this listing, after setting the position of streamIn
I call:
setg(eback(), egptr(), egptr());
After a seek, sputbackc
or sungetc
will operate on old data. You may want to consider whether this makes sense for your application and do something different.
精彩评论