Topic: pointer comparisons


Author: pkt@lpi.liant.com (Scott Turner)
Date: Mon, 25 Jan 1993 15:15:16 GMT
Raw View
In article <1993Jan20.173927.3061@vedge.com>, hendrik@vedge.com (Hendrik Boom) writes:
> In article <1993Jan11.190959.5305@lpi.liant.com>, pkt@lpi.liant.com (Scott Turner) writes:
> > If we have objects of a type which requires no state, e.g.
> >        struct B{} a, b;
> > then in order for them to have distinct identities an implementation
> > might allocate addresses that don't point to valid memory.  Again, no
> > storage is required.
>
> But if nonempty class C inherits from B, you have trouble comparing
> pointers to B with pointers to C that have been converted to B,
> unless you are careful that addresses of B's are outside of usable
> memory.

The trouble is negligible almost all of the time.  For example,
 struct C : B { int i; };
an implementation could convert "pointer to C" to "pointer to B" using the
same representation for both values.  If some pointers to B are pointing to
invalid memory, the comparison will work out just right.

In unusual cases such as Johan Bengtsson's example, the compiler can "waste"
a byte for the base class.
--
Prescott K. Turner, Jr.
Liant Software Corp. (developers of LPI languages)
959 Concord St., Framingham, MA 01701 USA    (508) 872-8700
UUCP: uunet!lpi!pkt                          Internet: pkt@lpi.liant.com




Author: linke@kirin (User - Bill Linke)
Date: 15 Jan 93 18:25:38 GMT
Raw View
In article <1993Jan11.190959.5305@lpi.liant.com>, pkt@lpi.liant.com (Scott Turner) writes:
> In article <1357@pivot-sts.sbi.com>, linke@sbi.sbi.com (User - Bill Linke) writes:
> > "Identity" means to me that we can
> > distinguish between two instances of a class
>
> Yes.
>
> > using only the attributes of the class itself
> > (i.e. without using anything from derived classes which the class
> > might happen to be a base class of). This in turn implies that somewhere
> > within each instance is some storage which conceptually could hold some
> > identifying value.  (Otherwise, there would be no way for two instances to
> > have different attributes.)
>
> How could this argument be meaningful?  If I have
>        int x=2, y=2;
> then in a typical implementation x and y are names of distinct objects,
> each of which has storage.  We can conceive of an atypical implementation
> which extra allows storage for identifying x and y with distinct keys. But for
> practical purposes all that's necessary is that x and y have different
> addresses.  Whether or not the storage can conceptually hold an identifying
> value doesn't matter.

I'm not sure we disagree so far.  I didn't mean that storage must actually be
used to hold distinguishing "keys"; only that the existence of the storage in
a class makes it both *possible* and *necessary* to apply the concept of
identity to the instances of the class.  For x and y as above, the pointers
&x and &y have different values, making it possible to identify them.  At the
same time, the fact that x and y can contain independent values makes it
necessary to identify them.

> If we have objects of a type which requires no state, e.g.
>        struct {} a, b;
> then in order for them to have distinct identities an implementation
> might allocate addresses that don't point to valid memory.  Again, no
> storage is required.

That's not good; then you can legally take an object's address which will
cause a core dump if it's dereferenced.  Or, there may be no invalid address
in some architectures.

> When I say that identity of objects is more of the essence than storage,
> here's the kind of thing I have in mind:
>        int x=2, y=2;
>        y = 3;
>        // Now the value of x is 2, not 3.
> Why?  Because the declaration defines 2 distinct objects named x and y.

Yes, but what I have in mind is that you can't do the above example with a
class which doesn't have storage members, because you can't store different
values into different instances.  So the motivation for distinguishing which
instance is x and which is y is gone.

> To explain it in terms of storage is more obscure.  One would
> have to say that the storage reserved for the definition of x
> is required not to overlap the storage reserved for the definition of y.
> And also mention that the state of the objects x and y resides in the
> storage reserved by their definitions.

Well, that doesn't seem so obscure; I think it's actually implicit in whatever
it means to implement "objects" in a program.  (I am still speaking only about
instances of a single class; not about instances of different classes.
Distinguishing between instances of different classes is done by the
type system, not by any storage address considerations.)

If you have:

 class X {} a, b, *p1;

you can currently determine whether p1 points to instance a or instance b.
What would be lost if this were not the case?  Only the ability to use
the instances as tokens, like "if I pass you &a, do something; if I pass &b,
do something else".  This is not much of a loss, and it can be recovered
just by defining a char member of X.

My thought is NOT that compilers *should* force a and b above to have the
same address, but rather that programmers *should not* be guaranteed
that a and b have different addresses if they have no storage members.
My hypothesis is that this restriction would not actually take anything away
(that could not be obtained trivially and without additional cost by simply
adding storage members whenever identity was important), while allowing the
compiler to avoid useless memory allocation when possible (such as in objects
derived from a class like X).

I would be interested to see counterexamples.

Bill Linke
linke@kirin.sbi.com




Author: maxtal@extro.ucc.su.OZ.AU (John MAX Skaller)
Date: Mon, 18 Jan 1993 16:37:22 GMT
Raw View
In article <1393@pivot-sts.sbi.com> linke@kirin (User - Bill Linke) writes:
>In article <1993Jan11.190959.5305@lpi.liant.com>, pkt@lpi.liant.com (Scott Turner) writes:
>
>My thought is NOT that compilers *should* force a and b above to have the
>same address, but rather that programmers *should not* be guaranteed
>that a and b have different addresses if they have no storage members.

>My hypothesis is that this restriction would not actually take anything away
>(that could not be obtained trivially and without additional cost by simply
>adding storage members whenever identity was important), while allowing the
>compiler to avoid useless memory allocation when possible (such as in objects
>derived from a class like X).
>
>I would be interested to see counterexamples.
>

 If all objects and subobjects occupy 1 byte or more
of storage, we can say:

 a==b <==> a is the same object as b (**)

(where a,b are the same type).

 If we remove the 1 byte requirement, a rule similar
to (**) exists, but it is not so easy to write. However,
(**) is preserved if some additional condition (NE) holds.
(NE==NonEmpty)

 f(T* a, T* b) {
  if(a==b) { ... }
 };

What can we say about 'f'? Well, most of the time, nothing.
No guarrantees, since it is very likely that T is an abstract
class with no data members.

We can say: IF the actual arguments obey NE, 'f' will work.
But that transfers responsibility for the correct
operation of 'f' to the caller.

So this is what we loose: the contractual arrangement between
'f' and its callers is changed, the onus is shifted
from the function 'f' to its callers.

For templates, this might be a nasty problem: certain
template functions would not work for all classes,
the same functions with the 1 byte rule would always work.

 class named { public: virtual char *name()const=0; };

 class ME : public named {
  char *name()const {return "ME";}
 };

 class YOU : public named {
  char *name()const {return "YOU";}
 };

 named *a1 = new ME;
 named *a2 = new YOU;

 template<class T>
 f(T *a1, T *a2) {
  if(a1==a2)cout<<"ME is YOU";
 };

 f(a1,a2);

WOOPS: an obscure bug. I forgot to put a dummy 'char' into
class 'named'. WORSE: it works fine on my compiler
(because of the vtable pointer). But on your compiler,
it flops (no vtable pointers in your implementation)

--
;----------------------------------------------------------------------
        JOHN (MAX) SKALLER,         maxtal@extro.ucc.su.oz.au
 Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
;------ SCIENTIFIC AND ENGINEERING SOFTWARE ---ph:  2 799 8223 --------




Author: pkt@lpi.liant.com (Scott Turner)
Date: Mon, 18 Jan 1993 18:24:52 GMT
Raw View
In article <1393@pivot-sts.sbi.com>, linke@kirin (User - Bill Linke) writes:
> In article <1993Jan11.190959.5305@lpi.liant.com>, pkt@lpi.liant.com (Scott Turner) writes:
> > If we have objects of a type which requires no state, e.g.
> >        struct {} a, b;
> > then in order for them to have distinct identities an implementation
> > might allocate addresses that don't point to valid memory.  Again, no
> > storage is required.
>
> That's not good; then you can legally take an object's address which will
> cause a core dump if it's dereferenced.

The fact that the implementation can avoid generating code to dereference
such a pointer is what makes it legitimate.  The generated code for
 a = b;
need not and must not _use_ the invalid addresses.

> Or, there may be no invalid address in some architectures.

In that case I believe storage should be required.

> > When I say that identity of objects is more of the essence than storage,
> > here's the kind of thing I have in mind:
> >        int x=2, y=2;
> >        y = 3;
> >        // Now the value of x is 2, not 3.
> > Why?  Because the declaration defines 2 distinct objects named x and y.
>
> Yes, but what I have in mind is that you can't do the above example with a
> class which doesn't have storage members, because you can't store different
> values into different instances.  So the motivation for distinguishing which
> instance is x and which is y is gone.

Let's go back to your "Yes".  The reason the code works the way it
does is that x and y are distinct, independent objects.  Do we reason
that x and y require storage?  No!

You're saying to consider again the similar example
 struct {} a, b;
which cannot be extended to work just like the x,y example.  But we can
go so far with it as to say that a and b are distinct, independent objects.
If you don't say that, then you must reason differently about x and y as
well.  It's possible, but ugly.

> > To explain it in terms of storage is more obscure.  One would
> > have to say that the storage reserved for the definition of x
> > is required not to overlap the storage reserved for the definition of y.
> > And also mention that the state of the objects x and y resides in the
> > storage reserved by their definitions.
>
> Well, that doesn't seem so obscure; I think it's actually implicit in whatever
> it means to implement "objects" in a program.

Yes, and let's keep it as implicit as possible.  In the definition of a
basic, clean language like Scheme, object identity is an explicit fundamental,
and storage is totally implicit.

> If you have:
>
>  class X {} a, b, *p1;
>
> you can currently determine whether p1 points to instance a or instance b.
> What would be lost if this were not the case?  Only the ability to use
> the instances as tokens, like "if I pass you &a, do something; if I pass &b,
> do something else".  This is not much of a loss, and it can be recovered
> just by defining a char member of X.

I should define a char member in my class, just to make its objects
distinguishable?  That's quite an oblique way of saying so.
Moreover, an implementation would be fully within its rights to
discover that the char member is never referenced,
and allocate no storage for the class anyway.
--
Scott Turner
Liant Software Corp. (developers of LPI languages)
959 Concord St., Framingham, MA 01701 USA    (508) 872-8700
UUCP: uunet!lpi!pkt                          Internet: pkt@lpi.liant.com





Author: hendrik@vedge.com (Hendrik Boom)
Date: 20 Jan 93 17:39:27 GMT
Raw View
pkt@lpi.liant.com (Scott Turner) writes:
: In article <1393@pivot-sts.sbi.com>, linke@kirin (User - Bill Linke) writes:
: > In article <1993Jan11.190959.5305@lpi.liant.com>, pkt@lpi.liant.com (Scott Turner) writes:
: > > If we have objects of a type which requires no state, e.g.
: > >        struct B{} a, b;
: > > then in order for them to have distinct identities an implementation
: > > might allocate addresses that don't point to valid memory.  Again, no
: > > storage is required.

(Not quite a quote -- I named the struct 'B' so I could talk about it.)
There's no problem with allocating no storage for struct B, and using
addresses into deep space or real space to point to B's. Even if user code
dereferences it or copies it this can be done with no action.
But it nonempty class C inherits from B, you have trouble comparing
pointers to B with pointers to C that have been converted to B,
unless you are careful that addresses of B's are outside of usable
memory.
:
--
-------------------------------------------------------
Try one or more of the following addresses to reply.
at work: hendrik@vedge.com,  iros1!vedge!hendrik
at home: uunet!ozrout!topoi!hendrik




Author: pkt@lpi.liant.com (Scott Turner)
Date: Fri, 8 Jan 1993 15:01:13 GMT
Raw View
In article <9300812.4038@mulga.cs.mu.OZ.AU>, fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON) writes:
> maxtal> One consequence: abstract objects can exist.
> maxtal>
> maxtal> Corollary: such objects have states not accessible via
> maxtal> the contiguous storage allocated for them.
> maxtal> (via virtual function calls)
>
> To me, an abstract class is a class without any actual objects of that class,
> so the idea of an "abstract object" seems to be an oxymoron. Similarly, the
> idea that an object can have "state" that is not stored in its "storage" seems
> to make a mockery of the whole idea of "storage".

Since John MAX Skaller was drawing inferences from my belief that base
class subobjects are objects, I want to go on record.  I agree that
"abstract objects" can exist, but the corollary does not follow.

The easiest way to find an actual object of an abstract class is from
the point of view of a constructor for an abstract class.  What's the
type of the object pointed to by 'this'?  The type of that object is the
abstract class, no question about it.

In the corollary, John MAX Skaller is referring to a different scenario
in which the object of abstract class is a base class subobject of a
fully-constructed enclosing object.  He concludes that the state of the
enclosing object is part of the state of the base class subobject.  The
reasoning has something to do with the additional state being accessible via
virtual function calls, but there's a big hole in it.  Virtual functions
(and non-virtual functions) can access any number of other objects,
independent of the object for which they are called.
--
Prescott K. Turner, Jr.
Liant Software Corp. (developers of LPI languages)
959 Concord St., Framingham, MA 01701 USA    (508) 872-8700
UUCP: uunet!lpi!pkt                          Internet: pkt@lpi.liant.com




Author: maxtal@extro.ucc.su.OZ.AU (John MAX Skaller)
Date: Fri, 8 Jan 1993 18:11:00 GMT
Raw View
In article <1993Jan8.150113.1643@lpi.liant.com> pkt@lpi.liant.com (Scott Turner) writes:
>In article <9300812.4038@mulga.cs.mu.OZ.AU>, fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON) writes:
>> maxtal> One consequence: abstract objects can exist.
>> maxtal>
>> maxtal> Corollary: such objects have states not accessible via
>> maxtal> the contiguous storage allocated for them.
>> maxtal> (via virtual function calls)
>>
>Since John MAX Skaller was drawing inferences from my belief that base
>class subobjects are objects, I want to go on record.  I agree that
>"abstract objects" can exist, but the corollary does not follow.
>
>In the corollary, John MAX Skaller is referring to a different scenario
>in which the object of abstract class is a base class subobject of a
>fully-constructed enclosing object.  He concludes that the state of the
>enclosing object is part of the state of the base class subobject.  The
>reasoning has something to do with the additional state being accessible via
>virtual function calls, but there's a big hole in it.  Virtual functions
>(and non-virtual functions) can access any number of other objects,
>independent of the object for which they are called.

 Um: globally defined objects yes.
 What else?

 Well, you can have a pointer to another object in an object,
and access the other object via the pointer, right?

 But this doesn't count :-) The pointer is part
of the state of the object, and thus so is the object pointed
to (and so on recursively)

 In any case, I will fill in the hole:

 class X {
 public:
  virtual int f()=0;
 };

 class Y : public X {
  int i;
 public:
  int f() {return i;}
  void setint(int j) {i=j;}
 };

In this single example, the abstract subobject X in Y has
public states  of measure 'int'. (There are 'int' values).
Yet X has private storage of measure zero.

Where is the hole in this argument? Answer: X has a vtble
pointer!  So I'm wrong. I concede.

--
;----------------------------------------------------------------------
        JOHN (MAX) SKALLER,         maxtal@extro.ucc.su.oz.au
 Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
;--------------- SCIENTIFIC AND ENGINEERING SOFTWARE ------------------




Author: linke@sbi.sbi.com (User - Bill Linke)
Date: 9 Jan 93 01:22:33 GMT
Raw View
In article <1993Jan7.145511.718@lpi.liant.com>, pkt@lpi.liant.com (Scott Turner) writes:
>
> Identity of objects is more of the essence than storage, so it would be
> problematic to define the former in terms of the latter.

Even this is not so clear, is it?  "Identity" means to me that we can
distinguish between two instances of a class using only the attributes of the
class itself (i.e. without using anything from derived classes which the class
might happen to be a base class of).  This in turn implies that somewhere
within each instance is some storage which conceptually could hold some
identifying value.  (Otherwise, there would be no way for two instances to
have different attributes.)

In other words, it makes more sense to me to restrict the concept of
"object identity" as being meaningful *only* when applied to instances of
a class which contains storage members within itself or its ancestors.
(Then it doesn't really matter whether pointers to non-storage class objects
compare equal or not, because there is no "identity" to be found that way
in the first place.)

Bill Linke
linke@kirin.sbi.com




Author: maxtal@extro.ucc.su.OZ.AU (John MAX Skaller)
Date: Sat, 9 Jan 1993 19:01:36 GMT
Raw View
In article <1993Jan7.025948.23000@lucid.com> jss@lucid.com (Jerry Schwarz) writes:
> struct X { int i ; } ;
> struct D : public X {
>  X xInD ;
> } ;
>
 This example is not contentious. But:

 struct X { virtual int f()=0; };

 struct Y : public X { int i; int f(){return i;} };

 struct D : public X {
  Y y;
  int j;
  int f(){return j;}
 };

Now this example IS contentious. For an implementation not using
vtble pointers, the abstract class X might occupy 0 bytes.

Then:

 D d;
 d.i=1;
 d.j=2;

 X* p1=&d; // base subobject
 X* p2=&d.y; // included object base subobject
 p1==p2; // OH!
 cout<<"1=="<<p1->f();
 cout<<"2=="<<p2->f();

and I dont see how such an implementation could actually work
UNLESS at least the language require that abstract base
subobjects occupy at least one byte.

I dont believe this is required by the ARM, clearly it should be.

--
;----------------------------------------------------------------------
        JOHN (MAX) SKALLER,         maxtal@extro.ucc.su.oz.au
 Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
;--------------- SCIENTIFIC AND ENGINEERING SOFTWARE ------------------




Author: pkt@lpi.liant.com (Scott Turner)
Date: Mon, 11 Jan 1993 19:09:59 GMT
Raw View
In article <1357@pivot-sts.sbi.com>, linke@sbi.sbi.com (User - Bill Linke) writes:
> In article <1993Jan7.145511.718@lpi.liant.com>, pkt@lpi.liant.com (Scott Turner) writes:
> >
> > Identity of objects is more of the essence than storage, so it would be
> > problematic to define the former in terms of the latter.
>
> Even this is not so clear, is it?

It may be worth discussing.

> "Identity" means to me that we can
> distinguish between two instances of a class

Yes.

> using only the attributes of the class itself
> (i.e. without using anything from derived classes which the class
> might happen to be a base class of). This in turn implies that somewhere
> within each instance is some storage which conceptually could hold some
> identifying value.  (Otherwise, there would be no way for two instances to
> have different attributes.)

How could this argument be meaningful?  If I have
       int x=2, y=2;
then in a typical implementation x and y are names of distinct objects,
each of which has storage.  We can conceive of an atypical implementation
which extra allows storage for identifying x and y with distinct keys.  But for
practical purposes all that's necessary is that x and y have different
addresses.  Whether or not the storage can conceptually hold an identifying
value doesn't matter.

If we have objects of a type which requires no state, e.g.
       struct {} a, b;
then in order for them to have distinct identities an implementation
might allocate addresses that don't point to valid memory.  Again, no
storage is required.


When I say that identity of objects is more of the essence than storage,
here's the kind of thing I have in mind:
       int x=2, y=2;
       y = 3;
       // Now the value of x is 2, not 3.
Why?  Because the declaration defines 2 distinct objects named x and y.

To explain it in terms of storage is more obscure.  One would
have to say that the storage reserved for the definition of x
is required not to overlap the storage reserved for the definition of y.
And also mention that the state of the objects x and y resides in the
storage reserved by their definitions.
--
Prescott K. Turner, Jr.
Liant Software Corp. (developers of LPI languages)
959 Concord St., Framingham, MA 01701 USA    (508) 872-8700
UUCP: uunet!lpi!pkt                          Internet: pkt@lpi.liant.com




Author: pkt@lpi.liant.com (Scott Turner)
Date: Mon, 11 Jan 1993 19:23:37 GMT
Raw View
In article <1993Jan8.181100.10727@ucc.su.OZ.AU>, maxtal@extro.ucc.su.OZ.AU (John MAX Skaller) writes:
> In article <1993Jan8.150113.1643@lpi.liant.com> pkt@lpi.liant.com (Scott Turner) writes:
> >> maxtal> Corollary: such objects have states not accessible via
> >> maxtal> the contiguous storage allocated for them.
> >> maxtal> (via virtual function calls)
> >>
> >Since John MAX Skaller was drawing inferences from my belief that base
> >class subobjects are objects, I want to go on record.  I agree that
> >"abstract objects" can exist, but the corollary does not follow.
>
>  Well, you can have a pointer to another object in an object,
> and access the other object via the pointer, right?
>
>  But this doesn't count :-) The pointer is part
> of the state of the object, and thus so is the object pointed
> to (and so on recursively)
>
>  In any case, I will fill in the hole:
>
>  class X {
>  public:
>   virtual int f()=0;
>  };
>
>  class Y : public X {
>   int i;
>  public:
>   int f() {return i;}
>   void setint(int j) {i=j;}
>  };
>
> In this single example, the abstract subobject X in Y has
> public states  of measure 'int'. (There are 'int' values).
> Yet X has private storage of measure zero.
>
> Where is the hole in this argument? Answer: X has a vtble
> pointer!

Yes, that's close to the way I look at it.  X declares a virtual function f
with the consequence that any subobject of type X includes
some indication of how f has been overridden in constructing the enclosing
object.   f may refer to non-contiguous storage, but that's no more
problematic than if X had a pointer member which refers to non-contiguous
storage.
--
Prescott K. Turner, Jr.
Liant Software Corp. (developers of LPI languages)
959 Concord St., Framingham, MA 01701 USA    (508) 872-8700
UUCP: uunet!lpi!pkt                          Internet: pkt@lpi.liant.com





Author: jimad@microsoft.com (Jim Adcock)
Date: 12 Jan 93 22:18:22 GMT
Raw View
In article <1993Jan7.025948.23000@lucid.com> jss@lucid.com (Jerry Schwarz) writes:
|We seem to be arguing as much about the meaning of terms
|and the model that should be used to define the semantics of C++
|as we are about what the semantics are or should be.  I don't
|think this is a fruitful discussion, so I'm going to take a different
|tack and present my point of view solely in terms of code.

So what's the point?  I can accept your code and still reserve
judgement to disagree with any implications you might try to push
forward from you particular code example.  One cannot reasonably
standardize from example.  ANSI-C realized this and avoided the
mistake of providing a conformance suite.

We do not have to agree on terms nor model.  If you put forward a set
of terms and model that I [and others] agree has to be acceptibly
implemented by all conforming C++ compilers using the "as-if" rule,
then you have won that right to that  particular model, whether or not I
"like" your particular model or terms.

An example starting point might be to put forth a simple object model
that only encompases SI and non-empty base classes.  If you can first
get "everybody" to buy into that particular model, then you might have
a starting point to expand into what model[s] compilers are allowed
to implement in regards MI, vfuncs, vbases, abstract bases, etc.





Author: pkt@lpi.liant.com (Scott Turner)
Date: Sat, 2 Jan 1993 21:15:38 GMT
Raw View
In article <9300300.29900@mulga.cs.mu.OZ.AU> (Fergus James HENDERSON)
writes:
> pkt@lpi.liant.com (Scott Turner) writes:
> > But note that section 1.3 of the working paper (The C++ Memory Model) says:
> >
> >        The constructs in a C++ program create, refer to, access, and
> >        manipulate objects in memory.  Each object (except bit-fields)
> >        occupies one or more contiguous bytes.

> > How much freedom does it leave a compiler to optimize away
> > an object's bytes (by application of the "as if" rule, etc.)?
> > I think a good deal of freedom, [...]
>
> Perhaps, but
>   "The _sizeof_ operator yields the size, in bytes, of its operand".
> I think that it would be a very brave compiler-writer whose compiler
> made it unreliable to write() an object and read() it in again, because
>   read(fd, &object, sizeof object);
> randomly trashes the bytes past the end of the object.

Yes, that's a relevant point.  But it does not mean that 1.3 restricts
the compiler's ability to optimize, because 5.3.2 already says that
sizeof yields a result of at least 1.  So I'll hold to my conclusion
that the above excerpt from 1.3 belongs in the C++ standard.

> However, the murkiness of the concepts involved in "object identity" appears
> to make it very difficult or impossible to provide such guarantees
[about pointer comparison].

On the contrary, this lengthy discussion has regularly debated how pointer
comparison should work in relation to whether the pointers refer to the
same object.  Despite murkiness in the ARM, our shared concept of object
identity has held up.  I can think of only one example in which there was
disagreement about object identity, and in that case it seemed to be
cleared up quickly.

> My own metaphysical perspective is that subobjects are
> by definition not objects in their own right

My perspective, that subobjects are objects, has stood up to 4 years of
working with the C++ reference manual and working paper.  It fits in better
with the common application of the prefix "sub".  For example, a subset is
so called not because it belongs to a set, but because it _is_ a set
subordinate to another set.  If a subset were not properly a set, then
it never would have been called a subset.  Likewise "subobject" would be
a poor term if it did not refer to an object.

Furthermore, the ARM calls subobjects objects.  Look toward the end of
10.1.1 (p. 206) "the same object representing the base class".
--
Prescott K. Turner, Jr.
Liant Software Corp. (developers of LPI languages)
959 Concord St., Framingham, MA 01701 USA    (508) 872-8700
UUCP: uunet!lpi!pkt                          Internet: pkt@lpi.liant.com




Author: jss@lucid.com (Jerry Schwarz)
Date: Mon, 4 Jan 93 20:06:25 GMT
Raw View
In article <9300300.29980@mulga.cs.mu.OZ.AU>, fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON) writes:
|>
|> As I noted in a previous article, I don't agree with Scott Turners belief
|> that base class subobjects are objects. To me, these are absurd
|> consequences, and thus I conclude that the assumption used in deriving them
|> must be flawed.

Perhaps fjh would specify some of these "absurd consequences".  I
agree with Scott.  Base class subobjects are objects. Anything else
would be incomprehensible to me.

Given a (pointer to) a base class subobject it is sometimes
necessary to find the containing derived class object. In the
past this conversion operation was necessary only for virtual
function call, but the semantics of C++ are best understood
by considering it a non-trivial operation.  In the future,
if RTTI is accepted, this may become an explicit user invocable
operation in some contexts.

   -- Jerry Schwarz






Author: jimad@microsoft.com (Jim Adcock)
Date: 05 Jan 93 00:25:47 GMT
Raw View
In article <1992Dec31.152700.21521@lpi.liant.com> pkt@lpi.liant.com (Scott Turner) writes:
|"Same type" is a term used repeatedly in the ARM.  It has its limitations,
|but is adequate for my purpose.

"Defined by use"?  How about a "real" definition?

|"Point to distinct objects" would imply two things:
|(1) Each operand points to one object (not a function, and is not null or
|    invalid).
|(2) The objects are not identicial.  I hope to say more about this in
|    another article.

Presumably distinct objects are not identical and objects that are not
identical are distinct? ;-)  I hope you will tell us more about this
later!

|The case I'm concerned with is limited to "objects of that type", i.e.
|each operand points to a properly constructed object of type A (which may
|be a subobject of an enclosing object constructed with a different type).

Define "subobject" and "enclosing now."

|"Compare not equal" is self-evident; the C standard uses the same kind
|of wording.  It means that the result of the operator != is 1 (TRUE) and
|the result of the operator == is 0 (FALSE).

"Compare not equal" was self-evident in C because C has a clearly stated
object model with well-defined object identity.  No such luck in C++.





Author: jimad@microsoft.com (Jim Adcock)
Date: 05 Jan 93 00:38:19 GMT
Raw View
In article <1992Dec31.170223.21637@lpi.liant.com> pkt@lpi.liant.com (Scott Turner) writes:
|In article <1992Dec30.184614.5551@microsoft.com> (Jim Adcock) writes:
|> Can you define your terms for us?
|
|> "distinct objects" means what?
|It means the objects are not identical.

Which only leaves the issue of object identity.

|I was going to say that the standard for C++ needs to spell out what it
|means for object to be identical in a better way than just by
|     1. An object is a region of storage.
|     2. A definition or a new expression reserves the storage
|        needed by the object.
|These two rules leave things much more loose than necessary when objects
|require no storage.  But note that section 1.3 of the working paper

Lots of other things remain unclear.  If a definition or a new expressions
reserves the storage needed by an object, when and when not can other
objects use this storage? [this is a trick question ;-]

| The constructs in a C++ program create, refer to, access, and
| manipulate objects in memory.  Each object (except bit-fields)
| occupies one or more contiguous bytes.

Can other objects occupy those bytes?

|I missed this a while back when enumerating the features of the ARM and
|working paper which supported
|> The desired feature is:
|>     Two pointers to the same type, which point to distinct objects
|>     of that type, compare not equal.
|This excerpt from 1.3 gives the general rule I was seeking, because
|objects can be distinguished by the storage reserved for them.

That depends on what exactly is meant by "two pointers to the same type,
which point to distinct objects of that type"

At the very least "same type" and "distinct objects" remain poorly defined
in this discussion.

| Does it govern base class subobjects?  As I understand it,
| base class subobjects are objects, and hence are covered.

As I understand it, base class subobjects are NOT objects, because
in the case of virtual bases this would violate the contiguous-bytes
rule.  Further it would violate the overlapping objects rule.  Also,
it would imply that deriving some class from some base class can cause
a change in behavior of an "object" of that base class, and such flies
in the face of reason [IMHO].  So I believe "base clas subobjects" are
simply distinct views on one object.  One object can have multiple
identities then, depending on how it is viewed -- as demonstrated in
my multiple-base example.





Author: jimad@microsoft.com (Jim Adcock)
Date: 05 Jan 93 00:49:50 GMT
Raw View
|In article <9300300.29900@mulga.cs.mu.OZ.AU> (Fergus James HENDERSON)
|writes:
|>   "The _sizeof_ operator yields the size, in bytes, of its operand".
|> I think that it would be a very brave compiler-writer whose compiler
|> made it unreliable to write() an object and read() it in again, because
|>   read(fd, &object, sizeof object);
|> randomly trashes the bytes past the end of the object.

Yet in the example I posted of a vacuous baseclass and a first member
of the base type, no such problem exists.  Sizeof == 1.  Bytes required
within the structure by the baseclass "subobject" == 0.
And read(fd, &object, sizeof object) *does not* randomly trash the bytes
past the end of the object.  Further, this implementation is consistent
with the ANSI-C base document *mandate* that implementations do not
introduce padding into a structure before the first member.
ANSI-C mandates that the address of the first member be the same as the
address of the structure.  Unless there is overriding reason in the ARM
why this cannot be the case, then the ANSI-C base document ought to be
respected.  My claim is that there is no such overriding ARM requirement.





Author: maxtal@extro.ucc.su.OZ.AU (John MAX Skaller)
Date: Tue, 5 Jan 1993 06:03:32 GMT
Raw View
In article <1993Jan4.200625.5680@lucid.com> jss@lucid.com (Jerry Schwarz) writes:
>In article <9300300.29980@mulga.cs.mu.OZ.AU>, fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON) writes:
>|>
>|> As I noted in a previous article, I don't agree with Scott Turners belief
>|> that base class subobjects are objects. To me, these are absurd
>|> consequences, and thus I conclude that the assumption used in deriving them
>|> must be flawed.
>
>Perhaps fjh would specify some of these "absurd consequences".  I
>agree with Scott.  Base class subobjects are objects. Anything else
>would be incomprehensible to me.

 If an 'object' is something whose attributes are computed
from its internal state, then clearly subobjects need not
be objects. In the sense an object is 'self contained',
an 'instance' of an abstract class cannot be an object.

 In particular, the semantics of a virtual function
in a subobject are not encapsulated in the subobject class
definition (especially for pure virtuals :-)

 I suspect that only 'complete objects' can really
qualify to be genuine, true blue objects.
Which is not to say they shouldn't support object identity.
However the current rules do seem to provide object
identity --- if at all -- only for complete objects.

 Since the current system does not provide subobjects
with object identity, nor do subobjects enjoy the same
'completeness' properties of complete objects, it is difficult
to argue that because subobjects are objects they ought to
have all the properties of objects---the converse argument
that because subobjects dont have the properties of
objects, they aren't objects is equally compelling and
equally meaningful (or devoid of such).

 Is the issue not simple: either we waste bytes
to obtain object identity for subobjects or we dont.

If we do .. we waste bytes.

If we dont, then comparison of pointers in a subroutine

 f(X* x1, X* x2) { if(x1==x2) ... }

is reliable only if f is called with complete object pointers.
I.e.: there is a precondition, and one that might be hard for
the caller to ensure.

--
;----------------------------------------------------------------------
        JOHN (MAX) SKALLER,         maxtal@extro.ucc.su.oz.au
 Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
;--------------- SCIENTIFIC AND ENGINEERING SOFTWARE ------------------




Author: pkt@lpi.liant.com (Scott Turner)
Date: Tue, 5 Jan 1993 20:32:37 GMT
Raw View
In article <1993Jan05.003819.12515@microsoft.com>, jimad@microsoft.com (Jim Adcock) writes:
> In article <1992Dec31.170223.21637@lpi.liant.com> pkt@lpi.liant.com (Scott Turner) writes:
> |In article <1992Dec30.184614.5551@microsoft.com> (Jim Adcock) writes:
> |> Can you define your terms for us?
> |
> |> "distinct objects" means what?
> |It means the objects are not identical.
>
> Which only leaves the issue of object identity.

Which is where I wish to leave the issue, willing to trust for the present
that objects which I wish distinct will be distinct.  This trust rests on

      1. The status of distinct objects as objects.

      2. The guarantee of 1.3 that an object occupies at least 1 byte
         of storage.

      3. The implementation's duty to reserve storage for objects.

If anyone can think of an example in which I am likely to be diappointed
and end up surprised with indistinct objects, I'd be grateful to see it.
(Implementations which don't do their duty aren't necessarily a surprise.)

> Lots of other things remain unclear.  If a definition or a new expressions
> reserves the storage needed by an object, when and when not can other
> objects use this storage? [this is a trick question ;-]

I was responding to clarify a possible feature of pointer comparison.
To clarify everything is beyond me.

> | The constructs in a C++ program create, refer to, access, and
> | manipulate objects in memory.  Each object (except bit-fields)
> | occupies one or more contiguous bytes.
>
> Can other objects occupy those bytes?

Yes.  An _other_ object is distinct, not identical to the object with which
it shares storage.  For example, given
 int x[9];
then the bytes occupied by the objects x and x[2] overlap.

> | Does it govern base class subobjects?  As I understand it,
> | base class subobjects are objects, and hence are covered.
>
> As I understand it, base class subobjects are NOT objects, because [...]
> Further it would violate the overlapping objects rule.

What rule?

> Also, it would imply that deriving some class from some base class can cause
> a change in behavior of an "object" of that base class, and such flies
> in the face of reason [IMHO].

If some class declares a virtual function, it's asking to be changeable.
--
Prescott K. Turner, Jr.
Liant Software Corp. (developers of LPI languages)
959 Concord St., Framingham, MA 01701 USA    (508) 872-8700
UUCP: uunet!lpi!pkt                          Internet: pkt@lpi.liant.com






Author: jss@lucid.com (Jerry Schwarz)
Date: Tue, 5 Jan 93 22:23:00 GMT
Raw View
In article <1993Jan5.060332.5262@ucc.su.OZ.AU>, maxtal@extro.ucc.su.OZ.AU (John MAX Skaller) writes:
|> In article <1993Jan4.200625.5680@lucid.com> jss@lucid.com (Jerry Schwarz) writes:
|> >In article <9300300.29980@mulga.cs.mu.OZ.AU>, fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON) writes:
|> >|>
|> >|> As I noted in a previous article, I don't agree with Scott Turners belief
|> >|> that base class subobjects are objects. To me, these are absurd
|> >|> consequences, and thus I conclude that the assumption used in deriving them
|> >|> must be flawed.
|> >
|> >Perhaps fjh would specify some of these "absurd consequences".  I
|> >agree with Scott.  Base class subobjects are objects. Anything else
|> >would be incomprehensible to me.

|>
|>  If an 'object' is something whose attributes are computed
|> from its internal state, then clearly subobjects need not
|> be objects. In the sense an object is 'self contained',
|> an 'instance' of an abstract class cannot be an object.
|>


I completely disagree with the premise. Maybe we are using
"object" in different senses.  An important "attribute" of
an object (defined as a region of storage by both the C
standard and the ARM) is its relationship to other regions
of storage.

To take a simple example

 int a[10];

It is an important attribute of a[2] that it is the 3rd
subobject of a 10 element array.

struct S { int i ; int j ; } s ;

It is an important attribute of s.j that it is the "j" subobject
of s.

If you prefer to use a word other than "subobject" to describe
this relationship, I suppose you can do so.  But however you
describe it, it is an important concept in the semantics of C++.

Perhaps you disagree that a[2] or s.j are "objects"?  If so
then I think you should reconsider your terminology.  "object"
is the term used throughout the C standard and ARM for this
concept.

And a "base subobject" bears an analogous relationship to the
containing object(s).

|>  In particular, the semantics of a virtual function
|> in a subobject are not encapsulated in the subobject class
|> definition (especially for pure virtuals :-)
|>

That is correct.  A virtual function call (as I noted in
another item I posted recently) requires taking a base object
and finding the appropriate derived object containing the base
object.  This can't be done based solely on the contents of
the base object.


|>  I suspect that only 'complete objects' can really
|> qualify to be genuine, true blue objects.
|> Which is not to say they shouldn't support object identity.
|> However the current rules do seem to provide object
|> identity --- if at all -- only for complete objects.

The data members of a class are also subobjects according to
my understanding.  You're even disallowing that?

|>
|>  Is the issue not simple: either we waste bytes
|> to obtain object identity for subobjects or we dont.
|>

I can phrase begging questions too. Either we implement the
language correctly or we fudge, save a byte and hope nobody
notices.

Apparently the issue isn't simple.  There is a fundamental
disagreement about the semantics of C++.  If that disagreement
is resolved it will tell us what is a legal implementation.
We can use the consequences for storage layout and sizes as
a criterion for determining the semantics, but it can't be
the sole criterion.

   -- Jerry Schwarz








Author: maxtal@extro.ucc.su.OZ.AU (John MAX Skaller)
Date: Wed, 6 Jan 1993 17:51:24 GMT
Raw View
In article <1993Jan5.203237.28304@lpi.liant.com> pkt@lpi.liant.com (Scott Turner) writes:
>
>Which is where I wish to leave the issue, willing to trust for the present
>that objects which I wish distinct will be distinct.  This trust rests on
>
>      1. The status of distinct objects as objects.
>
>      2. The guarantee of 1.3 that an object occupies at least 1 byte
>         of storage.

 What does 1.3 say? (I do not have the WP, only the ARM.
The ARM appears to guarrantee ONLY that operator new() must
yield distinct addresses, it says nothing about subobjects
or included objects that I can find. Have I missed something?)

--
;----------------------------------------------------------------------
        JOHN (MAX) SKALLER,         maxtal@extro.ucc.su.oz.au
 Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
;--------------- SCIENTIFIC AND ENGINEERING SOFTWARE ------------------




Author: maxtal@extro.ucc.su.OZ.AU (John MAX Skaller)
Date: Wed, 6 Jan 1993 18:49:07 GMT
Raw View
In article <1993Jan5.222300.29535@lucid.com> jss@lucid.com (Jerry Schwarz) writes:
>In article <1993Jan5.060332.5262@ucc.su.OZ.AU>, maxtal@extro.ucc.su.OZ.AU (John MAX Skaller) writes:

>|>  If an 'object' is something whose attributes are computed
>|> from its internal state, then clearly subobjects need not
>|> be objects. In the sense an object is 'self contained',
>|> an 'instance' of an abstract class cannot be an object.
>|>
>
>
>I completely disagree with the premise.

 Fair enough. If you want subobjects to be counted
as objects, you must!

>Maybe we are using
>"object" in different senses.  An important "attribute" of
>an object (defined as a region of storage by both the C
>standard and the ARM) is its relationship to other regions
>of storage.

 Maybe we are using "attribute" in different senses.
For me an attribute is a function returning boolean with
one argument.

 A relation returns boolean and has two arguments,
and it is not an attribute: an attribute is an intrinsic
property, needing no reference to the outside world
or other thing for its evaluation.
>
>To take a simple example
>
> int a[10];
>
>It is an important attribute of a[2] that it is the 3rd
>subobject of a 10 element array.

 It is a relation between a and a[2] at best.
>
>struct S { int i ; int j ; } s ;
>
>It is an important attribute of s.j that it is the "j" subobject
>of s.

 A relation. Not a property of 's.j' which as an object
is merely an 'int'. Yes, as an object that 'int' has an address
distinct from other ints. Two different 'int's may have the same
value, and the same 'int' object may have different values
at different times.

 The address of the int object 's.j' is an attribute
of 's.j' as an object. Its relation as an object to 's'
is not an attribute of 's.j'.
>
>Perhaps you disagree that a[2] or s.j are "objects"?

 No, I agree they are objects.

 I do not necessarily agree their relationship
to other objects is a property they possess as objects,
however. In fact, I might contend that maintaining
this is a violation of the notion of encapsulation.
An objects relation to some other object is an intrinsic
property of the object if, and only if, it contains
a pointer (or reference) to that other object.

 Otherwise it is a property of the system,
not the individual object.

>And a "base subobject" bears an analogous relationship to the
>containing object(s).

 Not necessarily: contained objects are self contained.
Base subobjects are not always self contained: they are
never self contained if they have pure virtual members.
>
>|>  I suspect that only 'complete objects' can really
>|> qualify to be genuine, true blue objects.
>|> Which is not to say they shouldn't support object identity.
>|> However the current rules do seem to provide object
>|> identity --- if at all -- only for complete objects.
>
>The data members of a class are also subobjects according to
>my understanding.  You're even disallowing that?

 No, the ARM is disallowing that if you require
object identity as a property of objects. That identity
being determined by pointer comparisons.

 Unless I missed something.

 class X {};
 class Y { X x1, x2; }

Nothing says &x1 != &x2. Perhaps it should. Perhaps I'm wrong.

>|>  Is the issue not simple: either we waste bytes
>|> to obtain object identity for subobjects or we dont.
>|>
>
>I can phrase begging questions too. Either we implement the
>language correctly or we fudge, save a byte and hope nobody
>notices.

 Why is this a 'begging question'? That the bytes
would be wasted in some cases is beyond dispute.
Whether this is important for other than complete objects
I am not sure.

>
>Apparently the issue isn't simple.  There is a fundamental
>disagreement about the semantics of C++.  If that disagreement
>is resolved it will tell us what is a legal implementation.
>We can use the consequences for storage layout and sizes as
>a criterion for determining the semantics, but it can't be
>the sole criterion.
>
 As I said I am not sure which choice to make.
I did not intend to claim *making* the choice would be
easy, only that the alternatives are just TWO, and
both of them are well defined and understood.

 Am I wrong in this? Are there other alternatives?

[My claim is marginally non-trivial: I am disallowing wasting
bytes in included objects but optimising them away in base
subobjects. That would be a third option. But it seems
to me that that does not give proper object identity
semantics to pointer comparisons, and also wastes bytes.
So it is not a good option.]

--
;----------------------------------------------------------------------
        JOHN (MAX) SKALLER,         maxtal@extro.ucc.su.oz.au
 Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
;--------------- SCIENTIFIC AND ENGINEERING SOFTWARE ------------------




Author: jimad@microsoft.com (Jim Adcock)
Date: 06 Jan 93 19:47:10 GMT
Raw View
In article <1993Jan5.203237.28304@lpi.liant.com> pkt@lpi.liant.com (Scott Turner) writes:
|Yes.  An _other_ object is distinct, not identical to the object with which
|it shares storage.  For example, given
| int x[9];
|then the bytes occupied by the objects x and x[2] overlap.

I still don't understand your [undefined] concepts of "distinctness"
and "identity"

Granted that in this case x and x[0] are different objects, and share
storage.  Yet x and &(x[0]) have identical addresses.  Likewise ANSI-C
requires a struct and its first member to have identical addresses.
So I still don't understand your [unstated] definitions of the terms
you are using.





Author: jimad@microsoft.com (Jim Adcock)
Date: 06 Jan 93 20:16:47 GMT
Raw View
In article <1993Jan5.222300.29535@lucid.com> jss@lucid.com (Jerry Schwarz) writes:
|I completely disagree with the premise. Maybe we are using
|"object" in different senses.  An important "attribute" of
|an object (defined as a region of storage by both the C
|standard and the ARM) is its relationship to other regions
|of storage.

Whether or not people agree with a particular set of premises it might
be nice to start with at least *one* explicitly stated set of premises
and terminology that remains consistent with the clearly stated requirements
of the ARM, and which remains relatively consistent with most parts of
most implementations of the ARM.

|To take a simple example
|
| int a[10];
|
|It is an important attribute of a[2] that it is the 3rd
|subobject of a 10 element array.
|
|struct S { int i ; int j ; } s ;
|
|It is an important attribute of s.j that it is the "j" subobject
|of s.

I wouldn't call either of these things "subobjects."  I would call then
elements and members respectively, and would require that elements and
members are indeed "objects" which follow all the rules of "objects"
whatever those rules might be.  I don't think this is much of an issue --
excepting deciding on what rules all "objects" must obey.  A bigger
issue seems to be whether or not inherited base parts represent
"objects" in their own rights, or are they not "objects."  If
they are "objects" then they must follow all the rules that
[someone someday] establishes for "objects" otherwise they could follow
some other set of rules established for "base parts" or whatever you
want to call them.  My claim would be that the [unstated] rules for
base parts as implied by ARM and common implementations are sufficiently
different from other "objects" that such can't reasonably be considered
as part of the "object" rules.  At least I can't think of such a set
of consistent rules.  If you can come up with a consistent set of rules
that match ARM requirements and most of most implementations -- hey,
then go for it!

|If you prefer to use a word other than "subobject" to describe
|this relationship, I suppose you can do so.  But however you
|describe it, it is an important concept in the semantics of C++.

Agreed, its just that those base parts don't necessarily follow
all the rules of "objects"

|Perhaps you disagree that a[2] or s.j are "objects"?  If so
|then I think you should reconsider your terminology.  "object"
|is the term used throughout the C standard and ARM for this
|concept.

I agree that elements and members are objects.  Base parts
are neither elements nor members, but base parts can have
elements and members.  I do not consider a base part in
itself to be an object.

|And a "base subobject" bears an analogous relationship to the
|containing object(s).

I disagree.  I consider the analogy to be sufficiently weak
that I don't think a consistent set of rules can be developed
for both base parts and other objects that reasonably captures
the behavior of both categories.

|A virtual function call (as I noted in
|another item I posted recently) requires taking a base object
|and finding the appropriate derived object containing the base
|object.  This can't be done based solely on the contents of
|the base object.

Note that this is considerably different behavior than built-in
objects, "C" struct objects, standalone C++ objects, member objects,
and element objects.  Thus I believe these base parts do not
represent "objects" but rather fall into a different category of thingees.

|The data members of a class are also subobjects according to
|my understanding.  You're even disallowing that?

I don't think that's anyone's intent.

||>  Is the issue not simple: either we waste bytes
||> to obtain object identity for subobjects or we dont.
||>
|
|I can phrase begging questions too. Either we implement the
|language correctly or we fudge, save a byte and hope nobody
|notices.

How can anyone implement the language "correctly" if you are
going to define "correctness" in terms of your undefined requirements
that neither you, ARM, nor the ANSI-C base document calls out?

If an implementation does not contradict clearly stated requirements of
the  base documents, then the language HAS been implemented correctly.
If this is unacceptable to you, then write a up a clear, complete,
set of rules of what you say objects are and aren't and what properties
they do have and don't have *in the language* and get the
rest of the C++ committee to buy-off on your definitions.
Then implementations can implement these agreed-upon set of rules
in the famous "as-if" manner, and no one can complain.  Until
then we simply are arguing for our particular favorite *implementation*
[NOT *LANGUAGE*] and arguing against the other guy's implementation.

|Apparently the issue isn't simple.  There is a fundamental
|disagreement about the semantics of C++.  If that disagreement
|is resolved it will tell us what is a legal implementation.
|We can use the consequences for storage layout and sizes as
|a criterion for determining the semantics, but it can't be
|the sole criterion.

We're not even yet to the stage of fundamental disagreement,
because neither camp[s] has clearly defined what they're talking
about.  Many people are even clear yet that they're being
unclear.  Once there are clear statements of what the various
positions are, *then* the possibility exists of working through
any disagreement, and reaching a workable compromise.




Author: jss@lucid.com (Jerry Schwarz)
Date: Thu, 7 Jan 93 02:59:48 GMT
Raw View
We seem to be arguing as much about the meaning of terms
and the model that should be used to define the semantics of C++
as we are about what the semantics are or should be.  I don't
think this is a fruitful discussion, so I'm going to take a different
tack and present my point of view solely in terms of code.

 struct X { int i ; } ;
 struct D : public X {
  X xInD ;
 } ;

 void set(X* p, int n) { p->i = n ; }

 void f() {
     D d ;
     X* p1 = &d;
     X* p2 = &d.xInD ;
     set(p1, 1) ;
     set(p2, 2) ;
     d.i ; // must be 1
     d.xInD.i ; // must be 2
     p1 == p2 ; // ????
        }


I'm not sure I can prove the two assertions (d.i is 1 and d.xInD.i is 2)
from the ARM.  I know I can't do it without using phrases (like
distinct object) that I'm trying to avoid in this post.  But I hope
there is no doubt about the assertions.

The rest of this posting is my opinion about how C++ ought to
be defined (by x3j16/wg21).

I believe that p1==p2 ought to be false (i.e. 0).  I'm not sure
I can prove it from the ARM, but I have what I consider good
justification. Namely, the only way that "set" can operate properly
with regard to d.i and d.xInX.i is for there to be some difference in
the values of its first argument on the two calls. Whatever this difference
is, it ought to be reflected in the result of ==.  If words to force
that result aren't already in the working paper, then I support
a change to the working paper.  Otherwise == on pointers becomes
almost useless and we would have to invent something else
(a library function perhaps) to do a complete comparison of
pointer values.

If there are any implementations out there that for which p1==p2
might be true in the above example, I'd be interested in hearing
about them.

I'm not going to propose any exact wording here or address the question
of whether words already exist in the ARM because any attempt would
drop me into the terminological tar pit.  But I will say that I don't
think those words should make a distinction between X's that have or
don't have data members, or virtuals, or pure virtuals or anything
else.

  -- Jerry Schwarz




















Author: pkt@lpi.liant.com (Scott Turner)
Date: Thu, 7 Jan 1993 14:55:11 GMT
Raw View
In article <1993Jan06.194710.6913@microsoft.com>, jimad@microsoft.com (Jim Adcock) writes:
> In article <1993Jan5.203237.28304@lpi.liant.com> pkt@lpi.liant.com (Scott Turner) writes:
> |Yes.  An _other_ object is distinct, not identical to the object with which
> |it shares storage.
>
> I still don't understand your [undefined] concepts of "distinctness"
> and "identity"

I suspect the definition wouldn't matter to our issues of pointer comparison.

Also, I'm hesitant to try to define identity of objects.  The fundamental
semantics of a programming language are typically specified by their
(1) operation, (2) denotation, or (3) axiomatically.  In (1) and (2) you
start with a rigorous framework, which we don't even have an inkling of
for C++.  In (3) you don't define the fundamentals, you specify how to
reason about them.

Identity of objects is more of the essence than storage, so it would be
problematic to define the former in terms of the latter.
--
Prescott K. Turner, Jr.
Liant Software Corp. (developers of LPI languages)
959 Concord St., Framingham, MA 01701 USA    (508) 872-8700
UUCP: uunet!lpi!pkt                          Internet: pkt@lpi.liant.com




Author: pkt@lpi.liant.com (Scott Turner)
Date: Thu, 7 Jan 1993 15:23:29 GMT
Raw View
Jim Adcock writes:
> |And a "base subobject" bears an analogous relationship to the
> |containing object(s).
>
> I disagree.  I consider the analogy to be sufficiently weak
> that I don't think a consistent set of rules can be developed
> for both base parts and other objects that reasonably captures
> the behavior of both categories.

It always seemed to me that the very term "base class subobject" was
making the analogy, and pointing to the fact that there were other kinds of
subobjects, i.e. elements and members.

If you don't think base class subobjects can be objects and still
support a reasonable set of rules regarding objects, how about saying
more clearly what's the conflict with the present rules?  Your multiple
inheritance example is interesting, but do you think it indicates any
difficulties for Jerry Schwarz's and my reading of the rules?

(You have indeed pointed out one conflict with the present rules.  1.3 of the
working paper says that an object occupies a contiguous set of bytes,
which conflicts with the usual implementation techniques for virtual bases
of base classes.  But it's easy enough to make an exception to this rule, for
base class subobjects.)
--
Prescott K. Turner, Jr.
Liant Software Corp. (developers of LPI languages)
959 Concord St., Framingham, MA 01701 USA    (508) 872-8700
UUCP: uunet!lpi!pkt                          Internet: pkt@lpi.liant.com




Author: fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON)
Date: Fri, 8 Jan 1993 01:05:26 GMT
Raw View
jss@lucid.com (Jerry Schwarz) writes:

>In article <9300300.29980@mulga.cs.mu.OZ.AU>, fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON) writes:
>|>
>|> As I noted in a previous article, I don't agree with Scott Turners belief
>|> that base class subobjects are objects. To me, these are absurd
>|> consequences, and thus I conclude that the assumption used in deriving them
>|> must be flawed.
>
>Perhaps fjh would specify some of these "absurd consequences".

I meant the ones that I quoted in the article I was responding to
(maxtal = John MAX Skaller):

maxtal> One consequence: abstract objects can exist.
maxtal>
maxtal> Corollary: such objects have states not accessible via
maxtal> the contiguous storage allocated for them.
maxtal> (via virtual function calls)

To me, an abstract class is a class without any actual objects of that class,
so the idea of an "abstract object" seems to be an oxymoron. Similarly, the
idea that an object can have "state" that is not stored in its "storage" seems
to make a mockery of the whole idea of "storage".

I can understand the opposing view, but it seems to me that we are just
starting from different axioms. Anyhow, this discussion does not seem to be
very fruitful.

--
Fergus Henderson             fjh@munta.cs.mu.OZ.AU
This .signature virus is a self-referential statement that is true - but
you will only be able to consistently believe it if you copy it to your own
.signature file!




Author: fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON)
Date: Fri, 8 Jan 1993 01:16:39 GMT
Raw View
jss@lucid.com (Jerry Schwarz) writes:

>I'm not going to propose any exact wording here or address the question
>of whether words already exist in the ARM because any attempt would
>drop me into the terminological tar pit.  But I will say that I don't
>think those words should make a distinction between X's that have or
>don't have data members, or virtuals, or pure virtuals or anything
>else.

I would tend to agree. But I think that it might very well be appropriate
for the commentary to contain something like

 - Note that the above wording implies that if a class has any data
   members, then ... (some guarantee about pointer comparisons).

In other words, requirements for data members (or such-like) could be implicit
rather than explicit in the ARM.

--
Fergus Henderson             fjh@munta.cs.mu.OZ.AU
This .signature virus is a self-referential statement that is true - but
you will only be able to consistently believe it if you copy it to your own
.signature file!




Author: pkt@lpi.liant.com (Scott Turner)
Date: Sun, 27 Dec 1992 05:01:18 GMT
Raw View
In article <9236102.3238@mulga.cs.mu.OZ.AU> (Fergus James HENDERSON) writes:
> One of the most important principles of C was that if you didn't use a
> feature, you shouldn't have to pay for it. This principle was carried
> through to C++. On the topic "Design Notes", Bjarne Stroustrup wrote
>  "Features that would incur run-time or memory overheads even when
>  not use were avoided" [C++PL, 2nd Ed. pg 3].
>
> As a feature, padding empty base classes fits this description exactly.

"Padding empty base classes" is not the feature -- it's the
simplest implementation.  The desired feature is:
    Two pointers to the same type, which point to distinct objects
    of that type, compare not equal.

> It has a very significant memory overhead, especially if there
> are multiple levels of derivation involved.

A smart implemetation would typically have no overhead.  The most practical
example which has come up is Jamshid Afshar's, which incurs no overhead
even with existing implementations.  The example which requires padding
(originally Johan Bengtsson's) is contrived.

> In the case of deriving from empty structures, it is absolutely TRIVIAL
> to insert padding yourself *IF* you really need the addresses to be different.

This is getting away from my point, but how do you know a compiler will
provide storage space for unused padding within a struct/class?

> Furthermore, if the programmer didn't
> ask for the padding and didn't want it, how could you say it was
> anything other than "uncalled for"?

There's a good chance the programmer needs data or object code compatibility
with other code.  The other code may use the above-described feature.
--
Prescott K. Turner, Jr.
Liant Software Corp. (developers of LPI languages)
959 Concord St., Framingham, MA 01701 USA    (508) 872-8700
UUCP: uunet!lpi!pkt                          Internet: pkt@lpi.liant.com




Author: jimad@microsoft.com (Jim Adcock)
Date: 30 Dec 92 18:46:14 GMT
Raw View
In article <1992Dec27.050118.1628@lpi.liant.com> pkt@lpi.liant.com (Scott Turner) writes:
|The desired feature is:
|Two pointers to the same type, which point to distinct objects
|of that type, compare not equal.

Can you define your terms for us?

"Two pointers" means what?

"to the same type" means what?

"distinct objects" means what?

"of that type" means what?

"compare not equal" means what?

I'm sure each of these things seems clear to you, yet they are not.




Author: pkt@lpi.liant.com (Scott Turner)
Date: Thu, 31 Dec 1992 15:27:00 GMT
Raw View
In article <1992Dec30.184614.5551@microsoft.com>, jimad@microsoft.com (Jim Adcock) writes:
> In article <1992Dec27.050118.1628@lpi.liant.com> pkt@lpi.liant.com (Scott Turner) writes:
> |The desired feature is:
> |Two pointers to the same type, which point to distinct objects
> |of that type, compare not equal.
>
> Can you define your terms for us?

> I'm sure each of these things seems clear to you, yet they are not.

I'm talking about expressions with equality operators,
i.e. 5.10 of the ARM.  By "two pointers to the same type" I mean
to address just those cases in which both operands are pointer-type
expressions, and in which the types of the expressions are
 [const/volatile] pointer to [const/volatile] type A
and
 [const/volatile] pointer to [const/volatile] type B
where A and B are the "same type".  (I spell this out to indicate that
this is concerned with the static expression type, not the type of some
object, possibly enclosing the one pointed to.)

"Same type" is a term used repeatedly in the ARM.  It has its limitations,
but is adequate for my purpose.

"Point to distinct objects" would imply two things:
(1) Each operand points to one object (not a function, and is not null or
    invalid).
(2) The objects are not identicial.  I hope to say more about this in
    another article.

The case I'm concerned with is limited to "objects of that type", i.e.
each operand points to a properly constructed object of type A (which may
be a subobject of an enclosing object constructed with a different type).

"Compare not equal" is self-evident; the C standard uses the same kind
of wording.  It means that the result of the operator != is 1 (TRUE) and
the result of the operator == is 0 (FALSE).
--
Prescott K. Turner, Jr.
Liant Software Corp. (developers of LPI languages)
959 Concord St., Framingham, MA 01701 USA    (508) 872-8700
UUCP: uunet!lpi!pkt                          Internet: pkt@lpi.liant.com




Author: pkt@lpi.liant.com (Scott Turner)
Date: Thu, 31 Dec 1992 17:02:23 GMT
Raw View
In article <1992Dec30.184614.5551@microsoft.com> (Jim Adcock) writes:
> Can you define your terms for us?

> "distinct objects" means what?
It means the objects are not identical.

I was going to say that the standard for C++ needs to spell out what it
means for object to be identical in a better way than just by
     1. An object is a region of storage.
     2. A definition or a new expression reserves the storage
        needed by the object.
These two rules leave things much more loose than necessary when objects
require no storage.  But note that section 1.3 of the working paper
(The C++ Memory Model) says:

 The constructs in a C++ program create, refer to, access, and
 manipulate objects in memory.  Each object (except bit-fields)
 occupies one or more contiguous bytes.

I missed this a while back when enumerating the features of the ARM and
working paper which supported
> The desired feature is:
>     Two pointers to the same type, which point to distinct objects
>     of that type, compare not equal.
This excerpt from 1.3 gives the general rule I was seeking, because
objects can be distinguished by the storage reserved for them.

The excerpt may raise a few questions.

 How much freedom does it leave a compiler to optimize away
 an object's bytes (by application of the "as if" rule, etc.)?
 I think a good deal of freedom, because the working paper
 doesn't define the results if a program attempts to refer to
 or manipulate the bytes of an object.

 Does it govern base class subobjects?  As I understand it,
 base class subobjects are objects, and hence are covered.

 Does it belong in the C++ standard?  I'd say yes, given that
 compilers will for many practical purposes be able to
 optimize away unneeded bytes.

--
Prescott K. Turner, Jr.
Liant Software Corp. (developers of LPI languages)
959 Concord St., Framingham, MA 01701 USA    (508) 872-8700
UUCP: uunet!lpi!pkt                          Internet: pkt@lpi.liant.com





Author: maxtal@extro.ucc.su.OZ.AU (John MAX Skaller)
Date: Fri, 1 Jan 1993 15:52:41 GMT
Raw View
In article <1992Dec31.170223.21637@lpi.liant.com> pkt@lpi.liant.com (Scott Turner) writes:
> Does it govern base class subobjects?  As I understand it,
> base class subobjects are objects, and hence are covered.

 One consequence: abstract objects can exist.

 Corollary: such objects have states not accessible via
 the contiguous storage allocated for them.
 (via virtual function calls)


--
;----------------------------------------------------------------------
        JOHN (MAX) SKALLER,         maxtal@extro.ucc.su.oz.au
 Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
;--------------- SCIENTIFIC AND ENGINEERING SOFTWARE ------------------




Author: fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON)
Date: Sat, 2 Jan 1993 13:30:07 GMT
Raw View
pkt@lpi.liant.com (Scott Turner) writes:

>(Jim Adcock) writes:
>> "distinct objects" means what?
>It means the objects are not identical.
>
>I was going to say that the standard for C++ needs to spell out what it
>means for object to be identical in a better way than just by
>     1. An object is a region of storage.
>     2. A definition or a new expression reserves the storage
>        needed by the object.
>These two rules leave things much more loose than necessary when objects
>require no storage.  But note that section 1.3 of the working paper
>(The C++ Memory Model) says:
>
> The constructs in a C++ program create, refer to, access, and
> manipulate objects in memory.  Each object (except bit-fields)
> occupies one or more contiguous bytes.

Let me just interject: what is the motivation for the above (as opposed
to "... occupies zero or more contiguous bytes")?
It seems to me that it is there primarily as an attempt to ensure some
sort of guarantee about pointer comparisons. (Are there any other reasons?)
However, the murkiness of the concepts involved in "object identity" appears
to make it very difficult or impossible to provide such guarantees.

If indeed it is the case that the above wording has failed to achieve its
purpose, then there are strong efficiency arguments which suggest that it
should be modified.

>I missed this a while back when enumerating the features of the ARM and
>working paper which supported
>> The desired feature is:
>>     Two pointers to the same type, which point to distinct objects
>>     of that type, compare not equal.
>This excerpt from 1.3 gives the general rule I was seeking, because
>objects can be distinguished by the storage reserved for them.
>
>The excerpt may raise a few questions.
>
> How much freedom does it leave a compiler to optimize away
> an object's bytes (by application of the "as if" rule, etc.)?
> I think a good deal of freedom, because the working paper
> doesn't define the results if a program attempts to refer to
> or manipulate the bytes of an object.

Perhaps, but
 "The _sizeof_ operator yields the size, in bytes, of its operand".
I think that it would be a very brave compiler-writer whose compiler
made it unreliable to write() an object and read() it in again, because
 read(fd, &object, sizeof object);
randomly trashes the bytes past the end of the object.

> Does it govern base class subobjects?  As I understand it,
> base class subobjects are objects, and hence are covered.

Is your understanding based on reading the working paper, or discussion with
members of the committee, or is it your personal judgement of the meaning
of "subobject"? My own metaphysical perspective is that subobjects are
by definition not objects in their own right - that's why virtual functions
work the way they do. If subobjects *were* objects, then there would be
no difference between private inheritance and membership. Thus my own
personal judgement would be that (presuming nothing else is to the contrary)
base class subobjects would not be covered.

> Does it belong in the C++ standard?  I'd say yes, given that
> compilers will for many practical purposes be able to
> optimize away unneeded bytes.

I would be inclined to suspect that such opportunities would be
quite rare, given the restrictions mentioned above.

--
Fergus Henderson             fjh@munta.cs.mu.OZ.AU
This .signature virus is a self-referential statement that is true - but
you will only be able to consistently believe it if you copy it to your own
.signature file!




Author: fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON)
Date: Sat, 2 Jan 1993 13:35:25 GMT
Raw View
maxtal@extro.ucc.su.OZ.AU (John MAX Skaller) writes:

>pkt@lpi.liant.com (Scott Turner) writes:
>> Does it govern base class subobjects?  As I understand it,
>> base class subobjects are objects, and hence are covered.
>
> One consequence: abstract objects can exist.
>
> Corollary: such objects have states not accessible via
> the contiguous storage allocated for them.
> (via virtual function calls)

As I noted in a previous article, I don't agree with Scott Turners belief
that base class subobjects are objects. To me, these are absurd
consequences, and thus I conclude that the assumption used in deriving them
must be flawed.

--
Fergus Henderson             fjh@munta.cs.mu.OZ.AU
This .signature virus is a self-referential statement that is true - but
you will only be able to consistently believe it if you copy it to your own
.signature file!