Инкапсулируйте доступ к данным, используя собственный istream_iterator - PullRequest
1 голос
/ 21 апреля 2019

В настоящее время у меня есть код, который по существу содержит эти команды:

std::string line;
std::getline(ifs, line);

ifs - это немного std::ifstream. Код работает.

Теперь я хотел бы использовать то же самое для данных, которые я распаковываю из другого источника. По сути, я читал порциями, которые я где-то буферизировал, а затем извлекал данные из этого буфера, используя функцию, которая немного похожа на fgetc. Это все работает, но это плохая смесь не STL с кодом STL.

Я хотел бы инкапсулировать декомпрессию и предоставить итератор, чтобы вызов std::getline работал. Я думаю, это будет istream_iterator? Какой минимум должен содержать этот класс?

ОБНОВЛЕНИЕ : На основании полученных комментариев и реализации streambuf на http://www.voidcn.com/article/p-vjnlygmc-gy.html, Теперь у меня есть следующий код, который работает. В качестве тестового примера я использовал сжатие zlib. Код не очень хороший, и я ценю любые советы по его улучшению.

class gzip_streambuf: public std::streambuf
{
private: 
    gzip_streambuf(const gzip_streambuf &);      
    gzip_streambuf &operator= (const gzip_streambuf &);
public:
    explicit gzip_streambuf(gzFile h_, std::size_t buf_sz_ = 1024, std::size_t putback_sz_ = 2) :
        putback_sz(std::max(putback_sz_, std::size_t(1))),
        buffer(std::max(putback_sz_, buf_sz_ ) + putback_sz_),
        h(h_)
    {
        char *end = &buffer.front() + buffer.size();        
        setg(end, end, end);
    }
    ~gzip_streambuf() {}    
    bool error(void)
    {
        return gzerr;
    }

private:    
    int_type underflow()
    {
        if (gptr() < egptr())
            return traits_type::to_int_type(*gptr());
        char *base = &buffer.front();
        char *start = base;
        if (eback() == base) {
            std::memmove(base, egptr() - putback_sz, putback_sz); 
            start += putback_sz;
        }
        int n = gzread(h, start, buffer.size() - (start - base));
        if ( n < 0 )
            gzerr = true;                        
        if ( n <= 0)
            return traits_type::eof();
        setg(base, start, start + n);
        return traits_type::to_int_type(*gptr());
    }

private:
    bool gzerr = false;    
    gzFile h = nullptr;
    const std::size_t putback_sz;
    std::vector<char> buffer;
};


class gzip_istream: public std::istream
{    
public:
    gzip_istream() {};
    ~gzip_istream() { delete gzbuf; };

private:    
    gzip_istream(const gzip_istream  &);      
    gzip_istream &operator= (const gzip_istream &);

public:    
    void open( const char * filename) {
        if ( h = gzopen(filename, "rb") ) {
            delete gzbuf;
            if ( gzbuf = new gzip_streambuf(h) ) {
                set_rdbuf(gzbuf);
                clear();
            } else
                close();            
        } 
    }    
    void close() {
        if (h)
            gzclose( std::exchange(h, nullptr) );
        delete std::exchange(gzbuf, nullptr);
        set_rdbuf(nullptr);
    }        
    bool is_open(void) {
        return h != nullptr;
    }    
private:
    gzFile h = nullptr;
    gzip_streambuf *gzbuf = nullptr;
};

Может использоваться следующим образом:

std::string line;
gzip_istream gin;
gin.open("file.gz");         
if (gin.good()) {    
    while (std::getline( gin, line)) 
        std::cout << line << std::endl;
    gin.close();
}

Я пытался использовать std::unique_ptr для gzip_streambuf члена в gzip_istream, но все попытки не увенчались успехом из-за отсутствия конструктора копирования. Я хотел бы избавиться от нового / удалить материал, но я не уверен, как.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...