Как мне реализовать seekg () для пользовательского istream / streambuf? - PullRequest
6 голосов
/ 15 августа 2010

Раньше я был экспертом по С ++ десять лет назад, но последние 10 лет я программировал на Java. Я только начал проект C ++, который использует небольшой сторонний анализатор XML. Синтаксический анализатор XML принимает поток STL. Мои данные XML поступают из Windows COM IStream. Я решил сделать все правильно и создать адаптер для получения данных IStream и представления их синтаксическому анализатору XML через istream.

Я следовал превосходному учебнику по http://www.mr -edd.co.uk / blog / beginners_guide_streambuf и создал COMStreambuf, который берет данные из базового COM IStream, и использовал его в качестве буфера для пользовательского COMIstream. Все выглядит хорошо, но я получаю ошибку чтения от парсера.

Оказывается, парсер считывает весь файл в память, используя seekg () для istream, чтобы узнать его размер, а затем возвращается к началу с seekg (), чтобы прочитать его за один раз. Неудивительно, что вышеупомянутое руководство решило «сохранить [инструкции по реализации поиска] для другого поста», который, очевидно, никогда не был написан.

Может кто-нибудь сказать мне, что мне нужно сделать, чтобы реализовать seekg () с моим собственным istream / streambuf? Я бы рискнул сделать это сам (мое первое желание было бы переопределить вещи в istream), но из-за моей неопытности в глубине STL и из-за моего менталитета на Java я боюсь сделать что-то неполное и найти хрупкое решение. (Без чтения учебников, например, я бы никогда не догадался, что можно создать собственный istream, например, написав новый streambuf, или что мне нужно было бы вызвать imbue () с локалью по умолчанию и т. Д.)

Спасибо за любую помощь. Я был очень впечатлен этим сайтом - как знанием участников, так и их дружелюбным, честным характером в признании того, кто имеет лучший ответ. :)

1 Ответ

3 голосов
/ 06 декабря 2010

Я предполагаю, что под "seekg" вы подразумеваете seekoff и seekpos.

Простой способ реализации членовseekoff и seekpos вашего 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_);
}

В этом листинге после установки позиции streamIn я звоню:

setg(eback(), egptr(), egptr());

После поиска, sputbackc илиsungetc будет работать со старыми данными.Вы можете подумать, имеет ли это смысл для вашего приложения и сделать что-то другое.

...