How do I implement seekg() for a custom istream/streambuf?

一个人想着一个人 提交于 2019-12-07 04:07:06

问题


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 the knowledge of the participants and their friendly, honest nature in conceding who has the best answer. :)


回答1:


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.



来源:https://stackoverflow.com/questions/3488153/how-do-i-implement-seekg-for-a-custom-istream-streambuf

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!