sebf
sebf

Reputation: 2952

Why is this Boost filtering_stream push call ambiguous?

I have the following code

std::ostringstream compressedstream; 

// Contains the source data to compress
boost::interprocess::obufferstream bufferstream((char*)bin.data(), bin.size());

boost::iostreams::filtering_stream<boost::iostreams::input> in;
in.push(boost::iostreams::gzip_compressor());
in.push(bufferstream); // The bufferstream is the 'device'
boost::iostreams::copy(in, compressedstream);

auto compresseddata = compressedstream.str();
fileContents = &std::vector<uint8_t>(compresseddata.begin(), compresseddata.end());

Which I expect to compress one std::vector<uint8_t> into another (that's the API, using files etc is not an option).

However, this fails with the following

Error   C2668   'boost::iostreams::detail::chain_client<Chain>::push': ambiguous call to overloaded function

could be 'void boost::iostreams::detail::chain_client<Chain>::push<char,std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,std::streamsize,std::streamsize)'

or       'void boost::iostreams::detail::chain_client<Chain>::push<char,std::char_traits<char>>(std::basic_streambuf<char,std::char_traits<char>> &,std::streamsize,std::streamsize)'

I understand the error, that obufferstream is being seen as a basic_streambuf, and a basic_ostream, but don't know what to do about it. It's not like I can remove one of the superclasses!

What's the correct way to feed a bufferstream into a filtering_stream?

Upvotes: 1

Views: 88

Answers (1)

sehe
sehe

Reputation: 393709

A possible way would be to disambiguate, e.g. using

in.push(bufferstream.rdbuf());

That said, there seems to be a lot of unnecessary steps/complexity. If you wanted to implement this:

using Data = std::vector<uint8_t>;
Data compress(Data const& bin);

I'd do it without bufferstream (and the interprocess library) in the first place:

Data compress(Data const& bin) {
    std::vector<char> bout;
    bio::array_source src(reinterpret_cast<char const*>(bin.data()), bin.size());
    bio::filtering_ostreambuf dst{bio::gzip_compressor()};
    dst.push(bio::back_inserter(bout));
    copy(src, dst);
    return Data(bout.begin(), bout.end());
}

This sadly requires a copy because back_insert_device would default char_type to uint8_t for Data. That's a shame. We can "trivially" 🤔 work around it by defining an adhoc converting output "device", like so:

Data compress(Data const& bin) {
    Data bout;

    bio::array_source src(reinterpret_cast<char const*>(bin.data()), bin.size());
    bio::filtering_ostreambuf dst{bio::gzip_compressor()};

    struct inserter {
        using char_type = char;
        using category  = bio::sink_tag;
        std::streamsize write(char_type const* p, std::streamsize n) {
            _c.insert(_c.end(), p, p + n);
            return n;
        }
        Data& _c;
    };
    dst.push(inserter{bout});
    copy(src, dst);
    return bout;
}

See it Live On Coliru

#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/filter/gzip.hpp>
#include <boost/iostreams/filtering_streambuf.hpp>
#include <sstream>
#include <vector>

namespace bio = boost::iostreams;

using Data = std::vector<uint8_t>;
Data compress(Data const& bin) {
    Data bout;

    bio::array_source src(reinterpret_cast<char const*>(bin.data()), bin.size());
    bio::filtering_ostreambuf dst{bio::gzip_compressor()};

    struct inserter {
        using char_type = char;
        using category  = bio::sink_tag;
        std::streamsize write(char_type const* p, std::streamsize n) {
            _c.insert(_c.end(), p, p + n);
            return n;
        }
        Data& _c;
    };
    dst.push(inserter{bout});
    copy(src, dst);
    return bout;
}

#include <fmt/ranges.h>
int main() {
    auto compressed = compress({'H', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n'});
    fmt::print("Compressed: {::02x}\n", compressed);
}

Printing

Compressed: [1f, 8b, 08, 00, 00, 00, 00, 00, 00, ff, f3, 48, cd, c9, c9, 57, 28, cf, 2f, ca, 49, 51, e4, 02, 00, 41, e4, a9, b2, 0d, 00, 00, 00]

Disregard the GCC version on Coliru complaining that category is unused - it's actually not.

Upvotes: 1

Related Questions