cereal
cereal copied to clipboard
serialization via base ptr fails for json archive using std::ostringstream on gcc6.3
I am testing cereal(version 1.2.2) to see if it can replace boost.
But cereal appear to have a very serious error, where saving/restore a simple polymorphic type via a shared ptrs. This works when saving and restoring via files???
The same test fails to save/restore a type via string See below. The test was run with gcc 6.3(where c++14 is now the default)
There are two file:
-
serealization.hpp
-
TestCerealWithHierarchy.cpp - this uses boost test
When the test is run, it can be seen that serializing via string forgets a trailing '}'
{ "12CmdContainer": { "cmd_": { "polymorphic_id": 2147483649, "polymorphic_name": "Derived1", "ptr_wrapper": { "id": 2147483649, "data": { "cereal_class_version": 0, "value0": { "cereal_class_version": 0 }, "value1": 10 } } } }
/////////////////////////////////////////////////////////////////////// // file: serealization.hpp
#ifndef SERIALIZATION_HPP_ #define SERIALIZATION_HPP_
#include
namespace ecf {
template< typename T > void save(const std::string& fileName, const T& t) { std::ofstream os(fileName); cereal::JSONOutputArchive oarchive(os); // Create an output archive oarchive(cereal::make_nvp(typeid(t).name(),t) ); // Write the data to the archive }
template< typename T > void restore(const std::string& fileName, T& restored) { std::ifstream is(fileName); cereal::JSONInputArchive iarchive(is); // Create an input archive iarchive(restored); // Read the data from the archive }
template< typename T > void save_as_string(std::string& outbound_data, const T& t) { std::ostringstream archive_stream; cereal::JSONOutputArchive oarchive(archive_stream); // Create an output archive oarchive(cereal::make_nvp(typeid(t).name(),t) ); // Write the data to the archive outbound_data = archive_stream.str(); }
template< typename T > void restore_from_string(const std::string& archive_data, T& restored) { std::istringstream archive_stream(archive_data); cereal::JSONInputArchive iarchive(archive_stream); // Create an input archive iarchive(restored); // Read the data from the archive }
}
#endif
///////////////////////////////////////////////////////////////////////
// file: TestCerealWithHierarchy.cpp
#include
#include <boost/test/unit_test.hpp> #include "boost/filesystem/operations.hpp" #include "boost/filesystem/path.hpp"
#include "Serialization.hpp"
using namespace ecf; using namespace boost; using namespace std; namespace fs = boost::filesystem;
// ======================================================================================
class BaseCmd { public: BaseCmd() {} virtual ~BaseCmd() {}
bool operator==(const BaseCmd & rhs) const { return true;} void print(std::ostream &os) const {} virtual bool equals(BaseCmd* rhs) const = 0; private: friend class cereal::access; template<class Archive> void serialize(Archive & archive, std::uint32_t const version) {} };
class Derived1 : public BaseCmd { public: Derived1() : x_(0) {} Derived1(int x) : BaseCmd(), x_(x) {} virtual ~Derived1() {}
int get_x() const { return x_;}
virtual bool equals(BaseCmd* rhs ) const { Derived1* the_rhs = dynamic_cast<Derived1*>(rhs); if (!the_rhs) return false; if (x_ != the_rhs->get_x()) return false; return true; }
void print(std::ostream &os) const { os << "Derived1:"; BaseCmd::print(os); os << ": x("<< x_ << ")"; } private: int x_;
friend class cereal::access; template<class Archive> void serialize(Archive & ar, std::uint32_t const version) { ar(cereal::base_class<BaseCmd>( this ), x_); } };
std::ostream& operator<<(std::ostream &os, Derived1 const &m) { m.print(os); return os; }
class CmdContainer { public: CmdContainer() {} CmdContainer(std::shared_ptr<BaseCmd> cmd) : cmd_(cmd) {}
bool operator==(const CmdContainer& rhs) const { if (!cmd_.get() && !rhs.cmd_.get()) return true; if (cmd_.get() && !rhs.cmd_.get()) return false; if (!cmd_.get() && rhs.cmd_.get()) return false; return (cmd_->equals(rhs.cmd_.get())); }
void print(std::ostream &os) const { if (cmd_.get()) cmd_->print(os); os << "NULL request"; }
private: std::shared_ptr<BaseCmd> cmd_;
friend class cereal::access; template<class Archive> void serialize(Archive & archive) { archive(CEREAL_NVP(cmd_) ); } };
std::ostream& operator<<(std::ostream &os, CmdContainer const &m) { m.print(os); return os; }
CEREAL_REGISTER_TYPE(Derived1);
// =============================================================================
BOOST_AUTO_TEST_SUITE( CoreTestSuite )
BOOST_AUTO_TEST_CASE( test_cereal_save_as_string_and_save_as_filename ) { cout << "ACore:: ...test_cereal_save_as_string_and_save_as_filename\n" ;
std::shared_ptr<BaseCmd> cmd = std::make_shared<Derived1>(10); CmdContainer originalCmd(cmd); std::string saved_cmd_as_string;
// SAVE as string and file { BOOST_REQUIRE_NO_THROW(ecf::save("ACore.txt",originalCmd )); // save as filename ecf::save_as_string(saved_cmd_as_string,originalCmd ); // save as string, this is buggy forgets trailing '}' }
// RESTORE from filename and string { CmdContainer restoredCmd; BOOST_REQUIRE_NO_THROW(ecf::restore("ACore.txt", restoredCmd)); // restore from filename BOOST_REQUIRE_MESSAGE(restoredCmd == originalCmd, "restoredCmd " << restoredCmd << " originalCmd " << originalCmd ); } { cout << saved_cmd_as_string << "\n"; CmdContainer restoredCmd ; ecf::restore_from_string(saved_cmd_as_string , restoredCmd ); // restore form string fails, due to missing '}' BOOST_REQUIRE_MESSAGE(restoredCmd == originalCmd, "restoredCmd " << restoredCmd << " originalCmd " << originalCmd ); }
fs::remove("ACore.txt"); }
BOOST_AUTO_TEST_SUITE_END()
This isn't a cereal bug, this is an error writing save_as_string below. See the documention under "Archives Basics" here: https://uscilab.github.io/cereal/serialization_archives.html
IN particular, see the part labeled 'Important'.
closes: #511