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

7
我在十年前是一名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()等。
感谢任何帮助。我对这个网站印象非常深刻——参与者的知识和友好、诚实的本质都令人印象深刻。 :)
1个回答

3
我假设你所说的“seekg”是指seekoffseekpos
实现COMStreambufseekoffseekpos成员的直接方法是包装IStream接口的Seek方法。例如,像这样的代码应该可以工作:
// 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_);
}

在这个列表中,在设置 streamIn 的位置之后,我调用:
setg(eback(), egptr(), egptr());

在执行查找操作之后,sputbackc或者sungetc将会对旧数据进行操作。你可能需要考虑这是否符合你的应用程序需求,并采取不同的处理方式。


gback()是什么?你是不是想说eback() - David G
@0x499602D2: 是的。谢谢你发现了这个问题。 - Daniel Trebbien
我认为你所说的“seekg”是指“seekoff”和“seekpos”。如果我想表达“seekoff”和“seekpos”,为什么要用“seekg”这个词呢?实际上,我不太确定当时具体指的是什么,因为那已经是很多年前的事情了。但我相信我并不是发明了“seekg”,而且在维基百科上也有相关的文章。 - Garret Wilson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接