ok i'm running problem in c++11 cereal (http://uscilab.github.io/cereal/).
in abstract sense have large graph serializing lots of shared pointers connecting edges , vertices. edges (and vertices) have attributes attached them.
now 1 of these attributes (base class) account (child class). account inherits idable serializable. here pertinent snips of code show of cereal usage. i'll explain issue after context:
attribute.hpp/cpp
class attribute { ... template<class archive> void serialize(archive&) { } friend class cereal::access; ... cereal_register_type(mgraph::attribute)
idable.hpp/cpp
class idable { ... id id; template<class archive> void serialize(archive& archive) { archive(cereal::make_nvp("id", id)); } template<class archive> static void load_and_construct(archive& ar, cereal::construct<mcommon::idable>& construct) { mcommon::id id; ar(id); construct(id); } friend class cereal::access; ... cereal_register_type(mcommon::idable)
position.hpp/cpp
class position : public mgraph::attribute , public mcommon::displayable { template<class archive> void serialize(archive& archive) { archive(cereal::make_nvp("attribute", cereal::base_class<mgraph::attribute>(this))); } friend class cereal::access; ... cereal_register_type(mfin::position)
account.hpp/cpp
class account : public mcommon::idable , public position { ... currency balance; template<class archive> void serialize(archive& archive) { archive(cereal::make_nvp("idable", cereal::base_class<mcommon::idable>(this)), cereal::make_nvp("position", cereal::base_class<mfin::position>(this)), cereal::make_nvp("balance", balance)); } template<class archive> static void load_and_construct(archive& ar, cereal::construct<account>& construct) { mcommon::id iden; currency::code code; ar(iden, code); construct(iden, code); } friend class cereal::access; ... cereal_register_type(mfin::account)
so problem comes when mfin::account being serialized. mfin::account belongs std::list>. when down serialize function idable object invalid.
going gdb halts on segfault go few stackframes this line: /usr/include/cereal/types/polymorphic.hpp:341. is:
(gdb) list 336 337 auto binding = bindingmap.find(std::type_index(ptrinfo)); 338 if(binding == bindingmap.end()) 339 unregistered_polymorphic_exception(save, cereal::util::demangle(ptrinfo.name())) 340 341 binding->second.shared_ptr(&ar, ptr.get()); 342 } 343 344 //! loading std::shared_ptr polymorphic types 345 template <class archive, class t> inline
now here ptr is:
(gdb) print *((mfin::account*)(ptr.get())) $10 = {<mcommon::idable> = {_vptr.idable = 0x4f0d50 <vtable mfin::account+16>, id = "bank"}, <mfin::position> = {<mgraph::attribute> = { _vptr.attribute = 0x4f0d78 <vtable mfin::account+56>}, <mcommon::displayable> = {_vptr.displayable = 0x4f0da0 <vtable mfin::account+96>}, <no data fields>}, balance = {<mcommon::displayable> = { _vptr.displayable = 0x4f0570 <vtable mfin::currency+16>}, amount = 0, code = mfin::currency::usd}} (gdb) print ptr $11 = std::shared_ptr (count 3, weak 0) 0x758ad0
everything looking good. notice when cast void*:
$11 = std::shared_ptr (count 3, weak 0) 0x758ad0 (gdb) print *((mfin::account*)((void*)ptr.get())) $12 = {<mcommon::idable> = {_vptr.idable = 0x4f0d78 <vtable mfin::account+56>, id = "\363al\000\000\000\000\000pbl\000\000\000\000\000\304\031l\000\000\000\000\000\021#l", '\000' <repeats 13 times>, " \232n", '\000' <repeats 21 times>, "p\251@\000\000\000\000\000\370\377\377\377\377\377\377\377 \232n", '\000' <repeats 21 times>, "\304\031l\000\000\000\000\000p\251@", '\000' <repeats 45 times>, "st19_sp_counted_deleteripn4mfin7accounte"...}, <mfin::position> = {<mgraph::attribute> = { _vptr.attribute = 0x4f0570 <vtable mfin::currency+16>}, <mcommon::displayable> = {_vptr.displayable = 0x0}, <no data fields>}, balance = {<mcommon::displayable> = {_vptr.displayable = 0x0}, amount = 49, code = (unknown: 7702648)}}
this of course happens in binding->second.shared_ptr (seen below) takes const void*.
(gdb) list 295 writemetadata(ar); 296 297 #ifdef _msc_ver 298 savepolymorphicsharedptr( ar, dptr, ::cereal::traits::has_shared_from_this<t>::type() ); // msvc doesn't typename here 299 #else // not _msc_ver 300 savepolymorphicsharedptr( ar, dptr, typename ::cereal::traits::has_shared_from_this<t>::type() ); 301 #endif // _msc_ver 302 }; 303 304 serializers.unique_ptr =
what wrong in usage of cereal cause this? here final error get:
program received signal sigsegv, segmentation fault. 0x000000000040f7cd in rapidjson::writer<rapidjson::genericwritestream, rapidjson::utf8<char>, rapidjson::memorypoolallocator<rapidjson::crtallocator> >::writestring (this=0x7fffffffd358, str=0x4f1ae0 <vtable mfin::account+96> "\363al", length=4989722) @ /usr/include/cereal/external/rapidjson/writer.h:276 276 if ((sizeof(ch) == 1 || characterok(*p)) && escape[(unsigned char)*p]) { missing separate debuginfos, use: debuginfo-install boost-date-time-1.55.0-8.fc21.x86_64 boost-filesystem-1.55.0-8.fc21.x86_64 boost-program-options-1.55.0-8.fc21.x86_64 boost-system-1.55.0-8.fc21.x86_64 boost-thread-1.55.0-8.fc21.x86_64 fcgi-2.4.0-24.fc21.x86_64 glog-0.3.3-3.128tech.x86_64 libgcc-4.9.2-1.fc21.x86_64 libstdc++-4.9.2-1.fc21.x86_64
ok after investigation believe have answer problem. , believe bug in library. after have confirmed library owners ensure date results there.
i have produced simple program below demonstrates problem. issue stems multiple inheritance, polymorphism, , casting. in program below notice create derived object. derived object when laid out in memory have format approximately.:
derived: base2::vtable base2::var base::vtable
consider:
(gdb) print ptr $2 = std::shared_ptr (count 1, weak 0) 0x63c580 (gdb) print *ptr $3 = (derived &) @0x63c580: {<base2> = {_vptr.base2 = 0x421f90 <vtable derived+16>, var = ""}, <base> = {_vptr.base = 0x421fa8 <vtable derived+40>}, <no data fields>}
now when dynamic_pointer_cast base have:
(gdb) print ptr $8 = std::shared_ptr (count 2, weak 0) 0x63c590 (gdb) print *ptr $9 = (base &) @0x63c590: {_vptr.base = 0x421fa8 <vtable derived+40>}
this problem begins. on /usr/include/cereal/types/polymorphic.hpp, line 341. have ptr base. here have:
binding->second.shared_ptr(&ar, ptr.get());
which ends being cast const void*. later on based on type info cast type registered polymorphic type. since shared_ptr points object of derived type means derived*. seen below:
272 static inline void savepolymorphicsharedptr( archive & ar, void const * dptr, std::false_type /* has_shared_from_this */ ) 273 { 274 polymorphicsharedpointerwrapper psptr( dptr ); 275 ar( cereal_nvp_("ptr_wrapper", memory_detail::make_ptr_wrapper( psptr() ) ) ); 276 }
now means down stack ptr base* cast void* , cast derived*. , cast chain results in invalid object. seen below ptr invalid now:
(gdb) print *ptr $7 = (const derived &) @0x63c590: {<base2> = {_vptr.base2 = 0x421fa8 <vtable derived+40>, var = <error reading variable: cannot access memory @ address 0x49>}, <base> = {_vptr.base = 0x0}, <no data fields>}
the pointer pointing vtable base , not derived/base2 should program crashes:
{ "ptr": { "polymorphic_id": 2147483649, "polymorphic_name": "derived", "ptr_wrapper": { "id": 2147483649, "data": { "base2": { program received signal sigsegv, segmentation fault. 0x00007ffff7b8e9e3 in std::string::size() const () /lib64/libstdc++.so.6
below sample program reproduces this:
// g++ test.cpp -std=c++11 -ggdb -o test && gdb ./test #include <cereal/archives/json.hpp> #include <cereal/types/polymorphic.hpp> #include <iostream> struct base { virtual void foo() { } template<class archive> void serialize(archive& archive) { } }; struct base2 { virtual void foo() { } std::string var; template<class archive> void serialize(archive& archive) { archive(cereal::make_nvp("var", var)); } }; struct derived : public base2, public base { template<class archive> void serialize(archive& archive) { archive(cereal::make_nvp("base2", cereal::base_class<base2>(this)), cereal::make_nvp("base", cereal::base_class<base>(this))); } }; cereal_register_type(base); cereal_register_type(base2); cereal_register_type(derived); int main() { auto ptr = std::make_shared<derived>(); cereal::jsonoutputarchive ar(std::cout); ar(cereal::make_nvp("ptr", std::dynamic_pointer_cast<base>(ptr))); return 0; }