Как подключить Boost-сериализацию и iostreams для сериализации и сжатия объекта в строку?

Я использовал библиотеку сериализации Boost, которая на самом деле довольно хороша и позволяет мне создавать простые оболочки для сохранения моих сериализуемых объектов в строки, например:

namespace bar = boost::archive;
namespace bio = boost::iostreams;

template <class T> inline std::string saveString(const T & o) {
 std::ostringstream oss;
 bar::binary_oarchive oa(oss);
 oa << o;
 return oss.str();
}
template <class T> inline void saveFile(const T & o, const char* fname) {
 std::ofstream ofs(fname, std::ios::out|std::ios::binary|std::ios::trunc);
 bar::binary_oarchive oa(ofs);
 oa << o;
}
template <class T> inline void loadFile(T & o, const char* fname) {
 std::ifstream ifs(fname, std::ios::in|std::ios::binary);
 assert(ifs.good()); // XXX catch if file not found
 bar::binary_iarchive ia(ifs);
 ia >> o;
}

Дело в том, что я только что обнаружил необходимость сжимать и мои сериализованные данные, поэтому я собираюсь сделать это с помощью фильтров в boost :: iostreams. Разобрался, как это успешно делать с файлами:

template <class T> inline void saveGZFile(const T & o, const char* fname) {
 std::ofstream ofs(fname, std::ios::out|std::ios::binary|std::ios::trunc);
 bio::filtering_streambuf<bio::output> out;
 out.push(boost::iostreams::gzip_compressor());
 out.push(ofs);
 bar::binary_oarchive oa(out);
 oa << o;
}
template <class T> inline void loadGZFile(T & o, const char* fname) {
 std::ifstream ifs(fname, std::ios::in|std::ios::binary);
 assert(ifs.good()); // XXX catch if file not found
 bio::filtering_streambuf<bio::input> in;
 in.push(bio::gzip_decompressor());
 in.push(ifs);
 bar::binary_iarchive ia(in);
 ia >> o;
}

Но не могу понять, как правильно сохранить в сжатую строку. Проблема в том, что я не промываю цепочку фильтров, но я пробовал запускать и синхронизировать, но ничего не работает. Вот мой неработающий код:

template <class T> inline std::string saveGZString(const T & o) {
 std::ostringstream oss;
 bio::filtering_streambuf<bio::output> out;
 out.push(bio::gzip_compressor());
 out.push(oss);
 bar::binary_oarchive oa(out);
 oa << o;
 // XXX out.pop() twice?  out.strict_sync()??  oss.flush()??
 return oss.str();
}

В результате некоторые данные застревают где-то в буфере потока, и я всегда получаю несколько полных блоков (16K или 32K) сжатых данных, когда я знаю, что это должно быть 43K или около того, учитывая (действительный) вывод, который я получаю от использования мой метод saveGZFile. По-видимому, подключение ofstream закрывается и очищается должным образом, но подключение ostringstream - нет.

Любая помощь? (Это мой первый вопрос о stackoverflow - помогите мне, ребята, вы моя единственная надежда!)


person cce    schedule 18.11.2009    source источник


Ответы (2)


Возвращаясь к этому вопросу, я понял, что, должно быть, исправил это где-то в прошлом году (поскольку сейчас я использую saveGZString). Покопавшись, чтобы увидеть, как я это исправил, это было довольно глупо / просто:

namespace bar = boost::archive;
namespace bio = boost::iostreams;

template <typename T> inline std::string saveGZString(const T & o) {
        std::ostringstream oss;
        { 
                bio::filtering_stream<bio::output> f;
                f.push(bio::gzip_compressor());
                f.push(oss);
                bar::binary_oarchive oa(f);
                oa << o;
        } // gzip_compressor flushes when f goes out of scope
        return oss.str();
}

Просто позвольте всей цепочке выйти за рамки, и она заработает! Аккуратный! Вот мой загрузчик для полноты:

template <typename T> inline void loadGZString(T & o, const std::string& s) {
        std::istringstream iss(s);
        bio::filtering_stream<bio::input> f;
        f.push(bio::gzip_decompressor());
        f.push(iss);
        bar::binary_iarchive ia(f);
        ia >> o;
}
person cce    schedule 27.02.2011
comment
Вы можете избежать трюка с ограничением области видимости с помощью вызова flush (). f.flush () - person Marios V; 01.11.2011
comment
в нерабочем коде моего вопроса в комментарии написано oss.flush()??, потому что вызов flush() на ostringstream не работал. трюк с ограничением объема - единственное, что у меня сработало. если вы не имеете в виду, что я должен сбрасывать f, у которого нет метода очистки (у него есть метод strict_sync(), который должен вызывать flush() на каждом устройстве в конвейере, что я также пробовал, но безрезультатно). - person cce; 07.12.2011
comment
Спасибо за это - я столкнулся с той же проблемой. Нет доступного флеша, и strict_sync не подействовал. - person erikreed; 07.02.2013

Я сам не запускал код, но лучше всего использую out.strict_sync(), который применяется flush() к каждому _3 _ / _ 4_ в конвейере. Однако я не могу сказать, gzip_compressor равно flushable. В противном случае strict_sync() вернет false, и sync() будет более подходящим.

person rcollyer    schedule 20.11.2009
comment
Внутри он ровный. Но он применяется к каждому фильтру / устройству в цепочке. - person rcollyer; 04.12.2009
comment
насколько я помню (давным-давно), я пробовал strict_sync(), и это не сработало, как и другие вещи в разочарованной строке // XXX ?? в моем вопросе ... может быть, это работает в последней версии Boost, кто знает. - person cce; 27.02.2011
comment
Компрессоры нельзя промывать. Таким образом, если в цепочке существует компрессор, strict_sync() всегда возвращает false. Удивительно, но получение компрессора через auto* ptr = f.component<bio::gzip_compressor>(0) с последующим вызовом boost::iostreams::flush(*p) возвращает true, но не оказывает никакого эффекта. (Это то, что я имел в виду, говоря, что нельзя смывать.) - person mavam; 03.03.2012