3

I am trying to compress a serialized boost archive without a temporary file. I managed to get it to work with a temporary, so I am trying to work off the code of that.

class MyClass {
public:
  int a;
  std::string b;
  template<class Archive> void serialize( Archive & arc, const unsigned int version ){
    arc & a & b;
  }
};

int main(){
  MyClass mc{ 4, "This is a somewhatishly long string." };

  std::ostringstream oss;
  boost::archive::binary_oarchive bo( oss );    
  bo << mc;

  std::ofstream ofs( "save.z", std::ios::out | std::ios::binary );
  boost::iostreams::filtering_ostreambuf fos;
  fos.push( boost::iostreams::zlib_compressor( boost::iostreams::zlib::best_compression ) );
  fos.push( bo );
  boost::iostreams::copy( fos, ofs );
}

Problem is I get two dozen template compile errors on the fos.push(bo) line.

val
  • 729
  • 6
  • 19
  • What's the 1st error? My psychic prediction is that `boost::archive::binary_oarchive` is not copyable. – Richard Critten Dec 30 '17 at 13:32
  • C3203 'type': unspecialized class template can't be used as a template argument for template parameter 'From', expected a real type. According to msvc this means something. – val Dec 30 '17 at 13:39
  • @RichardCritten except that no streams ever are so `push` doesn't copy. It's not a STL container, but a 'composite' stream – sehe Dec 30 '17 at 22:01

1 Answers1

5

I once added compression to the benchmarks in Boost C++ Serialization overhead (see the comment and Live On Coliru).

You can use them as a sample.

UPDATE

I did the leg-work. You had reversed some things. You didn't really want the stringstream anyways. (See below)

Main Program

Live On Coliru

#include <boost/serialization/serialization.hpp>
#include <string>

struct MyClass {
    int a;
    std::string b;
    template <class A> void serialize(A &ar, unsigned) { ar & a & b; }

    bool operator==(MyClass const& other) const {
        return a == other.a && b == other.b;
    }
};

#include <iostream>

static inline std::ostream& operator<<(std::ostream& os, MyClass const& obj) {
    return os << "{ a:" << obj.a << ", b:'" << obj.b << "' }";
}

void save(MyClass const& obj, std::string const& fname);
MyClass load(std::string const& fname);

int main() {
    MyClass original{ 4, "This is a somewhatishly long string." };

    save(original, "save.z");
    auto roundtrip= load("save.z");

    std::cout << "original:\t"  << original << "\n";
    std::cout << "roundtrip:\t" << roundtrip << "\n";
    std::cout << "equal? "      << std::boolalpha << (original == roundtrip) << "\n";
}

The output is

original:   { a:4, b:'This is a somewhatishly long string.' }
roundtrip:  { a:4, b:'This is a somewhatishly long string.' }
equal? true

save.z contains:

00000000: 78da 1363 8080 e2d4 a2cc c49c ccaa c492  x..c............
00000010: ccfc 3c2b abc4 a2e4 8ccc b254 0106 160e  ..<+.......T....
00000020: 160e 46a8 1a06 1620 5681 b243 3232 8b15  ..F.... V..C22..
00000030: 8028 51a1 383f 37b5 3c03 a8b1 3823 a752  .(Q.8?7.<...8#.R
00000040: 2127 3f2f 5da1 b8a4 2833 2f5d 0f00 a29a  !'?/]...(3/]....
00000050: 16b7                                     ..

Saving Code

Note the order in which the streams/archive get closed/flushed/destructed. The safe order is:

  • construct fstream
  • construct compressor / filtering stream
  • construct archive on filtering stream
  • flush/close/destruct archive
  • flush/close/destruct filtering stream and compressor
  • flush/close/destruct the fstream
#include <boost/archive/binary_oarchive.hpp>
#include <boost/iostreams/filter/zlib.hpp>
#include <boost/iostreams/filtering_streambuf.hpp>

#include <sstream>
#include <fstream>

void save(MyClass const& obj, std::string const& fname) {
    std::ofstream ofs(fname, std::ios::binary);
    {
        boost::iostreams::filtering_ostreambuf fos;

        // push the ofstream and the compressor
        fos.push(boost::iostreams::zlib_compressor(boost::iostreams::zlib::best_compression));
        fos.push(ofs);

        // start the archive on the filtering buffer:
        boost::archive::binary_oarchive bo(fos);
        bo << obj;
    }
}

Loading Code

Basically the inverse:

#include <boost/archive/binary_iarchive.hpp>
MyClass load(std::string const& fname) {
    std::ifstream ifs(fname, std::ios::binary);
    {
        boost::iostreams::filtering_istreambuf fis;

        // push the ifstream and the decompressor
        fis.push(boost::iostreams::zlib_decompressor());
        fis.push(ifs);

        // start the archive on the filtering buffer:
        boost::archive::binary_iarchive bi(fis);
        MyClass obj;
        bi >> obj;

        return obj;
    }
}
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Added a complete working answer to your specific question sample in the update, see **[Live On Coliru](http://coliru.stacked-crooked.com/a/c8572dbae5035708)** – sehe Dec 30 '17 at 21:57
  • Thanks for the legwork, i managed to make the code run using boost:asio, but that version didn't compress for some reason (probably ordering issue), but this works perfectly. – val Dec 31 '17 at 00:36