Topic: Obese compound objects??? >:o(


Author: "Vesa Karvonen" <vesa.karvonen@hNoOuSsPeAmMarque.fi>
Date: Wed, 23 May 2001 15:05:22 GMT
Raw View
"Andrei Alexandrescu" <andrewalex@hotmail.com> wrote in message
news:9eca6o$2153m$1@ID-14036.news.dfncis.de...
[snip]
> >From these two paragraphs, Vesa concludes that the compiler cannot aply
EBO
> to collapse Base1 and Base2 into Derived.
[snip]

This sounds like I would be almost certain, but I'm not. I just haven't been
able extract a clear statement from the standard that would explain when
exactly can EBO be performed with MI.

[snip]
> Apparently compiler writers reasoned the same because both MWCW and MSVC
> don't apply EBO to multiple inheritance, although they gladly do so for
> single inheritance.
[snip]

MWCW and MSVC do perform some form of EBO with MI, but at least in MSVC it
seems to be broken and MWCW seems to have quite interesting rules to it.

The following is a simple Ad Hoc EBO test program.

#include <iostream>

struct A {};
struct B {};
struct C : A, B {};

static struct C_Trouble
{ C c;
  B b;
} c_trouble;

struct D {};
struct E {};
struct F {};
struct G : D, E, F {};

static struct G_Trouble
{ G g;
  F f;
} g_trouble;

int main()
{ using std::cout;

  if (sizeof(C) == sizeof(A))
    cout << "EBO applied to C.\n";
  else
    cout << "EBO not fully applied to C!\n";

  if (&c_trouble.c == &c_trouble.b)
    cout << "EBO is broken on C!\n";
  else
    cout << "No Trouble on C.\n";

  if (sizeof(G) == sizeof(D))
    cout << "EBO applied to G.\n";
  else
    cout << "EBO not fully applied to G!\n";

  if (&g_trouble.g == &g_trouble.f)
    cout << "EBO is broken on G!\n";
  else
    cout << "No Trouble on G.\n";

  return 0;
}

On MSVC 6.0 I get the following output:

    EBO applied to C.
    EBO is broken on C!
    EBO not fully applied to G!
    EBO is broken on G!

On MWCW 7.0 for Palm OS, I can not use the cout, but if I interpreted the
disassembly correctly, it would print:

    EBO not fully applied to C!
    No Trouble on C.
    EBO not fully applied to G!
    No Trouble on G.

On DEC CXX I get:

    EBO not fully applied to C!
    No Trouble on C.
    EBO not fully applied to G!
    No Trouble on G.

On G++ 2.8 something (quite old - I know!) I Get:

    EBO not fully applied to C!
    No Trouble on C.
    EBO not fully applied to G!
    No Trouble on G.

I could test many more compilers, but accessing those compilers would
reguire installing them, which would take too much of my time to be useful.
I personally avoid MI in my design by using parameterized inheritance (PI),
which doesn't exhibit the problem.


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: news/comp.std.c++@nmhq.net (Niklas Matthies)
Date: Wed, 23 May 2001 15:07:57 GMT
Raw View
On Tue, 22 May 2001 20:27:40 GMT, Andrei Alexandrescu <andrewalex@hotmail=
.com> wrote:
[=B7=B7=B7]
> 2. Is there a serious reason for which the empty base subobjects in a M=
I
> hierarchy must have distinct addresses as compared after converted to
> void* - any examples?

Those base subobjects may insert themselves in some global collection
(for reference counting, serialization, or whatever), which might use
equality checks on the pointers to ensure having only one reference etc.
Code may heavily break if this isn't true anymore.

> 3. Is there a snowball's chance in hell that C++ 0x rewords the 10.1
> para 4 to allow implementers to apply EBO to multiple inheritance
> hierarchies? I hope Loki is a proof of EBO's utility.

Methinks that C++ needs something like Java-style interfaces to enable
supertypes that aren't object types in the sense of a well-defined
region of data storage. For example, sizeof(interface_ref) would not be
valid. Currently, every pointer/reference to an object is expected to
refer to an object_in_the_above_sense having the pointer/reference's
base type and that can be used as if it were an independent object of
that type. (Current tricks like declaring the assignment operator
private to make it appear like a non-object_in_the_above_sense can be
considered to be merely a hack.)

-- Niklas Matthies

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Dennis Yelle <dennis51@jps.net>
Date: Wed, 23 May 2001 18:45:55 GMT
Raw View
Niklas Matthies wrote:
>=20
> On Tue, 22 May 2001 20:27:40 GMT, Andrei Alexandrescu <andrewalex@hotma=
il.com> wrote:
> [=B7=B7=B7]
> > 2. Is there a serious reason for which the empty base subobjects in a=
 MI
> > hierarchy must have distinct addresses as compared after converted to
> > void* - any examples?
>=20
> Those base subobjects may insert themselves in some global collection
> (for reference counting, serialization, or whatever), which might use
> equality checks on the pointers to ensure having only one reference etc.
> Code may heavily break if this isn't true anymore.

Do people actually do this with zero-sized objects?
I don't like arguments that say:
"We can't do that because some program might break",
if no useful program like that actually exists.

> > 3. Is there a snowball's chance in hell that C++ 0x rewords the 10.1
> > para 4 to allow implementers to apply EBO to multiple inheritance
> > hierarchies? I hope Loki is a proof of EBO's utility.
>=20
> Methinks that C++ needs something like Java-style interfaces to enable
> supertypes that aren't object types in the sense of a well-defined
> region of data storage. For example, sizeof(interface_ref) would not be
> valid. Currently, every pointer/reference to an object is expected to
> refer to an object_in_the_above_sense having the pointer/reference's
> base type and that can be used as if it were an independent object of
> that type. (Current tricks like declaring the assignment operator
> private to make it appear like a non-object_in_the_above_sense can be
> considered to be merely a hack.)

Well, I would want to default to be the other way and
have some way of requesting that a particular zero
sized object actually have a distinct address when that
is needed.

Dennis Yelle
--=20
I am a computer programmer and I am looking for a job.
There is a link to my resume here: =20
http://table.jps.net/~vert/

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: news/comp.std.c++@nmhq.net (Niklas Matthies)
Date: Thu, 24 May 2001 19:02:26 GMT
Raw View
On Wed, 23 May 2001 18:45:55 GMT, Dennis Yelle <dennis51@jps.net> wrote:
> Niklas Matthies wrote:
> >=20
> > On Tue, 22 May 2001 20:27:40 GMT, Andrei Alexandrescu <andrewalex@hot=
mail.com> wrote:
> > [=B7=B7=B7]
> > > 2. Is there a serious reason for which the empty base subobjects in=
 a MI
> > > hierarchy must have distinct addresses as compared after converted =
to
> > > void* - any examples?
> >=20
> > Those base subobjects may insert themselves in some global collection
> > (for reference counting, serialization, or whatever), which might use
> > equality checks on the pointers to ensure having only one reference e=
tc.
> > Code may heavily break if this isn't true anymore.
>=20
> Do people actually do this with zero-sized objects?

I can think of situations where this make sense; essentially every
situation where only the existence of the particular object is
important, not its state. [1]
Although it would always be possible to use alternative approaches for
the wanted functionality, there's currently no particular reason not to
use a class that happens to have no non-static data members.

  [1] Actually, for argument's sake, it is conceivable to have an
      object's state not stored within the object itself, but at some
      different memory location, and have the members use some global
      hash table that maps the object's memory address to the location
      of its state variables when they need to access the object's
      state. It might even make sense to actually do something similar
      to this for stuff like marshalled objects.

> I don't like arguments that say:
> "We can't do that because some program might break",
> if no useful program like that actually exists.

I think it should be a sufficient argument if a useful program like that
is conceivable, since we are far from knowing which source code is being
written around the world.

> > > 3. Is there a snowball's chance in hell that C++ 0x rewords the 10.=
1
> > > para 4 to allow implementers to apply EBO to multiple inheritance
> > > hierarchies? I hope Loki is a proof of EBO's utility.
> >=20
> > Methinks that C++ needs something like Java-style interfaces to enabl=
e
> > supertypes that aren't object types in the sense of a well-defined
> > region of data storage. For example, sizeof(interface_ref) would not =
be
> > valid. Currently, every pointer/reference to an object is expected to
> > refer to an object_in_the_above_sense having the pointer/reference's
> > base type and that can be used as if it were an independent object of
> > that type. (Current tricks like declaring the assignment operator
> > private to make it appear like a non-object_in_the_above_sense can be
> > considered to be merely a hack.)
>=20
> Well, I would want to default to be the other way and
> have some way of requesting that a particular zero
> sized object actually have a distinct address when that
> is needed.

To be absolutely safe, you'd have to do this for non-zero-sized objects
as well, because the next version of their implementation could suddenly
change to being zero-sized.

Anyway, I think the more important point is that in C++, classes
historically were meant to be blueprints for objects, but that now,
empty classes are being used to represent things that aren't objects (in
the data storage sense) at all, and we now get the problems resulting
from this discrepancy. It would be cleaner to explicitly introduce the
notion of a class-like type where its instances don't have the implicit
assumption of being objects in that sense.

-- Niklas Matthies

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: "Anthony Williams" <anthwil@nortelnetworks.com>
Date: Wed, 23 May 2001 08:21:22 GMT
Raw View
"Andrei Alexandrescu" <andrewalex@hotmail.com> wrote in message
news:9eca6o$2153m$1@ID-14036.news.dfncis.de...
> Vesa Karvonen pointed out via email a very unsettling issue about the
Empty
> Base Optimization (EBO) in the presence of multiple inheritance.
>
> Let's recap EBO. In the following setting:
>
> class Base {}; // no data members at all
> class Derived : public Base
> {
>     ... some data members ...
> };
>
> the compiler is allowed to collapse Base into a zero-sized subobject, thus
> not adding any size to Derived at all. So this it the EBO.
>
> Ok, now let's switch to multiple inheritance.
>
> class Base1 {}; // no data members at all
> class Base2 {}; // unrelated to Base1, no data members at all
> class Derived : public Base1, public Base2
> {
>     ... some data members ...
> };
>
> Here Vesa points out two verses from the standard:
>
> 5.10 para 1, which says:
>
> <<
> The == (equal to) and the != (not equal to) operators have the same
semantic
> restrictions, conversions, and result type as the relational operators
> except for their lower precedence and truth-value result. [...] Pointers
to
> objects or functions of the same type (after pointer conversions) can be
> compared for equality. Two pointers of the same type compare equal if and
> only if they are both null, both point to the same object or function, or
> both point one past the end of the same array.
> >>
>
> and 10.1 para 4, which says:
>
> <<
> A base class specifier that does not contain the keyword virtual,
specifies
> a nonvirtual base class. A base class specifier that contains the keyword
> virtual, specifies a virtual base class. For each distinct occurrence of a
> nonvirtual base class in the class lattice of the most derived class, the
> most derived object (1.8) shall contain a corresponding distinct base
class
> subobject of that type. [...]
> >>
>
> (hehe, I got a copyable standard pdf.)
>
> >From these two paragraphs, Vesa concludes that the compiler cannot aply
EBO
> to collapse Base1 and Base2 into Derived. EBO would imply that Base1 and
> Base2 have the same address (you can detect that by converting their
> addresses to void* and compare them for equality) and that is not allowed.
> According to 10.1 para 4 abopve, the Base1 and Base2 subobjects must be
> distinct, and "distinct" as inferred from 5.10 para 1 means that they must
> have different addresses. Which ultimately means that the compiler cannot
> apply EBO to multiple inheritance from empty objects.
>
> Apparently compiler writers reasoned the same because both MWCW and MSVC
> don't apply EBO to multiple inheritance, although they gladly do so for
> single inheritance. This is very unsettling to me because Loki uses
multiple
> inheritance from empty classes heavily. I literally lost sleep last night.
> In many cases Loki can avoid using multiple inheritance, but that would
> either make the implementation clumsier, the usage syntax less elegant,
the
> design more limited, or some combination of the above.

MSVC DOES apply EBO to multiple inheritance. I checked it when verifying
that my StartOfObject and GenericPtr templates worked OK - I had to have
non-empty bases to get different addresses even with MI.

> So I have the following questions.
>
> 1. Is Vesa's and implementer's interpretation the only possible one?

I think not, otherwise extra padding must be added to all derived classes -
the base subobject would have to have be at a different address to the
derived object, even if both were empty.

I think the point is that they both point to the same full object, so they
are permitted to compare equal, however they also point to different
subobjects, so they are permitted compare not equal.

Also, you could take the view that if they have the same value as void*,
then they both point to the same object (of type void), and if they have
different values as void*, they point to different (void) objects.

> 2. Is there a serious reason for which the empty base subobjects in a MI
> hierarchy must have distinct addresses as compared after converted to
> void* - any examples?

No.

> 3. Is there a snowball's chance in hell that C++ 0x rewords the 10.1 para
4
> to allow implementers to apply EBO to multiple inheritance hierarchies? I
> hope Loki is a proof of EBO's utility.

I don't think a reword is necessary, except perhaps for clarity.

Anthony
--
Anthony Williams
Software Engineer, Nortel Networks Optoelectronics
The opinions expressed in this message are not necessarily those of my
employer



---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Michiel Salters<Michiel.Salters@cmg.nl>
Date: Wed, 23 May 2001 13:43:01 GMT
Raw View
In article <9eca6o$2153m$1@ID-14036.news.dfncis.de>, Andrei Alexandrescu says...
>
>Vesa Karvonen pointed out via email a very unsettling issue about the Empty
>Base Optimization (EBO) in the presence of multiple inheritance.

[ SNIP EBO explanation ]

>class Base1 {}; // no data members at all
>class Base2 {}; // unrelated to Base1, no data members at all
>class Derived : public Base1, public Base2
>{
>    ... some data members ...
>};
>
>Here Vesa points out two verses from the standard:
>
>5.10 para 1, which says:
>
><<
>The == (equal to) and the != (not equal to) operators have the same semantic
>restrictions, conversions, and result type as the relational operators
>except for their lower precedence and truth-value result. [...] Pointers to
>objects or functions of the same type (after pointer conversions) can be
>compared for equality. Two pointers of the same type compare equal if and
>only if they are both null, both point to the same object or function, or
>both point one past the end of the same array.

Which follows 5.9, which describes Relational operators. It explains which
pointer conversions are done, referencing 4.4 and 4.10. The latter is
interesting, because it says the conversion is done as if the pointer
points to a most-derived object.

>and 10.1 para 4, which says:

><<
>... For each distinct occurrence of a nonvirtual base class in the class
>lattice of the most derived class, the most derived object (1.8) shall
>contain a corresponding distinct base class subobject of that type. [...]
>>>

>From these two paragraphs, Vesa concludes that the compiler cannot aply EBO
>to collapse Base1 and Base2 into Derived. EBO would imply that Base1 and
>Base2 have the same address (you can detect that by converting their
>addresses to void* and compare them for equality) and that is not allowed.
>According to 10.1 para 4 abopve, the Base1 and Base2 subobjects must be
>distinct, and "distinct" as inferred from 5.10 para 1 means that they must
>have different addresses. Which ultimately means that the compiler cannot
>apply EBO to multiple inheritance from empty objects.

>So I have the following questions.
>
>1. Is Vesa's and implementer's interpretation the only possible one?

No. The conversion to void* is done as if the pointers point to
most-derived objects. Since they don't, information might be lost.
(including whether objects are distinct) Thus, drawing conclusions
from the equality of converted pointers is not warranted.
[ That's another interpretation. I'm not sure if it's mine. ]

>2. Is there a serious reason for which the empty base subobjects in a MI
>hierarchy must have distinct addresses as compared after converted to
>void* - any examples?

Can't think of any. But it takes me a while to switch from language
lawyer mode to programmer mode.

>3. Is there a snowball's chance in hell that C++ 0x rewords the 10.1 para 4
>to allow implementers to apply EBO to multiple inheritance hierarchies? I
>hope Loki is a proof of EBO's utility.

I think a footnote to 4.10/2 would suffice, or a small rewording.
E.g "The result cannot be used for comparisons (5.9, 5.10) if the
object of type T is a base class subobject."

Regards, Michiel Salters

--
Michiel Salters
Consultant Technical Software Engineering
CMG Trade, Transport & Industry
Michiel.Salters@cmg.nl

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: "Andrei Alexandrescu" <andrewalex@hotmail.com>
Date: Tue, 22 May 2001 20:27:40 GMT
Raw View
Vesa Karvonen pointed out via email a very unsettling issue about the Empty
Base Optimization (EBO) in the presence of multiple inheritance.

Let's recap EBO. In the following setting:

class Base {}; // no data members at all
class Derived : public Base
{
    ... some data members ...
};

the compiler is allowed to collapse Base into a zero-sized subobject, thus
not adding any size to Derived at all. So this it the EBO.

Ok, now let's switch to multiple inheritance.

class Base1 {}; // no data members at all
class Base2 {}; // unrelated to Base1, no data members at all
class Derived : public Base1, public Base2
{
    ... some data members ...
};

Here Vesa points out two verses from the standard:

5.10 para 1, which says:

<<
The == (equal to) and the != (not equal to) operators have the same semantic
restrictions, conversions, and result type as the relational operators
except for their lower precedence and truth-value result. [...] Pointers to
objects or functions of the same type (after pointer conversions) can be
compared for equality. Two pointers of the same type compare equal if and
only if they are both null, both point to the same object or function, or
both point one past the end of the same array.
>>

and 10.1 para 4, which says:

<<
A base class specifier that does not contain the keyword virtual, specifies
a nonvirtual base class. A base class specifier that contains the keyword
virtual, specifies a virtual base class. For each distinct occurrence of a
nonvirtual base class in the class lattice of the most derived class, the
most derived object (1.8) shall contain a corresponding distinct base class
subobject of that type. [...]
>>

(hehe, I got a copyable standard pdf.)

>From these two paragraphs, Vesa concludes that the compiler cannot aply EBO
to collapse Base1 and Base2 into Derived. EBO would imply that Base1 and
Base2 have the same address (you can detect that by converting their
addresses to void* and compare them for equality) and that is not allowed.
According to 10.1 para 4 abopve, the Base1 and Base2 subobjects must be
distinct, and "distinct" as inferred from 5.10 para 1 means that they must
have different addresses. Which ultimately means that the compiler cannot
apply EBO to multiple inheritance from empty objects.

Apparently compiler writers reasoned the same because both MWCW and MSVC
don't apply EBO to multiple inheritance, although they gladly do so for
single inheritance. This is very unsettling to me because Loki uses multiple
inheritance from empty classes heavily. I literally lost sleep last night.
In many cases Loki can avoid using multiple inheritance, but that would
either make the implementation clumsier, the usage syntax less elegant, the
design more limited, or some combination of the above.

So I have the following questions.

1. Is Vesa's and implementer's interpretation the only possible one?

2. Is there a serious reason for which the empty base subobjects in a MI
hierarchy must have distinct addresses as compared after converted to
void* - any examples?

3. Is there a snowball's chance in hell that C++ 0x rewords the 10.1 para 4
to allow implementers to apply EBO to multiple inheritance hierarchies? I
hope Loki is a proof of EBO's utility.

Andrei

--
Check out THE C++ Seminar:  3 Days with 5 Experts
http://www.gotw.ca/cpp_seminar/



---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]