Как создать вектор-член фильтрации потоков? - PullRequest
1 голос
/ 15 марта 2019

Давайте начнем с простого класса чтения сжатых файлов, используя boost::iostreams:

class SingleFileOpener{
    public:
        SingleFileOpener(const std::string& filename, bool is_compressed) {
            if(is_compressed) m_stream.push(bio::zlib_decompressor());
            m_stream.push(bio::file_source{filename});
        }

        void print() {
            bio::copy(m_stream, std::cout);
        }
    private:
        using unseekable_stream = boost::iostreams::filtering_istream;
        unseekable_stream m_stream;
};

Теперь вызов SingleFileOpener("input.txt", true) с последующим print() работает правильно. Coliru Link

Я хочу расширить свой класс для чтения и манипулирования несколькими файлами аналогичным образом.Ниже приведен пример кода, который я опробовал (закомментировано в ссылке Coliru выше):

class MultiFileOpener{
    public:
        MultiFileOpener(const std::vector<std::string> filenames, std::vector<bool> is_compressed) {
            for(auto i = 0u; i < filenames.size(); i++) {
                unseekable_stream s;
                if(is_compressed[i]) s.push(bio::zlib_decompressor());
                s.push(bio::file_source{filenames[i]});
                m_stream.emplace_back(s); // <- error: use of deleted function(copy ctor)
            }
        }

        void print(int i) {
            bio::copy(*m_stream[i], std::cout);
        }
    private:
        using unseekable_stream = boost::iostreams::filtering_istream;
        std::vector<boost::optional<unseekable_stream>> m_stream;
};

Выше не компилируется из-за отсутствия конструкторов копирования в базовых классах.Я пытался использовать boost::optional, std::shared_ptr и другие альтернативы, используемые для отложенной инициализации.До сих пор единственное решение, которое сработало, это использование конструктора списка инициализатора для std::vector, то есть выполнение ctor: m_stream(filenames.size()) {...}.У меня было 2 вопроса:

  1. Почему здесь даже вызывается конструктор копирования?
  2. Возможно ли это сделать без списка инициализаторов?

1 Ответ

1 голос
/ 15 марта 2019

Почему здесь даже вызывается конструктор копирования?

Здесь:

m_stream.emplace_back(s);

Возможно ли это сделать безспособ списка инициализаторов?

Опция 1

Использовать список:

    std::list<unseekable_stream> m_stream;

Измените цикл for следующим образом:

m_stream.emplace_back();
auto& s = m_stream.back();
if(is_compressed[i]) s.push(bio::zlib_decompressor());
s.push(bio::file_source{filenames[i]});

Опция 2

Использование unique_ptr:

    std::vector<std::unique_ptr<unseekable_stream>> m_stream;

Для кода цикла:

auto stream_ptr = std::make_unique<unseekable_stream>();
... //same as above but change . to ->
m_stream.push_back(std::move(stream_ptr));

Опция 3

Инициализировать вектор с размером и не использовать push_back или emplace_back.

std::vector<unseekable_stream> m_stream;

MultiFileOpener(const std::vector<std::string>& filenames, const std::vector<bool>& is_compressed) 
 : m_stream(filenames.size())
   {
        for(auto i = 0u; i < filenames.size(); i++) {
            unseekable_stream& s = m_stream[i];
            if(is_compressed[i]) s.push(bio::zlib_decompressor());
            s.push(bio::file_source{filenames[i]});
        }
    }

При этом вы не сможете добавлять или удалять потоки позже.Если эти функции необходимы, используйте другие параметры.

...