Boost C++ Libraries: Ticket #3604: Access violation on diamond inheritance https://svn.boost.org/trac10/ticket/3604 <h1 class="section" id="phenomenon">phenomenon</h1> <p> When I serialize the sub-class via virtual base class, some BOOST_CLASS_EXPORT order makes access violation. </p> <p> main.cpp(attached file) reproduce these behavior on VC++ ver 9. Please check it. </p> <p> Classes structure is structure.png(attached file). </p> <p> If BOOST_CLASS_EXPORT order is Target, Sub1, the error doesn't occur. If BOOST_CLASS_EXPORT order is Sub1, Target, the error occurs. </p> <p> main.cpp </p> <div class="wiki-code"><div class="code"><pre><span class="cp">#if 0</span><span class="c"></span> <span class="c">// OK</span> <span class="c">BOOST_CLASS_EXPORT(Target)</span> <span class="c">BOOST_CLASS_EXPORT(Sub1)</span> <span class="cp">#else</span> <span class="c1">// NG</span> <span class="n">BOOST_CLASS_EXPORT</span><span class="p">(</span><span class="n">Sub1</span><span class="p">)</span> <span class="n">BOOST_CLASS_EXPORT</span><span class="p">(</span><span class="n">Target</span><span class="p">)</span> <span class="cp">#endif</span> </pre></div></div><h1 class="section" id="analysis">analysis</h1> <p> I checked the behavior of void_caster::recursive_register step by step. </p> <p> boost_1_40_0\libs\serialization\src\void_cast.cpp </p> <div class="wiki-code"><div class="code"><pre><span class="c1">// implementation of void caster base class</span> <span class="n">BOOST_SERIALIZATION_DECL</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="n">void_caster</span><span class="o">::</span><span class="n">recursive_register</span><span class="p">(</span><span class="kt">bool</span> <span class="n">includes_virtual_base</span><span class="p">)</span> <span class="k">const</span> <span class="p">{</span> <span class="n">void_cast_detail</span><span class="o">::</span><span class="n">set_type</span> <span class="o">&amp;</span> <span class="n">s</span> <span class="o">=</span> <span class="n">void_cast_detail</span><span class="o">::</span><span class="n">void_caster_registry</span><span class="o">::</span><span class="n">get_mutable_instance</span><span class="p">();</span> <span class="n">s</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="k">this</span><span class="p">);</span> <span class="c1">// generate all implied void_casts.</span> <span class="n">void_cast_detail</span><span class="o">::</span><span class="n">set_type</span><span class="o">::</span><span class="n">const_iterator</span> <span class="n">it</span><span class="p">;</span> <span class="k">for</span><span class="p">(</span><span class="n">it</span> <span class="o">=</span> <span class="n">s</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span> <span class="n">it</span> <span class="o">!=</span> <span class="n">s</span><span class="p">.</span><span class="n">end</span><span class="p">();</span> <span class="o">++</span><span class="n">it</span><span class="p">){</span> <span class="k">if</span><span class="p">(</span><span class="o">*</span> <span class="n">m_derived</span> <span class="o">==</span> <span class="o">*</span> <span class="p">(</span><span class="o">*</span><span class="n">it</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">m_base</span><span class="p">)</span> <span class="k">new</span> <span class="n">void_caster_shortcut</span><span class="p">(</span> <span class="c1">// match A</span> <span class="p">(</span><span class="o">*</span><span class="n">it</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">m_derived</span><span class="p">,</span> <span class="n">m_base</span><span class="p">,</span> <span class="n">m_difference</span> <span class="o">+</span> <span class="p">(</span><span class="o">*</span><span class="n">it</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">m_difference</span><span class="p">,</span> <span class="n">includes_virtual_base</span> <span class="p">);</span> <span class="k">if</span><span class="p">(</span><span class="o">*</span> <span class="p">(</span><span class="o">*</span><span class="n">it</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">m_derived</span> <span class="o">==</span> <span class="o">*</span> <span class="n">m_base</span><span class="p">)</span> <span class="k">new</span> <span class="n">void_caster_shortcut</span><span class="p">(</span> <span class="c1">// match B</span> <span class="n">m_derived</span><span class="p">,</span> <span class="p">(</span><span class="o">*</span><span class="n">it</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">m_base</span><span class="p">,</span> <span class="n">m_difference</span> <span class="o">+</span> <span class="p">(</span><span class="o">*</span><span class="n">it</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">m_difference</span><span class="p">,</span> <span class="n">includes_virtual_base</span> <span class="p">);</span> <span class="p">}</span> </pre></div></div><hr /> <p> OK Case </p> <p> r-level means recursive level(not so important). 'No' means registration order(not internal order of container set). </p> <table class="wiki"> <tr><td>No</td><td>r-level</td><td>virtual base</td><td>Derived</td><td>Base</td><td>Action</td><td>OverWrite </td></tr><tr><td>0</td><td colspan="2">false</td><td>Target</td><td>Sub2</td><td>-</td><td> </td></tr><tr><td>1</td><td colspan="2">false</td><td>Sub1</td><td>Mid1</td><td>-</td><td> </td></tr><tr><td>2</td><td colspan="2">false</td><td>Sub1</td><td>Mid2</td><td>-</td><td> </td></tr><tr><td>3</td><td colspan="2">false</td><td>Sub2</td><td>Mid1</td><td>match A Sub2 No.0,add No.4</td><td> </td></tr><tr><td>4</td><td>1</td><td>false</td><td>Target</td><td>Mid1</td><td>-</td><td> </td></tr><tr><td>5</td><td colspan="2">false</td><td>Sub2</td><td>Mid2</td><td>match A Sub2 No.0,add No.6</td><td> </td></tr><tr><td>6</td><td>1</td><td>false</td><td>Target</td><td>Mid2</td><td>-</td><td> </td></tr><tr><td>7</td><td colspan="2">true</td><td>Mid1</td><td>VBase</td><td>match A Mid1 No.1,add No.8</td><td> </td></tr><tr><td colspan="6">match A Mid1 No.3 add No.9</td><td> </td></tr><tr><td colspan="6">match A Mid1 No.4 add No.11</td><td> </td></tr><tr><td>8</td><td>1</td><td>true</td><td>Sub1</td><td>VBase</td><td>-</td><td> </td></tr><tr><td>9</td><td>1</td><td>true</td><td>Sub2</td><td>VBase</td><td>match A Sub2 No.0,add No.10</td><td> </td></tr><tr><td>10</td><td>2</td><td>true</td><td>Target</td><td>VBase</td><td>-</td><td> </td></tr><tr><td>11</td><td>2</td><td>true</td><td>Target</td><td>VBase</td><td>-</td><td>No.10 </td></tr><tr><td>12</td><td colspan="2">true</td><td>Mid2</td><td>VBase</td><td>match A Mid2 No.2,add No.13</td><td> </td></tr><tr><td colspan="6">match A Mid2 No.5,add No.14</td><td> </td></tr><tr><td colspan="6">match A Mid2 No.6,add No.16</td><td> </td></tr><tr><td>13</td><td>1</td><td>true</td><td>Sub1</td><td>Vbase</td><td>-</td><td>No.8 </td></tr><tr><td>14</td><td>1</td><td>true</td><td>Sub2</td><td>VBase</td><td>match A Sub2 No.0,add No.15</td><td>No.9 </td></tr><tr><td>15</td><td>2</td><td>true</td><td>Target</td><td>VBase</td><td>-</td><td>No.10 </td></tr><tr><td>16</td><td>1</td><td>true</td><td>Target</td><td>VBase</td><td>-</td><td>No.10 </td></tr></table> <p> Result (focus on m_includes_virtual_base) </p> <p> values of s </p> <div class="wiki-code"><div class="code"><pre><span class="n">void_cast_detail</span><span class="o">::</span><span class="n">set_type</span> <span class="o">&amp;</span> <span class="n">s</span> <span class="o">=</span> <span class="n">void_cast_detail</span><span class="o">::</span><span class="n">void_caster_registry</span><span class="o">::</span><span class="n">get_mutable_instance</span><span class="p">();</span> </pre></div></div><pre class="wiki">[0] 0x004add48 t const boost::serialization::void_cast_detail::void_caster * [1] 0x004add7c t const boost::serialization::void_cast_detail::void_caster * [2] 0x004adb7c t const boost::serialization::void_cast_detail::void_caster * [3] 0x004adbb0 t const boost::serialization::void_cast_detail::void_caster * [4] 0x003981e0 {m_includes_virtual_base=true } const boost::serialization::void_cast_detail::void_caster * [5] 0x004adce0 t const boost::serialization::void_cast_detail::void_caster * [6] 0x004add14 t const boost::serialization::void_cast_detail::void_caster * [7] 0x00398298 {m_includes_virtual_base=true } const boost::serialization::void_cast_detail::void_caster * [8] 0x00397a18 {m_includes_virtual_base=false } const boost::serialization::void_cast_detail::void_caster * [9] 0x003943c8 {m_includes_virtual_base=false } const boost::serialization::void_cast_detail::void_caster * [10] 0x004adb48 t const boost::serialization::void_cast_detail::void_caster * [11] 0x00398350 {m_includes_virtual_base=true } const boost::serialization::void_cast_detail::void_caster * </pre><hr /> <p> NG Case </p> <table class="wiki"> <tr><td>No</td><td>r-level</td><td>vb</td><td>Derived</td><td>Base</td><td>Action</td><td>OverWrite </td></tr><tr><td>0</td><td colspan="2">false</td><td>Sub1</td><td>Mid1</td><td>-</td><td> </td></tr><tr><td>1</td><td colspan="2">false</td><td>Sub1</td><td>Mid2</td><td>-</td><td> </td></tr><tr><td>2</td><td colspan="2">false</td><td>Target</td><td>Sub2</td><td>-</td><td> </td></tr><tr><td>3</td><td colspan="2">true</td><td>Mid1</td><td>VBase</td><td>match A Mid1 No.0,add No.4</td><td> </td></tr><tr><td>4</td><td>1</td><td>true</td><td>Sub1</td><td>VBase</td><td>-</td><td> </td></tr><tr><td>5</td><td colspan="2">true</td><td>Mid2</td><td>VBase</td><td>match A Mid2 No.1,add No.6</td><td> </td></tr><tr><td>6</td><td>1</td><td>true</td><td>Sub1</td><td>VBase</td><td>-</td><td>No.4 </td></tr><tr><td>7</td><td colspan="2">false</td><td>Sub2</td><td>Mid1</td><td>match B Mid1 No.3,add No.8</td><td> </td></tr><tr><td colspan="6">match A Sub2 No.2,add No.10</td><td> </td></tr><tr><td>8</td><td>1</td><td>false</td><td>Sub2</td><td>VBase</td><td>match A Sub2 No.2,add No.9</td><td> </td></tr><tr><td>9</td><td>2</td><td>false</td><td>Target</td><td>VBase</td><td>-</td><td> </td></tr><tr><td>10</td><td>1</td><td>false</td><td>Target</td><td>Mid1</td><td>match B Mid1 No.3,add No.11</td><td> </td></tr><tr><td>11</td><td>2</td><td>false</td><td>Target</td><td>VBase</td><td>-</td><td>No.9 </td></tr><tr><td>12</td><td colspan="2">false</td><td>Sub2</td><td>Mid2</td><td>match B Mid2 No.5,add No.13</td><td> </td></tr><tr><td colspan="6">match A Sub2 No.2,add No.15</td><td> </td></tr><tr><td>13</td><td>1</td><td>false</td><td>Sub2</td><td>VBase</td><td>match A Sub2 No.2,add No.14</td><td>No.8 </td></tr><tr><td>14</td><td>2</td><td>false</td><td>Target</td><td>VBase</td><td>-</td><td>No.9 </td></tr><tr><td>15</td><td>1</td><td>false</td><td>Target</td><td>Mid2</td><td>match B Mid2 No.5,add No.16</td><td> </td></tr><tr><td>16</td><td>2</td><td>false</td><td>Target</td><td>VBase</td><td>-</td><td>No.9 </td></tr></table> <p> Result (focus on m_includes_virtual_base) </p> <pre class="wiki">[0] 0x004adce0 t const boost::serialization::void_cast_detail::void_caster * [1] 0x004add14 t const boost::serialization::void_cast_detail::void_caster * [2] 0x004adb48 t const boost::serialization::void_cast_detail::void_caster * [3] 0x004adb7c t const boost::serialization::void_cast_detail::void_caster * [4] 0x00397a18 {m_includes_virtual_base=true } const boost::serialization::void_cast_detail::void_caster * [5] 0x004add48 t const boost::serialization::void_cast_detail::void_caster * [6] 0x004add7c t const boost::serialization::void_cast_detail::void_caster * [7] 0x00398190 {m_includes_virtual_base=false } const boost::serialization::void_cast_detail::void_caster * [8] 0x00398300 {m_includes_virtual_base=false } const boost::serialization::void_cast_detail::void_caster * [9] 0x00398540 {m_includes_virtual_base=false } const boost::serialization::void_cast_detail::void_caster * [10] 0x004adbb0 t const boost::serialization::void_cast_detail::void_caster * [11] 0x00398248 {m_includes_virtual_base=false } const boost::serialization::void_cast_detail::void_caster * </pre><hr /> <p> m_includes_virtual_base is different in OK case and NG case. </p> <p> Probably it is the reason of access violation. </p> <p> I think that there is a problem in void_caster::recursive_register algorithm. </p> en-us Boost C++ Libraries /htdocs/site/boost.png https://svn.boost.org/trac10/ticket/3604 Trac 1.4.3 kondo@… Tue, 10 Nov 2009 12:42:58 GMT attachment set https://svn.boost.org/trac10/ticket/3604 https://svn.boost.org/trac10/ticket/3604 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">main.cpp</span> </li> </ul> Ticket kondo@… Tue, 10 Nov 2009 12:43:22 GMT attachment set https://svn.boost.org/trac10/ticket/3604 https://svn.boost.org/trac10/ticket/3604 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">structure.png</span> </li> </ul> Ticket Robert Ramey Fri, 27 Nov 2009 22:53:29 GMT status changed https://svn.boost.org/trac10/ticket/3604#comment:1 https://svn.boost.org/trac10/ticket/3604#comment:1 <ul> <li><strong>status</strong> <span class="trac-field-old">new</span> → <span class="trac-field-new">assigned</span> </li> </ul> <p> Well, I've looked at this. </p> <p> a) Looks like you've found a real problem b) Looks like you've done a lot work already. Without this I couldn't have spend any time on it. c) Looks like this is a bitch of a problem to find. I'll spend a little more time on it - but can't make any promises. </p> <p> Robert Ramey </p> Ticket Robert Ramey Sun, 29 Nov 2009 07:29:18 GMT status changed; resolution set https://svn.boost.org/trac10/ticket/3604#comment:2 https://svn.boost.org/trac10/ticket/3604#comment:2 <ul> <li><strong>status</strong> <span class="trac-field-old">assigned</span> → <span class="trac-field-new">closed</span> </li> <li><strong>resolution</strong> → <span class="trac-field-new">invalid</span> </li> </ul> <p> Making a small change in your example illustrates the problem here. </p> <p> Turns out you can downcast a pointer from a virtual base class. It makes sense when you think about it. The serialization library does this at runtime through an intermediate void * so it doesn't detect the error at compile time. And the MSVC compiler doesn't detect it unless it's done with a static cast. It turns out that the vbc data is "attached" to the first subclass. So the problem only occurs when we try to use the second - sub2. </p> <p> If you make the following change in your program, and try to compile I think the problem will be obvious. (note to self, always use static_cast rather than implicit pointer conversions !!!) </p> <pre class="wiki"> // Serialize pTarget via VBase pointer. { std::ofstream ofs("outtarget.xml"); boost::archive::xml_oarchive oa(ofs); assert(ofs); Target* pTarget = new Target; VBase * pVBase = static_cast&lt;VBase *&gt;(pTarget); Target* pTarget2 = static_cast&lt;Target *&gt;(pVBase); oa &lt;&lt; boost::serialization::make_nvp("pVBase", pVBase); } { std::ifstream ifs("outtarget.xml"); assert(ifs); boost::archive::xml_iarchive ia(ifs); VBase * pVBase; ia &gt;&gt; boost::serialization::make_nvp("pVBase", pVBase); } </pre><p> PS I did in fact find some problems with void cast registry - but I don't think they're related to this. After I fixed them, I still had the problem. </p> <p> Robert Ramey </p> Ticket kondo@… Sun, 29 Nov 2009 14:38:18 GMT status changed; resolution deleted https://svn.boost.org/trac10/ticket/3604#comment:3 https://svn.boost.org/trac10/ticket/3604#comment:3 <ul> <li><strong>status</strong> <span class="trac-field-old">closed</span> → <span class="trac-field-new">reopened</span> </li> <li><strong>resolution</strong> <span class="trac-field-deleted">invalid</span> </li> </ul> <p> Sorry, I couldn't understand enough your comment. </p> <p> I modified my code (main.cpp) , and it made compile error. </p> <p> The compile error is in next code. </p> <pre class="wiki"> Target* pTarget2 = static_cast&lt;Target *&gt;(pVBase); </pre><p> And next, I checked that use only static_cast from Target* to Vbase *, it doesn't make compile error, but run result is not changed. </p> <p> I think that we can't use static_cast as downcast. </p> <p> (Does it mean library internal behavior??) </p> <p> When we used BOOST_CLASS_EXPORT, I think that we can serialize sub-class via virtual base class. </p> <p> Please point out the wrong point of my code. </p> Ticket Robert Ramey Sun, 29 Nov 2009 17:52:29 GMT status changed; resolution set https://svn.boost.org/trac10/ticket/3604#comment:4 https://svn.boost.org/trac10/ticket/3604#comment:4 <ul> <li><strong>status</strong> <span class="trac-field-old">reopened</span> → <span class="trac-field-new">closed</span> </li> <li><strong>resolution</strong> → <span class="trac-field-new">invalid</span> </li> </ul> <p> The library does a downcast from VBase * to Target * which is illegal in C++ if VBase is a virtual base class. However msvc doesn't always detect this and will sometimes work anyway. The following code WILL do the serialization, but not through a virtual base class pointer. </p> <pre class="wiki"> // Serialize pTarget { std::ofstream ofs("outtarget.xml"); boost::archive::xml_oarchive oa(ofs); assert(ofs); Target* pTarget = new Target; oa &lt;&lt; boost::serialization::make_nvp("pTarget", pTarget); } { std::ifstream ifs("outtarget.xml"); assert(ifs); boost::archive::xml_iarchive ia(ifs); Target* pTarget = new Target; ia &gt;&gt; boost::serialization::make_nvp("pTarget", pTarget); // if you want you could then use VBase *pVbase = pTarget. } </pre><p> the way you want to do can't be done in legal/portable C++ even though it seems to work sometimes with MSVC. </p> <p> Robert Ramey </p> <p> </p> Ticket kondo@… Mon, 30 Nov 2009 11:47:42 GMT attachment set https://svn.boost.org/trac10/ticket/3604 https://svn.boost.org/trac10/ticket/3604 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">UML_META.png</span> </li> </ul> Ticket kondo@… Mon, 30 Nov 2009 11:48:49 GMT <link>https://svn.boost.org/trac10/ticket/3604#comment:5 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/3604#comment:5</guid> <description> <p> Thanks for reply. </p> <p> I understand it is illegal that static down cast from virtual base class. </p> <p> I have 3 questions. </p> <h2 class="section" id="Q1">Q1</h2> <p> There is the test that serialize via the virtual base class. </p> <p> <a class="ext-link" href="https://svn.boost.org/trac/boost/browser/tags/release/Boost_1_41_0/libs/serialization/test/test_diamond.cpp#L184"><span class="icon">​</span>https://svn.boost.org/trac/boost/browser/tags/release/Boost_1_41_0/libs/serialization/test/test_diamond.cpp#L184</a> </p> <p> And It seems to work correctly. </p> <p> It it illegal? </p> <p> Is it only accidental correct behavior? </p> <p> It makes me confusing. Why does such test exist? </p> <h2 class="section" id="Q2">Q2</h2> <p> This question depends on the answer of Q1. </p> <p> If the test of Q1 is not illegal, Is the number of bottom classes of diamond inherit is important? </p> <p> Is only one bottom class allowed ? </p> <h2 class="section" id="Q3">Q3</h2> <p> We often need to treat the collection of the virtual base class, and we often want to serialize them. </p> <p> For example, UML meta-model structure.(Attached file UML_META.png) </p> <p> Doesn't current serialization library support such needs? </p> <p> Of course I understand that this is my problem (not your problem).If possible, Could you give me some advice? </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Robert Ramey</dc:creator> <pubDate>Mon, 30 Nov 2009 18:22:23 GMT</pubDate> <title>type, severity changed https://svn.boost.org/trac10/ticket/3604#comment:6 https://svn.boost.org/trac10/ticket/3604#comment:6 <ul> <li><strong>type</strong> <span class="trac-field-old">Bugs</span> → <span class="trac-field-new">Support Requests</span> </li> <li><strong>severity</strong> <span class="trac-field-old">Problem</span> → <span class="trac-field-new">Not Applicable</span> </li> </ul> <p> Q1: Now I'm wondering about this test. It currently passes on all platforms but it has failed for unexplained reasons in the past. I'm not really sure about this. Your test case is more ambiguous in that there are several "bottom" objects besides "Target". Perhaps this is what makes the difference. Also, "Target" derives from another "bottom" class. Perhaps this can be made to work by avoiding this situation. </p> <p> Q2: When I run this with the MSVC debugger an examine pVBASE . It shows garbage in the derived class. This is not what I expect to see. When you export in the other sequence, it shows OK. This is what led me to try the static downcast (which is similar to what the serialization library does to make this work). The static downcast shows a compile time error which leads me to believe that we're depending on undefined behavior. What I believe is happening is that the data looks like </p> <p> when we </p> <p> EXPORT(Target) EXPORT(sub1) </p> <p> Target object: </p> <blockquote> <p> VBase data Mid2 data Sub2 data target data </p> </blockquote> <p> Sub1 object </p> <blockquote> <p> pointer to VBase data Mid1 data Sub1 data </p> </blockquote> <p> So we can downcast from pVbase to pTarget because downcast just adjusts the pointer according to the size of the embedded data structures. </p> <p> But when we do </p> <p> EXPORT(sub1) EXPORT(Target) </p> <p> Sub1 object </p> <blockquote> <p> VBase data Mid1 data Sub1 data </p> </blockquote> <p> Target object: </p> <blockquote> <p> pointer VBase data Mid2 data Sub2 data target data </p> </blockquote> <p> and we try to downcast from pVBase to pTarget it doesnt work because the address cannot be properly adjusted. The difference is that the single instance of Vbase data is included only in the first instance created. </p> <p> So that's my guess. That is I believe that my test and your example work depend upon C++ undefined behavior. I think that knowing this will permit you to tweak your structure so that it does what you want on all compilers. But you'll really be going beyond the guarentees of the C++ language. </p> <p> BTW:C++ is now so complex in it's details that I don't have 100% confidence when I'm dealing with these kinds of details. So you're free be skeptical of my assessment. </p> <p> Q3: Assuming I'm correct in my answer to Q2, there's two approaches: </p> <p> a) Code strictly according to standard C++ and don't depend on any undefined behavior. That makes your program "provably correct" b) Make it work and move on. </p> <p> Generally I prefer a). Unfortunately, sometimes I have to do b). One such case is the EXPORT functionality which is very convenient (perhaps necessary) but relies on undefined C++ behavior from all compilers. If you need export you're already in b) territory to some extent. A couple of options. </p> <p> a) craft your application in accordance with the hypothesis in my answer to Q2 which would mean : export only one bottom class. This probably would be OK. </p> <p> b) avoid virtual base class by including a static member in the Base class. Extra work and not exactly equivalent but it might be a better choice. This would not depend on any undefined C++ behavior. </p> <p> c) note that this whole issue comes from the usage of the EXPORT. If you were to avoid using this, there would no problem. This does however required "pre-registration" using Archive::register. This is not as convenient as EXPORT, but it has the advantage that it doesn't depended on undefined C++ behavior such as EXPORT does so it is not as "fragile". </p> <p> I think any the above would likely work well. </p> <p> c) restructure the class hierarchy so that there is one (non-virtual) base class which is used as the "universal pointer". Lower classes would use multiple in heritance to "add-in" the virtual base class functionality. </p> <p> d) note that I addressed some similar issues with boost::serialization::singleton&lt;T&gt; template. </p> <p> These later approaches would be more experimental. </p> <p> Good luck. </p> <p> Robert Ramey </p> Ticket kondo@… Tue, 01 Dec 2009 12:54:06 GMT <link>https://svn.boost.org/trac10/ticket/3604#comment:7 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/3604#comment:7</guid> <description> <p> It may be out of C++ standard topic. The static down cast via virtual base class is illegal. But I think that using void pointer, the most of compiler works correctly. And it is one of the essence of void-cast mechanism. See next exmample. </p> <pre class="wiki"> Target* pTarget = new Target; Sub1* pSub1 = new Sub1; VBase *pVbTarget = pTarget; VBase *pVbSub1 = pSub1; std::ptrdiff_t dTargetToVBase = static_cast&lt;unsigned char *&gt;(static_cast&lt;void *&gt;(pTarget)) - static_cast&lt;unsigned char *&gt;(static_cast&lt;void *&gt;(pVbTarget)); std::ptrdiff_t dSub12VBase = static_cast&lt;unsigned char *&gt;(static_cast&lt;void *&gt;(pSub1)) - static_cast&lt;unsigned char *&gt;(static_cast&lt;void *&gt;(pVbSub1)); Target *pTarget_2 = static_cast&lt;Target *&gt;(static_cast&lt;void *&gt;( static_cast&lt;unsigned char *&gt;(static_cast&lt;void *&gt;(pVbTarget)) + dTargetToVBase)); Sub1 *pSub1_2 = static_cast&lt;Sub1 *&gt;(static_cast&lt;void *&gt;( static_cast&lt;unsigned char *&gt;(static_cast&lt;void *&gt;(pVbSub1)) + dSub12VBase)); </pre><p> My understanding of serialization library is not perfect. </p> <p> But I think that there is a possibility that serialize via virtual base class. </p> <p> Please hear me. </p> <p> I found a part of code that seems to support void downcast via virtual base class. </p> <p> void_caster_shortcut::vbc_downcas is it. </p> <p> <a class="ext-link" href="https://svn.boost.org/trac/boost/browser/trunk/libs/serialization/src/void_cast.cpp#L126"><span class="icon">​</span>https://svn.boost.org/trac/boost/browser/trunk/libs/serialization/src/void_cast.cpp#L126</a> </p> <p> This function is called from void_caster_shortcut::downcast. </p> <p> <a class="ext-link" href="https://svn.boost.org/trac/boost/browser/trunk/libs/serialization/src/void_cast.cpp#L95"><span class="icon">​</span>https://svn.boost.org/trac/boost/browser/trunk/libs/serialization/src/void_cast.cpp#L95</a> </p> <p> In function void_caster_shortcut::downcast, the data member m_includes_virtual_base is very important. </p> <p> <a class="ext-link" href="https://svn.boost.org/trac/boost/browser/trunk/libs/serialization/src/void_cast.cpp#L90"><span class="icon">​</span>https://svn.boost.org/trac/boost/browser/trunk/libs/serialization/src/void_cast.cpp#L90</a> </p> <p> My code (main.cpp) failure case, </p> <pre class="wiki">94 virtual void const * 95 downcast(void const * const t) const{ 96 if(m_includes_virtual_base) // false 97 return vbc_downcast(t); 98 =&gt; return static_cast&lt;const char *&gt; ( t ) + m_difference; 99 } </pre><p> success case, </p> <pre class="wiki">94 virtual void const * 95 downcast(void const * const t) const{ 96 if(m_includes_virtual_base) // true 97 =&gt; return vbc_downcast(t); 98 return static_cast&lt;const char *&gt; ( t ) + m_difference; 99 } </pre><p> In failure case, I think that the data member m_includes_virtual_base is set to false in spite of the virtual inheritance. </p> <p> I modified the library code to always call vbc_downcast. Of course it's temporary. </p> <pre class="wiki">94 virtual void const * 95 downcast(void const * const t) const{ 96 if(true) // temporary always true 97 =&gt; return vbc_downcast(t); 98 return static_cast&lt;const char *&gt; ( t ) + m_difference; 99 } </pre><p> Both EXPORT sequences work correctly. </p> <p> The data member m_includes_virtual_base is set in void_caster_shortcut's constructor, And it is called in void_caster::recursive_register. (Return to my analysis.) </p> <p> I think it may be possible to modify m_includes_virtual_base registration algorithm. </p> <p> Am I missing any big problems? </p> </description> <category>Ticket</category> </item> <item> <author>kondo@…</author> <pubDate>Wed, 02 Dec 2009 08:02:59 GMT</pubDate> <title>attachment set https://svn.boost.org/trac10/ticket/3604 https://svn.boost.org/trac10/ticket/3604 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">ticket3604.patch</span> </li> </ul> Ticket kondo@… Wed, 02 Dec 2009 08:03:33 GMT status changed; resolution deleted https://svn.boost.org/trac10/ticket/3604#comment:8 https://svn.boost.org/trac10/ticket/3604#comment:8 <ul> <li><strong>status</strong> <span class="trac-field-old">closed</span> → <span class="trac-field-new">reopened</span> </li> <li><strong>resolution</strong> <span class="trac-field-deleted">invalid</span> </li> </ul> <p> I hesitated to reopen this ticket. </p> <p> If the serialization library doesn't support the object serialization via virtual base class, this idea might be a expansion proposal. </p> <p> Should I create a new ticket as enhancement? </p> <p> I wrote the patch(ticket3604.patch) seems to resolve this problem. </p> <p> After applying this patch, Both EXPORT sequences in main.cpp work correctly. </p> <p> This patch is for trunk. </p> <p> Please try it. </p> <p> It is based on next idea. </p> <h1 class="section" id="BackgroundMyopinion">Background (My opinion)</h1> <p> I think if the information in void_caster_registry is correct, we can serialize the target class via virtual base class. </p> <p> The void_caster_registry means a table that we can get it to call void_caster_registry::get_mutable_instance(). </p> <p> It might be out of standard C++, but most of compiler works correctly. </p> <h1 class="section" id="Strategy">Strategy</h1> <p> Avoid overwriting the virtual inheritance information in recursive register process. </p> <h1 class="section" id="Modification">Modification</h1> <p> Add the new data member m_includes_virtual_base to class void_caster. </p> <p> In void_caster::recursive_register() member function, m_includes_virtual_base memorize virtual inheritance information from the argument includes_virtual_base. </p> <table class="wiki"> <tr>When the program creates the void_caster_shortcut object in void_caster::recursive_register(), pass the 4th argument '(*it)-&gt;m_includes_virtual_base <td> includes_virtual_base' instead of includes_virtual_base. </td></tr></table> <p> As a result, the virtual inheritance information in void_caster_registry is kept. </p> <h1 class="section" id="Resultreport">Result report</h1> <p> void_caster_registry information </p> <h2 class="section" id="OKCase">OK Case</h2> <p> EXPORT seruqnce:Target,Sub1 </p> <p> Before patching and after patching made same result. </p> <p> MIVB means m_includes_virtual_base. </p> <table class="wiki"> <tr><td>No</td><td>MIVB</td><td>Derived</td><td>Base </td></tr><tr><td>0</td><td>FALSE</td><td>Target</td><td>Sub2 </td></tr><tr><td>1</td><td>FALSE</td><td>Sub1</td><td>Mid1 </td></tr><tr><td>2</td><td>FALSE</td><td>Sub1</td><td>Mid2 </td></tr><tr><td>3</td><td>FALSE</td><td>Sub2</td><td>Mid1 </td></tr><tr><td>4</td><td>FALSE</td><td>Target</td><td>Mid1 </td></tr><tr><td>5</td><td>FALSE</td><td>Sub2</td><td>Mid2 </td></tr><tr><td>6</td><td>FALSE</td><td>Target</td><td>Mid2 </td></tr><tr><td>7</td><td>TRUE</td><td>Mid1</td><td>VBase </td></tr><tr><td>8</td><td>TRUE</td><td>Sub1</td><td>VBase </td></tr><tr><td>9</td><td>TRUE</td><td>Sub2</td><td>VBase </td></tr></table> <h2 class="section" id="NGCase">NG Case</h2> <p> EXPORT seruqnce:Sub1,Target </p> <p> Before patching </p> <table class="wiki"> <tr><td>No</td><td>MIVB</td><td>Derived</td><td>Base </td></tr><tr><td>0</td><td>FALSE</td><td>Sub1</td><td>Mid1 </td></tr><tr><td>1</td><td>FALSE</td><td>Sub1</td><td>Mid2 </td></tr><tr><td>2</td><td>FALSE</td><td>Target</td><td>Sub2 </td></tr><tr><td>3</td><td>TRUE</td><td>Mid1</td><td>VBase </td></tr><tr><td>4</td><td>TRUE</td><td>Sub1</td><td>VBase </td></tr><tr><td>5</td><td>TRUE</td><td>Mid2</td><td>VBase </td></tr><tr><td>6</td><td>TRUE</td><td>Sub1</td><td>VBase </td></tr><tr><td>7</td><td>FALSE</td><td>Sub2</td><td>Mid1 </td></tr><tr><td>8</td><td>FALSE</td><td>Sub2</td><td>VBase </td></tr><tr><td>9</td><td>FALSE</td><td>Target</td><td>VBase </td></tr></table> <p> No.8 and 9 seem to incorrect.Their MIVB should be true. </p> <p> Afret patching </p> <table class="wiki"> <tr><td>No</td><td>MIVB</td><td>Derived</td><td>Base </td></tr><tr><td>0</td><td>FALSE</td><td>Sub1</td><td>Mid1 </td></tr><tr><td>1</td><td>FALSE</td><td>Sub1</td><td>Mid2 </td></tr><tr><td>2</td><td>FALSE</td><td>Target</td><td>Sub2 </td></tr><tr><td>3</td><td>TRUE</td><td>Mid1</td><td>VBase </td></tr><tr><td>4</td><td>TRUE</td><td>Sub1</td><td>VBase </td></tr><tr><td>5</td><td>TRUE</td><td>Mid2</td><td>VBase </td></tr><tr><td>6</td><td>TRUE</td><td>Sub1</td><td>VBase </td></tr><tr><td>7</td><td>FALSE</td><td>Sub2</td><td>Mid1 </td></tr><tr><td>8</td><td>TRUE</td><td>Sub2</td><td>VBase </td></tr><tr><td>9</td><td>TRUE</td><td>Target</td><td>VBase </td></tr></table> <p> All information seems to be correct! </p> Ticket kondo@… Sun, 20 Dec 2009 13:28:20 GMT attachment set https://svn.boost.org/trac10/ticket/3604 https://svn.boost.org/trac10/ticket/3604 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">ticket3064_remove_const.patch</span> </li> </ul> Ticket kondo@… Sun, 20 Dec 2009 13:29:06 GMT attachment set https://svn.boost.org/trac10/ticket/3604 https://svn.boost.org/trac10/ticket/3604 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">test_diamond_complex.cpp</span> </li> </ul> Ticket kondo@… Sun, 20 Dec 2009 13:49:17 GMT <link>https://svn.boost.org/trac10/ticket/3604#comment:9 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/3604#comment:9</guid> <description> <p> I modify my patch. (Attached file:ticket3064_remove_const.patch) </p> <p> The new patch removes const from void_caster::recursive_register() instead of using mutable data member. </p> <p> Behavior is not changed. But it is more clear about an intention. </p> <p> And I did the regression test of serialization. </p> <p> All of them have passed. </p> <p> In addition, I did the exhaustive test about this problem. (Attached file:test_diamond_complex.cpp) </p> <p> After applying my patch, this test also have passed. </p> <p> (Before applying my patch, this test didn't pass.) </p> <p> Please check my test and patch, and if result is OK, accept them. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Robert Ramey</dc:creator> <pubDate>Tue, 30 Mar 2010 15:46:38 GMT</pubDate> <title>attachment set https://svn.boost.org/trac10/ticket/3604 https://svn.boost.org/trac10/ticket/3604 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">void_cast.cpp</span> </li> </ul> Ticket Robert Ramey Tue, 30 Mar 2010 15:47:11 GMT attachment set https://svn.boost.org/trac10/ticket/3604 https://svn.boost.org/trac10/ticket/3604 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">void_cast.hpp</span> </li> </ul> Ticket Robert Ramey Tue, 30 Mar 2010 15:48:07 GMT <link>https://svn.boost.org/trac10/ticket/3604#comment:10 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/3604#comment:10</guid> <description> <p> I looked at this again. I made some changes based on your code. Basically, I wasn't happy with the movement duplication of m_is_virtual base. When I made these changes, your test failed so I'm still not convinced that the passing of a test is a good indicator that I'm wrong that static downcasting from a virtual base class can only work as a matter of coincidence. I've attached the copies of void_cast.* that I've used. </p> </description> <category>Ticket</category> </item> <item> <author>kondo@…</author> <pubDate>Sat, 03 Apr 2010 05:11:36 GMT</pubDate> <title>attachment set https://svn.boost.org/trac10/ticket/3604 https://svn.boost.org/trac10/ticket/3604 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">void_cast_add_or.cpp</span> </li> </ul> <p> Additional modified file based on Comment 10 </p> Ticket kondo@… Sat, 03 Apr 2010 05:13:36 GMT attachment set https://svn.boost.org/trac10/ticket/3604 https://svn.boost.org/trac10/ticket/3604 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">cls1.png</span> </li> </ul> <p> Simple class diagram pattern1 </p> Ticket kondo@… Sat, 03 Apr 2010 05:14:13 GMT attachment set https://svn.boost.org/trac10/ticket/3604 https://svn.boost.org/trac10/ticket/3604 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">cls2.png</span> </li> </ul> <p> Simple class diagram pattern2 </p> Ticket kondo@… Sat, 03 Apr 2010 05:42:49 GMT attachment set https://svn.boost.org/trac10/ticket/3604 https://svn.boost.org/trac10/ticket/3604 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">abc1.cpp</span> </li> </ul> <p> the test code corresponding to cls1. </p> Ticket kondo@… Sat, 03 Apr 2010 05:44:17 GMT attachment set https://svn.boost.org/trac10/ticket/3604 https://svn.boost.org/trac10/ticket/3604 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">abc2.cpp</span> </li> </ul> <p> the test code corresponding to cls2. </p> Ticket kondo@… Sat, 03 Apr 2010 05:46:33 GMT <link>https://svn.boost.org/trac10/ticket/3604#comment:11 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/3604#comment:11</guid> <description> <p> Thanks for reply. I checked your modification. It is surely based on my idea. And My test failed. </p> <p> I think there are some problems in your modification. Consider the case of cls1.png, abc1.cpp, cls2.png and abc2.cpp (attached.file). </p> <p> The new shortcut element of void_caster_registry is made by already registered member and current registering member. </p> <p> And if either includes virtual base, it shall include virtual base. </p> <pre class="wiki">110 void_caster_shortcut( 111 extended_type_info const * derived, 112 extended_type_info const * base, 113 std::ptrdiff_t difference, 114 bool includes_virtual_base, 115 void_caster const * const parent 116 ) : 117 void_caster(derived, base, difference, parent), 118 m_includes_virtual_base(includes_virtual_base) 119 { 120 recursive_register(includes_virtual_base); 121 } </pre><p> I think the member function recursive_register() needs the argument includes_virtual_base. </p> <p> And in the member function recursive_register(), 'logical OR' operation is needed. </p> <pre class="wiki">243 if(* m_derived == * (*it)-&gt;m_base){ 244 const void_caster_argument vca( 245 (*it)-&gt;m_derived, 246 m_base 247 ); 248 void_cast_detail::set_type::const_iterator i; 249 i = s.find(&amp; vca); 250 if(i == s.end()){ 251 new void_caster_shortcut( 252 (*it)-&gt;m_derived, 253 m_base, 254 m_difference + (*it)-&gt;m_difference, 255 (*it)-&gt;has_virtual_base() || includes_virtual_base, 256 this 257 ); 258 } 259 } 260 if(* (*it)-&gt;m_derived == * m_base){ 261 const void_caster_argument vca( 262 m_derived, 263 (*it)-&gt;m_base 264 ); 265 void_cast_detail::set_type::const_iterator i; 266 i = s.find(&amp; vca); 267 if(i == s.end()){ 268 new void_caster_shortcut( 269 m_derived, 270 (*it)-&gt;m_base, 271 m_difference + (*it)-&gt;m_difference, 272 (*it)-&gt;has_virtual_base() || includes_virtual_base, 273 this 274 ); 275 } 276 } </pre><p> Based on this idea, I modified the void_caster.cpp you attached. </p> <p> It is void_cast_add_or.cpp (attached file). </p> <p> After this modification, my test succeeded. </p> <p> What do you think? </p> </description> <category>Ticket</category> </item> <item> <author>kondo@…</author> <pubDate>Sat, 10 Apr 2010 00:18:13 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/3604#comment:12 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/3604#comment:12</guid> <description> <p> I offer additional information. I believe that if the virtual inheritance inclusion distinguished correctly, static downcast from a virtual base class does not occur anymore. </p> <p> Because the void cast includes virtual inheritance case, the void cast code is dispatched below class. </p> <p> void_cast.hpp </p> <pre class="wiki">template &lt;class Derived, class Base&gt; class void_caster_virtual_base : public void_caster { virtual bool has_virtual_base() const { return true; } public: virtual void const * downcast(void const * const t) const { const Derived * d = dynamic_cast&lt;const Derived *&gt;( static_cast&lt;const Base *&gt;(t) ); return d; } virtual void const * upcast(void const * const t) const { const Base * b = dynamic_cast&lt;const Base *&gt;( static_cast&lt;const Derived *&gt;(t) ); return b; } void_caster_virtual_base(); virtual ~void_caster_virtual_base(); }; </pre><p> And the void cast use dynamic_cast instead of static_cast. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Robert Ramey</dc:creator> <pubDate>Sun, 16 May 2010 22:30:35 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/3604#comment:13 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/3604#comment:13</guid> <description> <p> This is still not working for me. </p> <p> I'm using: </p> <p> test_diamond_complex.cpp latest 1.43 release version void_cast_add_or.cpp as you've added void_cast.hpp as above MSVC 7.1 static version of the library </p> <p> As far as I can tell, the following code </p> <pre class="wiki"> virtual void const * upcast(void const * const t) const { const Base * b = dynamic_cast&lt;const Base *&gt;( static_cast&lt;const Derived *&gt;(t) ); return b; } </pre><p> on line # 219 of void_cast.hpp isn't working. </p> <p> It should do a dynamic_cast to EX1Level2_A * from a EX1Level1 * but the pointer doesn't change and subsequent to that the program crashes. </p> <p> I should say that the next item in the stack shows line # 348 in void_cast.cpp which is a mystery to me. </p> <p> Double check this to see if we're still getting the same results. If not, I can check my change into the trunk and you can run from there. I might also check with another compiler - but I'm curious to know what you're using so we be sure that's not a source of difference. </p> <p> So nice to meet you in person at <a class="wiki" href="https://svn.boost.org/trac10/wiki/BoostCon">BoostCon</a> 2010 </p> <p> Robert Ramey </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Robert Ramey</dc:creator> <pubDate>Mon, 17 May 2010 18:18:18 GMT</pubDate> <title>status changed; resolution set https://svn.boost.org/trac10/ticket/3604#comment:14 https://svn.boost.org/trac10/ticket/3604#comment:14 <ul> <li><strong>status</strong> <span class="trac-field-old">reopened</span> → <span class="trac-field-new">closed</span> </li> <li><strong>resolution</strong> → <span class="trac-field-new">fixed</span> </li> </ul> <p> I found an error in my build set up and re-ran your test. </p> <p> Congratulations ! - looks like it works well. </p> <p> Sooooooo </p> <p> I'm adding this change to the library. I'm adding your test_diamond_complex to the test suite I'm updating the "acknowledgments" section to include your contribution. </p> <p> Thanks for persevering with this. </p> <p> Robert Ramey </p> <p> Since you've invested a lot of understanding in the library, you might want to consider investigating some other issues. </p> <p> a) My original implementation and your fix of virtual base class serialization depends on the compiler enabling RTTI. This is not a huge limitation, I'll note it in the documentation. But wonder if there is a way around this? </p> Ticket kondo@… Mon, 24 May 2010 12:32:45 GMT <link>https://svn.boost.org/trac10/ticket/3604#comment:15 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/3604#comment:15</guid> <description> <blockquote class="citation"> <p> I'm adding this change to the library. I'm adding your test_diamond_complex to the test suite I'm updating the "acknowledgments" section to include your contribution. </p> </blockquote> <p> It's my honor. I'm very glad to contribute to your library. </p> <blockquote class="citation"> <p> Since you've invested a lot of understanding in the library, you might want to consider investigating some other issues. </p> </blockquote> <blockquote class="citation"> <p> a) My original implementation and your fix of virtual base class serialization depends on the compiler enabling RTTI. This is not a huge limitation, I'll note it in the documentation. But wonder if there is a way around this? </p> </blockquote> <p> I think that if we follow the C++ standard, it is impossible. As you know, downcasting via virtual base class must use dynamic_cast. </p> <p> But... </p> <p> If we go out of the C++ stantard, there might be some possibilities. Of course it depends on the compiler implementation. I think it is dangerous. Nevertheless, Do you think that RTTI free implementation is still important? If you think so, I'll consider about it. </p> </description> <category>Ticket</category> </item> </channel> </rss>