Как подключить сериализацию Boost и iostreams для сериализации и сжатия объекта в строку?
-
20-09-2019 - |
Вопрос
Я использую библиотеку сериализации 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();
}
В результате некоторые данные застревают где-то в буфере потока, и я всегда получаю несколько полных блоков (16 КБ или 32 КБ) сжатых данных, хотя знаю, что они должны быть 43 КБ или около того, учитывая (действительный) вывод, который я получаю от использования мой метод saveGZFile.По-видимому, подключение ofstream закрывается и сбрасывается правильно, но подключение ostringstream - нет.
Любая помощь?(Это мой первый вопрос о stackoverflow — помогите мне, ребята, вы моя единственная надежда!)
Решение
Возвращаясь к этому вопросу, я понял, что, должно быть, исправил это где-то в прошлом году (поскольку сейчас я использую 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;
}
Другие советы
Я сам не запускал код, но лучше всего использовать out.strict_sync()
что применимо flush()
каждому filter
/device
в трубопроводе.Хотя я не могу сказать, если gzip_compressor
является flushable
.Если это не так, то strict_sync()
вернет false, и sync()
было бы более уместно.