[comp.std.c++] Packing Across Inheritance Boundaries is Currently Allowed.

gjditchfield@watmsg.uwaterloo.ca (Glen Ditchfield) (08/04/90)

I think that packing of classes is allowed by the current definition of
C++.  I base this conclusion on Ellis & Stroustrup's ``The Annotated C++
Reference Manual'', including the commentary sections, which may not be
part of the proposed standard but which give information about its intent.
   The main discussion of the order of members is in section 9.2 ``Class
Members'' on page 173.

     Nonstatic data members of a class declared without an intervening
     access-specifier are allocated so that later members have higher
     addresses within a class object.  The order of allocation of nonstatic
     data members separated by an access-specifier is implementation
     dependent (s. 11.1).  Implementation alignment requirements may cause
     two adjacent members not to be allocated immediately after each other;
     so may requirements for managing virtual functions (s. 10.2) and
     virtual base classes (s. 10.1).

Commentary in section 11.1 (p. 242) shows that a compiler might want to
move all public members to the front of a class as part of a strategy to
minimize recompilation.
   Section 10.1c is commentary, and says on page 218 that ``C++ does not
guarantee the order in which storage is allocated for derived classes''.  I
couldn't find an explicit statement of this in non-commentary text.
Apparently the members of a descendant class are considered to be separated
from members in its parents by the (explicit or default) access-specifier
in the derived class header.  The commentary shows three ways that a
derived class might be laid out.
   A compiler could take advantage of this to pack classes across
inheritance boundaries.  There is no guarantee that the addresses of all
descendant class members are greater than the address of all base class
members, so descendant members can be positioned in holes in the base
class.  If ``class Adam {char a1; long a2;}'' is laid out as
	.----v----v----v----v----v----v----v----.
	| a1 |     hole     |        a2         |
	`----^----^----^----^----^----^----^----'
then ``class Bob:public Adam {char a1;}'' can be laid out as
	.----v----v----v----v----v----v----v----.
	| a1 | b1 |   hole  |        a2         |
	`----^----^----^----^----^----^----^----'
if the compiler feels up to the task.
   A trickier question is whether the compiler is allowed to separate
members with the same access-specifier in order to fill holes.  For
example, can ``class Chuck: public Bob { char c1, c2; short c3}'' be laid
out this way?
	.----v----v----v----v----v----v----v----v----v----.
	| a1 | b1 | c1 | c2 |        a2         |   c3    |
	`----^----^----^----^----^----^----^----^----^----'
I think that this is allowed.  One might argue that is is forbidden,
because c2 and c3 are not adjacent, and the separation is not caused by
alignment requirements, virtual functions, or virtual base classes.
However, I think that the list of reasons for separation in section 9.2 is
not meant to be an exhaustive list.  If it were, it would build assumptions
about the implementation of inheritance into the language.  Besides, such a
restriction wouldn't help programmers much, since 9.2 already gives
implementations permission to insert arbitrary separations between
consecutive members if the class uses virtual functions or virtual base
classes.

    Glen Ditchfield  gjditchfield@violet.uwaterloo.ca  Office: DC 2517
Dept. of Computer Science, U of Waterloo, Waterloo, Ontario, Canada, N2L 3G1
	      These opinions have not been tested on animals.

rfg@NCD.COM (Ron Guilmette) (08/05/90)

In article <1990Aug3.211414.23872@watmath.waterloo.edu> gjditchfield@watmsg.uwaterloo.ca (Glen Ditchfield) writes:
>
>I think that packing of classes is allowed by the current definition of
>C++...

Of course it is ALLOWED.  Lots of things are allowed.  That's not the point.
The point is "What do the people who need inheritance-boundary packing to
ALWAYS occur (regardless of which compiler they are using) do?"

If you are trying to write portable code, and you need the kind of
packing that we are talking about, as of now you are out of luck because
the closest thing that we currently have to a "standard" is E&S which
does not specify a way to *force* this kind of packing to occur regardless
of which "conforming" implementation you are using.


-- 
// Ron Guilmette
// C++ Entomologist
// Internet: rfg@ncd.com      uucp: ...uunet!lupine!rfg
// Motto:  If it sticks, force it.  If it breaks, it needed replacing anyway.

perry@key.COM (Perry The Cynic) (08/18/90)

In article <1086@lupine.NCD.COM> rfg@NCD.COM (Ron Guilmette) writes:
> In article <1990Aug3.211414.23872@watmath.waterloo.edu> gjditchfield@watmsg.uwaterloo.ca (Glen Ditchfield) writes:
> >I think that packing of classes is allowed by the current definition of
> >C++...
> Of course it is ALLOWED.  Lots of things are allowed.  That's not the point.
> The point is "What do the people who need inheritance-boundary packing to
> ALWAYS occur (regardless of which compiler they are using) do?"
> 
> If you are trying to write portable code, and you need the kind of
> packing that we are talking about, as of now you are out of luck because
> the closest thing that we currently have to a "standard" is E&S which
> does not specify a way to *force* this kind of packing to occur regardless
> of which "conforming" implementation you are using.

I agree that with the current standard, any form of packing is allowed, as long
as the compiler does *not* reorder fields *within* an access specified group
(between "public","private","protected",or end-of-class-definition).

E&S gives as rationale for this one constraint the compatibility with the
(usually) coexisting C system. The idea is that a straight C-style "struct"
declaration uses the same layout as when compiled by the C compiler (ANSI C
is explicit about structure field ordering - reordering is forbidden).
I don't think the *intent* of the C++ rule is to inhibit layout modifications
in complex C++ class hierarchies.  Certainly the *wording* does allow it.

However, Ron: if you want to write "simple portable" code (no customization,
no conditionals), you cannot possibly expect to dictate to the compiler (that
is *every* compiler) what layout strategy it should use. The compiler is
responsible for reconciling the language requirements with the oddities of
the hardware. The only thing you can resonably expect to specify is guidelines.
For example, "inline" does not mean "inline this, I know what's good for you".
It means, "I guess I'd prefer if you could inline this, if it's okay with you".
Likewise I don't see how you can "force" *every* compiler out there to conform
to your idea of optimal layout.

And yes, I can see how a "packed" attribute could make sense for a struct/class,
just like "inline" makes sense for functions. I'm beginning to feel worried
though about the number of "implementation directive" features people want to
add to the language. "Inline" makes sense as a keyword, because it slightly
changes the semantics of the program (see E&S for details).

May I point out that we have a general mechanism for compiler hints and
directives, inherited from ANSI C? Pragmas. Yes, I know they are not
portable. But I'd rather see a minimal set of pragmas that every implementation
should heed as described or ignore (as opposed to redefine), than another
five keywords in the language.
  -- perry
-- 
--------------------------------------------------------------------------
Perry The Cynic (Peter Kiehtreiber)		       perry@arkon.key.com
** What good signature isn't taken yet? **  {amdahl,sgi,pacbell}!key!perry

rfg@NCD.COM (Ron Guilmette) (08/18/90)

In article <2051@key.COM> perry@arkon.key.COM (Perry The Cynic) writes:
>
>However, Ron: if you want to write "simple portable" code (no customization,
>no conditionals), you cannot possibly expect to dictate to the compiler (that
>is *every* compiler) what layout strategy it should use. The compiler is
>responsible for reconciling the language requirements with the oddities of
>the hardware. The only thing you can resonably expect to specify is guidelines.

Well, yes and no.  I understand that I can only specify (as you call them)
guidelines, but I have a reasonable expectation that the following will be
interpreted the same way on virtually every machine I care about (and even
some that I don't care about):

	struct s {
		unsigned f1:7;
		unsigned f2:9;
		unsigned f3:4;
		unsigned f4:12;
	};
-- 

// Ron Guilmette  -  C++ Entomologist
// Internet: rfg@ncd.com      uucp: ...uunet!lupine!rfg
// Motto:  If it sticks, force it.  If it breaks, it needed replacing anyway.

jimad@microsoft.UUCP (Jim ADCOCK) (08/20/90)

>I agree that with the current standard, any form of packing is allowed, as long
>as the compiler does *not* reorder fields *within* an access specified group
>(between "public","private","protected",or end-of-class-definition).
>
>E&S gives as rationale for this one constraint the compatibility with the
>(usually) coexisting C system. The idea is that a straight C-style "struct"
>declaration uses the same layout as when compiled by the C compiler (ANSI C
>is explicit about structure field ordering - reordering is forbidden).
>I don't think the *intent* of the C++ rule is to inhibit layout modifications
>in complex C++ class hierarchies.  Certainly the *wording* does allow it.
>

...given that "reorder" simply means that within an access specified group
members declared later have to have higher addresses.  A compiler faced
with derivation could pack-in members of the derived class into a base
class - as long as the order is not violated.  But its easy to imagine
how compilers *would* need to change member order in order to pack-into
a base class.  Again, what does the currect restriction on field ordering
buy a C++ user?  It certainly *does not* guarantee compatibility with 
C structures!  So why not remove this restriction, and leave it up to
C++ vendors to choose their C-backwards compatibility trade-offs?

roland@ai.mit.edu (Roland McGrath) (08/21/90)

In article <1234@lupine.NCD.COM> rfg@NCD.COM (Ron Guilmette) writes:

   Well, yes and no.  I understand that I can only specify (as you call them)
   guidelines, but I have a reasonable expectation that the following will be
   interpreted the same way on virtually every machine I care about (and even
   some that I don't care about):

	   struct s {
		   unsigned f1:7;
		   unsigned f2:9;
		   unsigned f3:4;
		   unsigned f4:12;
	   };

You are welcome to your reasonable expectations.  However, a conformant
compiler is not bound by them.  It is bound only by the contraints of the
standard.  We are discussing standards, not reasonable expectations.
--
	Roland McGrath
	Free Software Foundation, Inc.
roland@ai.mit.edu, uunet!ai.mit.edu!roland