Boost C++ Libraries: Ticket #5340: Patch to support concrete base classes becomming abstract classes https://svn.boost.org/trac10/ticket/5340 <p> If you are serializing a pointer to a polymorphic object as a pointer to its base class, and that base class is concrete, it will fail to load if that base class later becomes abstract. That is because the serializer for the base class is automatically registered if it is concrete, but not if it is abstract, making a mismatch in the class id for the object. I have attached a patch that detects this case and registers the serializer for the abstract class if it occurs. </p> <p> This patch contains multiple pieces. Fist, basic_iarchive.cpp is patched in load_pointer() to detect that mismatch in class ids and register the serializer. Second, iserializer.hpp is patched so that register_type() for abstract classes returns the proper pointer serializer. (though it isn't directly registered) Third, pointer_iserializer::load_object_pointer() now uses templates to split into two implementations: one for abstract and one for non-abstract classes. The abstract implementation throws an archive_exception, to which an abstract_class_error field has been added. </p> <p> This patch does not handle going from an abstract base class to a concrete base class, as that would break backwards compatibility. However, it should be more common to go from concrete to abstract than the other way around, and having this support can solve a lot of debugging headaches when it comes up. (headaches that prompted me adding this support) </p> <p> I have tried to keep consistent with your coding style and techniques to make it as seamless to patch as possible. </p> en-us Boost C++ Libraries /htdocs/site/boost.png https://svn.boost.org/trac10/ticket/5340 Trac 1.4.3 Aaron Barany <akb825@…> Sat, 19 Mar 2011 00:26:55 GMT attachment set https://svn.boost.org/trac10/ticket/5340 https://svn.boost.org/trac10/ticket/5340 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">concrete to abstract patch.zip</span> </li> </ul> <p> Zip file containing the patches. </p> Ticket Bryce Adelstein Lelbach Wed, 11 May 2011 02:37:57 GMT version, severity changed https://svn.boost.org/trac10/ticket/5340#comment:1 https://svn.boost.org/trac10/ticket/5340#comment:1 <ul> <li><strong>version</strong> <span class="trac-field-old">Boost 1.46.0</span> → <span class="trac-field-new">Boost 1.47.0</span> </li> <li><strong>severity</strong> <span class="trac-field-old">Problem</span> → <span class="trac-field-new">Showstopper</span> </li> </ul> <p> Ramey, is there any reason not to apply this? I am willing to apply it and take responsibility for this. </p> Ticket Aaron Barany <akb825@…> Wed, 11 May 2011 03:44:47 GMT <link>https://svn.boost.org/trac10/ticket/5340#comment:2 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/5340#comment:2</guid> <description> <p> There is one issue, which I mentioned in <a class="reopened ticket" href="https://svn.boost.org/trac10/ticket/5341" title="#5341: Patches: Patch to improve shared library behavior with serialization (reopened)">#5341</a>. It will currently fail to compile if you are serializing into a pointer to an abstract class that doesn't have a serialize function. The case this was failing was in the shared_ptr_132 serialization when it reads the class that holds the reference count. </p> <p> The safest way to fix this would be to find the best place to cut off the chain of template instantiations which references the potentially non-existent serialize functions for abstract classes. I'm pretty sure I know the best place, I just need to find the time to implement and test it. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>anonymous</dc:creator> <pubDate>Wed, 11 May 2011 05:36:08 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/5340#comment:3 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/5340#comment:3</guid> <description> <p> This last one is easy. </p> <p> The base class serialization has two functions. First it invokes void_cast_register to maintain the list hierarchy of base/derived classes and second, it invokes the serialization of the base class. One can invoke void_cast_register directly so the chain of serialization to base classes is broken. At least one of the tests does this for some reason that I don't remember. Actually I do believe it was related to shared_ptr serialization. </p> <p> Robert Ramey </p> </description> <category>Ticket</category> </item> <item> <author>Aaron Barany <akb825@…></author> <pubDate>Wed, 11 May 2011 07:08:19 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/5340#comment:4 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/5340#comment:4</guid> <description> <p> This isn't when a serialize function is calling serialize on its base class, this is when serializing into a pointer of a polymorphic type. For example: </p> <div class="wiki-code"><div class="code"><pre><span class="n">MyClass</span><span class="o">*</span> <span class="n">c</span><span class="p">;</span> <span class="n">a</span> <span class="o">&gt;&gt;</span> <span class="n">c</span><span class="p">;</span> </pre></div></div><p> The problem isn't that it's trying to call serialize, the problem is templates are being instantiated that reference the serialize function. Before, it was never instantiating pointer_iserializer&lt;<a class="missing wiki">MyClass</a>&gt; if Myclass was abstract, but that would cause mismatches if <a class="missing wiki">MyClass</a> started as concrete, then later became abstract. Now I am always instantiating pointer_iserializer&lt;<a class="missing wiki">MyClass</a>&gt;, and when the type registration mismatch occurs on load it is registered as if it were concrete. </p> <p> The exact template instantiation chain is: pointer_iserializer&lt;<a class="missing wiki">MyClass</a>&gt; instantiates iserializer&lt;<a class="missing wiki">MyClass</a>&gt;, which uses access to call serialize on <a class="missing wiki">MyClass</a>. Even if it's never actually executing the code that calls serialize on <a class="missing wiki">MyClass</a>, the compiler still expects it to be there. </p> <p> After thinking about it a little more and looking at where it registers the serializer, it's actually not as easy to cut off the reference to serialize as I thought. I thought I could use stub out the iserializer&lt;<a class="missing wiki">MyClass</a>&gt; instantiation in pointer_iserializer&lt;<a class="missing wiki">MyClass</a>&gt; if <a class="missing wiki">MyClass</a> is abstract, but that won't work if <a class="missing wiki">MyClass</a> is serialized anywhere else. (such as from a subclass' serialize function) What I will need to do is stub out the iserializer&lt;<a class="missing wiki">MyClass</a>&gt; instantiation if <a class="missing wiki">MyClass</a> doesn't have a serialize function. Fortunately, such a check is possible. (<a class="ext-link" href="http://groups.google.com/group/comp.lang.c++/msg/1a3b6e58852ef3af"><span class="icon">​</span>http://groups.google.com/group/comp.lang.c++/msg/1a3b6e58852ef3af</a>) </p> <p> This also brings up another interesting case: if <a class="missing wiki">MyClass</a> in my example above is actually a concrete base class without a serialize function, it will also fail to compile even without my patches applied. With a mechanism in place to detect if a serialize function exists, I can have it so serializing <a class="missing wiki">MyClass</a> will compile if <a class="missing wiki">MyClass</a> doesn't have a serialize function, regardless of whether <a class="missing wiki">MyClass</a> is abstract or not, as long as it is polymorphic. </p> </description> <category>Ticket</category> </item> <item> <author>Aaron Barany <akb825@…></author> <pubDate>Fri, 20 May 2011 21:19:10 GMT</pubDate> <title>attachment set https://svn.boost.org/trac10/ticket/5340 https://svn.boost.org/trac10/ticket/5340 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">issue 5340 patch v2.zip</span> </li> </ul> <p> Updated patch. </p> Ticket Aaron Barany <akb825@…> Fri, 20 May 2011 21:19:45 GMT attachment set https://svn.boost.org/trac10/ticket/5340 https://svn.boost.org/trac10/ticket/5340 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">issue 5340 and 5341 patch v2.zip</span> </li> </ul> <p> Combined patch for this issue and <a class="reopened ticket" href="https://svn.boost.org/trac10/ticket/5341" title="#5341: Patches: Patch to improve shared library behavior with serialization (reopened)">#5341</a> </p> Ticket Aaron Barany <akb825@…> Fri, 20 May 2011 22:14:30 GMT <link>https://svn.boost.org/trac10/ticket/5340#comment:5 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/5340#comment:5</guid> <description> <p> I have submitted an updated patch, which resolves the compile issues with abstract classes. As mentioned earlier, this also removes the need for the base class being passed into the archive with operator &amp;, &lt;&lt;, or &gt;&gt; to have serialization functions as well, as long as the instance has the proper functions. </p> <p> I achieved this by using the member function query trick described in my last post to stub out the call to the serialize member function in access. It was necessary for me to do it there for two reasons: </p> <ol><li>The access class is the only one guaranteed to have access to the serialize (or save/load) functions. </li><li>Just because an object doesn't have a serialize member function, it doesn't mean it isn't serializable, since it can have a free function. By putting that check in access instead of at a higher level, it will gracefully handle that case. </li></ol><p> What it will do now is throw an exception if you try to serialize an object that doesn't have either a free serialize function or a serialize member function. This can't be a compile time check, because the template instantiations will reference the serialize function even if it is never called. </p> </description> <category>Ticket</category> </item> <item> <author>Aaron Barany <akb825@…></author> <pubDate>Fri, 20 May 2011 22:16:24 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/5340#comment:6 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/5340#comment:6</guid> <description> <p> I forgot to mention, I also attached the patch that combines this issue and <a class="reopened ticket" href="https://svn.boost.org/trac10/ticket/5341" title="#5341: Patches: Patch to improve shared library behavior with serialization (reopened)">#5341</a>. This is for convenience, since they aren't mutually exclusive, so the patches won't apply separately. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>anonymous</dc:creator> <pubDate>Wed, 13 Aug 2014 07:31:01 GMT</pubDate> <title>attachment set https://svn.boost.org/trac10/ticket/5340 https://svn.boost.org/trac10/ticket/5340 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">compileErrorSerialiazationPatch.cpp</span> </li> </ul> Ticket anonymous Wed, 13 Aug 2014 07:32:00 GMT <link>https://svn.boost.org/trac10/ticket/5340#comment:7 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/5340#comment:7</guid> <description> <p> The patch breaks the compatibility with older implementations. The following snipped shows this and it works before applying the patch, now the code issue a compiler error on gcc 4.6.3. See attachment compileErrorSerialiazationPatch.cpp </p> <p> Compiler error </p> <pre class="wiki">g++ -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"testMemberFuncCheck.d" -MT"testMemberFuncCheck.d" -o "testMemberFuncCheck.o" "../testMemberFuncCheck.cpp" ../testMemberFuncCheck.cpp:149:1: error: template-id ‘serialize&lt;boostIArchive&gt;’ for ‘void AccessTestDerB::serialize(boostIArchive&amp;, unsigned int)’ does not match any template declaration ../testMemberFuncCheck.cpp:149:1: error: template-id ‘serialize&lt;boostOArchive&gt;’ for ‘void AccessTestDerB::serialize(boostOArchive&amp;, unsigned int)’ does not match any template declaration In file included from /usr/include/boost/serialization/split_member.hpp:23:0, from /usr/include/boost/serialization/nvp.hpp:33, from /usr/include/boost/serialization/array.hpp:19, from /usr/include/boost/archive/basic_binary_oprimitive.hpp:50, from /usr/include/boost/archive/binary_oarchive_impl.hpp:22, from /usr/include/boost/archive/binary_oarchive.hpp:21, from ../testMemberFuncCheck.cpp:106: /usr/include/boost/serialization/access.hpp: In instantiation of ‘const bool boost::serialization::access::has_serialize_func&lt;boost::archive::binary_iarchive, AccessTestDerB&gt;::value’: /usr/include/boost/serialization/access.hpp:306:21: instantiated from ‘static void boost::serialization::access::serialize(Archive&amp;, T&amp;, unsigned int) [with Archive = boost::archive::binary_iarchive, T = AccessTestDerB]’ /usr/include/boost/serialization/serialization.hpp:69:5: instantiated from ‘void boost::serialization::serialize(Archive&amp;, T&amp;, unsigned int) [with Archive = boost::archive::binary_iarchive, T = AccessTestDerB]’ /usr/include/boost/serialization/serialization.hpp:128:9: instantiated from ‘void boost::serialization::serialize_adl(Archive&amp;, T&amp;, unsigned int) [with Archive = boost::archive::binary_iarchive, T = AccessTestDerB]’ /usr/include/boost/archive/detail/iserializer.hpp:192:5: instantiated from ‘void boost::archive::detail::iserializer&lt;Archive, T&gt;::load_object_data(boost::archive::detail::basic_iarchive&amp;, void*, unsigned int) const [with Archive = boost::archive::binary_iarchive, T = AccessTestDerB]’ ../testMemberFuncCheck.cpp:157:1: instantiated from here /usr/include/boost/serialization/access.hpp:78:9: error: ‘&amp;AccessTestBase::serialize’ is not a valid template argument for type ‘void (AccessTestDerB::*)(boost::archive::binary_iarchive&amp;, unsigned int)’ because it is of type ‘void (AccessTestBase::*)(boost::archive::binary_iarchive&amp;, unsigned int)’ /usr/include/boost/serialization/access.hpp:78:9: note: standard conversions are not allowed in this context /usr/include/boost/serialization/access.hpp: In instantiation of ‘const bool boost::serialization::access::has_serialize_func&lt;boost::archive::binary_oarchive, AccessTestDerB&gt;::value’: /usr/include/boost/serialization/access.hpp:306:21: instantiated from ‘static void boost::serialization::access::serialize(Archive&amp;, T&amp;, unsigned int) [with Archive = boost::archive::binary_oarchive, T = AccessTestDerB]’ /usr/include/boost/serialization/serialization.hpp:69:5: instantiated from ‘void boost::serialization::serialize(Archive&amp;, T&amp;, unsigned int) [with Archive = boost::archive::binary_oarchive, T = AccessTestDerB]’ /usr/include/boost/serialization/serialization.hpp:128:9: instantiated from ‘void boost::serialization::serialize_adl(Archive&amp;, T&amp;, unsigned int) [with Archive = boost::archive::binary_oarchive, T = AccessTestDerB]’ /usr/include/boost/archive/detail/oserializer.hpp:152:5: instantiated from ‘void boost::archive::detail::oserializer&lt;Archive, T&gt;::save_object_data(boost::archive::detail::basic_oarchive&amp;, const void*) const [with Archive = boost::archive::binary_oarchive, T = AccessTestDerB]’ ../testMemberFuncCheck.cpp:157:1: instantiated from here /usr/include/boost/serialization/access.hpp:78:9: error: ‘&amp;AccessTestBase::serialize’ is not a valid template argument for type ‘void (AccessTestDerB::*)(boost::archive::binary_oarchive&amp;, unsigned int)’ because it is of type ‘void (AccessTestBase::*)(boost::archive::binary_oarchive&amp;, unsigned int)’ /usr/include/boost/serialization/access.hpp:78:9: note: standard conversions are not allowed in this context </pre><p> PS: Maybe someone can say how to log in here or how to recover my old account. Pressing Login will only give a shitty login prompt without anything else. </p> </description> <category>Ticket</category> </item> <item> <author>boldie@…</author> <pubDate>Wed, 13 Aug 2014 12:24:35 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/5340#comment:8 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/5340#comment:8</guid> <description> <p> I just found another problem with the patch, on shutdown there is a pure virtual method called. Using the debugger and the this pointer of frame 7, I got a segfault, so this seems to be a dangling pointer which also caused the "pure virtual method called". </p> <p> I have tried to understand what happens here, but my insight to the singleton things from the lib is very limited. So far I found a suspicious part in boost/archive/impl/archive_serializer_map.ipp:175 where the element is only erased if the wrapper is not destroyed, but unfortunately the wrapper destroy will be set prior to the destructor calling of the iserialzer. Removing this part of code does not fix the problem, so it must be elsewhere. </p> <pre class="wiki">pure virtual method called terminate called without an active exception Program received signal SIGABRT, Aborted. 0xb7fdd424 in __kernel_vsyscall () (gdb) bt #0 0xb7fdd424 in __kernel_vsyscall () #1 0xb1a3120f in __GI_raise (sig=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64 #2 0xb1a34855 in __GI_abort () at abort.c:91 #3 0xb1ca613d in __gnu_cxx::__verbose_terminate_handler() () from /usr/lib/i386-linux-gnu/libstdc++.so.6 #4 0xb1ca3ed3 in ?? () from /usr/lib/i386-linux-gnu/libstdc++.so.6 #5 0xb1ca3f0f in std::terminate() () from /usr/lib/i386-linux-gnu/libstdc++.so.6 #6 0xb1ca4b82 in __cxa_pure_virtual () from /usr/lib/i386-linux-gnu/libstdc++.so.6 #7 0xb2aea283 in boost::serialization::extended_type_info::operator&lt; (this=0x83d5c34, rhs=...) at libs/serialization/src/extended_type_info.cpp:169 #8 0xb2ae00f7 in operator&lt; (rhs=..., this=0x83d5c58) at ./boost/archive/detail/basic_serializer.hpp:54 #9 boost::archive::detail::basic_serializer_map::type_info_pointer_compare::operator() (this=0xb2b15188, lhs=0x83d5c58, rhs=0x83d5cac) at libs/serialization/src/basic_serializer_map.cpp:42 #10 0xb2ae0472 in std::_Rb_tree&lt;boost::archive::detail::basic_serializer*, boost::archive::detail::basic_serializer*, std::_Identity&lt;boost::archive::detail::basic_serializer*&gt;, boost::archive::detail::basic_serializer_map::type_info_pointer_compare, std::allocator&lt;boost::archive::detail::basic_serializer*&gt; &gt;::equal_range (this=0xb2b15188, __k=@0xbfffeee4: 0x83d5cac) at /usr/include/c++/4.6/bits/stl_tree.h:1158 #11 0xb2ae028e in equal_range (__x=@0xbfffeee4: 0x83d5cac, this=0xb2b15188) at /usr/include/c++/4.6/bits/stl_multiset.h:649 #12 boost::archive::detail::basic_serializer_map::erase (this=0xb2b15188, bs=0x83d5cac) at libs/serialization/src/basic_serializer_map.cpp:57 #13 0xb2ae2939 in boost::archive::detail::archive_serializer_map&lt;boost::archive::binary_iarchive&gt;::erase (bs=0x83d5cac) at ./boost/archive/impl/archive_serializer_map.ipp:175 #14 0xb4f7a9b3 in boost::archive::detail::iserializer&lt;boost::archive::binary_iarchive, boost::intrusive_ptr&lt;ngp::Variant::ValueBase&gt; &gt;::~iserializer ( this=0x83d5ca8, __in_chrg=&lt;optimized out&gt;) at /usr/include/boost/archive/detail/iserializer.hpp:161 #15 0xb4f7aa58 in boost::serialization::detail::singleton_wrapper&lt;boost::archive::detail::iserializer&lt;boost::archive::binary_iarchive, boost::intrusive_ptr&lt;ngp::Variant::ValueBase&gt; &gt; &gt;::~singleton_wrapper (this=0x83d5ca8, __in_chrg=&lt;optimized out&gt;) at /usr/include/boost/serialization/singleton.hpp:112 #16 0xb1a3632b in __cxa_finalize (d=0xb7f95fc0) at cxa_finalize.c:56 </pre> </description> <category>Ticket</category> </item> </channel> </rss>