Como conectar a serialização Boost e iostreams para serializar e compactar um objeto para string?
-
20-09-2019 - |
Pergunta
Tenho usado a biblioteca de serialização Boost, que é realmente muito boa e me permite criar wrappers simples para salvar meus objetos serializáveis em strings, assim:
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;
}
O problema é que acabei de descobrir a necessidade de compactar meus dados serializados também, então estou pensando em fazer isso com os filtros em boost::iostreams.Eu descobri como fazer isso com sucesso com arquivos:
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;
}
Mas não consigo descobrir como salvar corretamente em uma string compactada.O problema é que não estou liberando a cadeia de filtros, mas tentei abrir e sincronizar e nada parece funcionar.Aqui está meu código quebrado:
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();
}
Como resultado, alguns dados ficam presos no buffer de fluxo em algum lugar, e sempre acabo com alguns blocos completos (16K ou 32K) de dados compactados quando sei que deveriam ser 43K ou mais, dada a saída (válida) que obtenho usando meu método saveGZFile.Aparentemente, conectar o ofstream fecha e libera corretamente, mas conectar o ostringstream não.
Qualquer ajuda?(Esta é minha primeira pergunta sobre stackoverflow - ajudem-me, pessoal, vocês são minha única esperança!)
Solução
Voltando a esta questão, percebi que devo tê-la corrigido em algum momento do ano passado (já que estou usando saveGZString agora).Procurando para ver como consertei, foi bem bobo/simples:
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();
}
Basta deixar toda a cadeia sair do escopo e funciona!Organizado!Aqui está meu carregador para completar:
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;
}
Outras dicas
Eu não executei o código sozinho, mas meu melhor palpite é usar out.strict_sync()
que se aplica flush()
a cada filter
/device
no pipeline.Eu não consigo dizer, porém, se gzip_compressor
é flushable
.Se não for, então strict_sync()
retornará falso e sync()
seria mais apropriado.