简体   繁体   中英

How to create a member vector of filtering streams?

Let's start with a simple compressed file reader class using 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;
};

Now calling SingleFileOpener("input.txt", true) followed by print() works correctly.Coliru Link

I want to extend my class to read and manipulate multiple files in a similar manner. Below is the sample code I tried out(commented out in the Coliru link above too):

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;
};

The above doesnt compile due to missing copy constructors in base classes. I've tried using boost::optional , std::shared_ptr and miscellaneous alternatives used for delayed initialization. Uptil now the only solution that has worked is to use an initializer list constructor for the std::vector , ie doing ctor: m_stream(filenames.size()) {...} . I had 2 questions:

  1. Why is a copy constructor even being called here?
  2. Is it possible to do this without the initializer list way?

Why is a copy constructor even being called here?

Here:

m_stream.emplace_back(s);

Is it possible to do this without the initializer list way?

Option 1

Use a list:

    std::list<unseekable_stream> m_stream;

Change the for loop as follows:

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]});

Option 2

Use unique_ptr :

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

For loop code:

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

Option 3

Initialize vector with size and not use push_back or 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]});
        }
    }

With this, you cannot add or remove streams later. If those features are needed, use the other options.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM