Как подключить сериализацию Boost и iostreams для сериализации и gzip объекта в строку? - PullRequest
25 голосов
/ 18 ноября 2009

Я использую библиотеку сериализации 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 или около того, учитывая (действительный) вывод I получить от использования моего метода saveGZFile. По-видимому, подключение ofstream закрывает и очищает должным образом, но подключение ostringstream - нет.

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

Ответы [ 2 ]

19 голосов
/ 27 февраля 2011

Возвращаясь к этому вопросу, я понял, что, должно быть, исправил это где-то в прошлом году (так как сейчас я использую 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;
}
1 голос
/ 20 ноября 2009

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

...