cereal icon indicating copy to clipboard operation
cereal copied to clipboard

serialization via base ptr fails for json archive using std::ostringstream on gcc6.3

Open avibahra opened this issue 7 years ago • 2 comments

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 #include #include #include <cereal/archives/json.hpp> #include <cereal/types/memory.hpp> #include <cereal/types/vector.hpp> #include <cereal/types/set.hpp> #include <cereal/types/deque.hpp> #include <cereal/types/map.hpp> #include <cereal/types/utility.hpp>

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 #include #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()

avibahra avatar Jul 11 '18 15:07 avibahra

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'.

erichkeane avatar Jul 11 '18 15:07 erichkeane

closes: #511

avi369 avatar Jan 27 '20 14:01 avi369