Re: [Boost-bugs] [Boost C++ Libraries] #3604: Access violation on diamond inheritance

Subject: Re: [Boost-bugs] [Boost C++ Libraries] #3604: Access violation on diamond inheritance
From: Boost C++ Libraries (noreply_at_[hidden])
Date: 2009-11-30 18:22:24


#3604: Access violation on diamond inheritance
----------------------------------+-----------------------------------------
  Reporter: kondo@… | Owner: ramey
      Type: Support Requests | Status: closed
 Milestone: Boost 1.41.0 | Component: serialization
   Version: Boost 1.40.0 | Severity: Not Applicable
Resolution: invalid | Keywords:
----------------------------------+-----------------------------------------
Changes (by ramey):

  * type: Bugs => Support Requests
  * severity: Problem => Not Applicable

Comment:

 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.

 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

 when we

 EXPORT(Target)
 EXPORT(sub1)

 Target object:
     VBase data
     Mid2 data
     Sub2 data
     target data

 Sub1 object
     pointer to VBase data
     Mid1 data
     Sub1 data

 So we can downcast from pVbase to pTarget because downcast just adjusts
 the pointer according to the size of the embedded data structures.

 But when we do

 EXPORT(sub1)
 EXPORT(Target)

 Sub1 object
     VBase data
     Mid1 data
     Sub1 data

 Target object:
     pointer VBase data
     Mid2 data
     Sub2 data
     target data

 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.

 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.

 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.

 Q3: Assuming I'm correct in my answer to Q2, there's two approaches:

 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.

 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.

 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.

 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.

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

 I think any the above would likely work well.

 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.

 d) note that I addressed some similar issues with
 boost::serialization::singleton<T> template.

 These later approaches would be more experimental.

 Good luck.

 Robert Ramey

-- 
Ticket URL: <https://svn.boost.org/trac/boost/ticket/3604#comment:6>
Boost C++ Libraries <http://www.boost.org/>
Boost provides free peer-reviewed portable C++ source libraries.

This archive was generated by hypermail 2.1.7 : 2017-02-16 18:50:01 UTC