如何为自定义istream/streambuf实现seekg()?

Gar*_*son 6 c++ xml istream streambuf seekg

十年前,我曾经是一名C++专家,但在过去的十年里,我一直在编写Java.我刚刚开始使用一个小型第三方XML解析器的C++项目.XML解析器接受STL istream.我的XML数据来自Windows COM IStream.我以为我会做正确的事情并创建一个适配器来获取IStream数据并通过istream将它呈现给XML解析器.

我按照http://www.mr-edd.co.uk/blog/beginners_guide_streambuf上的优秀教程创建了一个COMStreambuf,它从底层的COM IStream获取数据,并将其用作自定义COMIstream的缓冲区.一切看起来都不错,但我从解析器中得到一个读错误.

事实证明,解析器通过在istream上使用seekg()来读取整个文件到内存中以查找其大小,然后使用seekg()返回到开头一次读取它.不出所料,前面提到的教程决定"保存[实施寻求的指示]另一篇文章",这显然从未写过.

有人能告诉我我需要做什么来实现我的自定义istream/streambuf的seekg()吗?我会冒险自己做(我的第一个倾向是覆盖istream中的东西),但由于我在STL的深层次和我的Java心态,我担心我会做一些不完整的事情并且有一个脆弱的解决方案.(例如,在没有阅读教程的情况下,我从来没有想过通过编写新的streambuf来制作自定义istream,或者我需要使用默认语言环境调用imbue()等)

谢谢你的帮助.我对这个网站印象非常深刻 - 无论是参与者的知识还是他们友好,诚实的承认谁拥有最佳答案.:)

Dan*_*ien 3

我认为“seekg”你的意思是seekoffseekpos

实现成员seekoffseekpos成员的直接方法COMStreambuf是包装接口Seek的方法IStream。例如,这样的事情应该有效:

// 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_);
}
Run Code Online (Sandbox Code Playgroud)

在此清单中,设置位置后,streamIn我调用:

setg(eback(), egptr(), egptr());
Run Code Online (Sandbox Code Playgroud)

查找后,sputbackcsungetc将对旧数据进行操作。您可能需要考虑这对您的应用程序是否有意义并做一些不同的事情。