Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

STL set flaw

346 views
Skip to first unread message

Willy Wood

unread,
May 27, 1996, 3:00:00 AM5/27/96
to

The following declaration of a set of pointers to class X
contains a serious flaw. Who can spot it.

#include <set.h>

class X
{
};

typedef set<X*,less<X*> > setX;


--
Willy Wood, Israel.
---
[ comp.std.c++ is moderated. To submit articles: try just posting with ]
[ your news-reader. If that fails, use mailto:std...@ncar.ucar.edu ]
[ FAQ: http://reality.sgi.com/employees/austern_mti/std-c++/faq.html ]
[ Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++...@ncar.ucar.edu ]

James Kanze US/ESC 60/3/141 #40763

unread,
May 28, 1996, 3:00:00 AM5/28/96
to

In article <31AA90...@aquanet.co.il> Willy Wood
<of...@aquanet.co.il> writes:

|> The following declaration of a set of pointers to class X
|> contains a serious flaw. Who can spot it.

|> #include <set.h>

|> class X
|> {
|> };

|> typedef set<X*,less<X*> > setX;

The normal default implementation of less invokes undefined behavior if
the pointers involved do not point to objects in the same array.

I presume that once implementations start supporting partial
specialization, they will provide a partial specialization for pointers
that will work.

--
James Kanze Tel.: (+33) 88 14 49 00 email: ka...@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France
Conseils, itudes et rialisations en logiciel orienti objet --
-- A la recherche d'une activiti dans une region francophone

Nathan Myers

unread,
May 31, 1996, 3:00:00 AM5/31/96
to

James Kanze US/ESC 60/3/141 #40763 wrote:
>
> In article <31AA90...@aquanet.co.il> Willy Wood
> <of...@aquanet.co.il> writes:
>
> |> The following declaration of a set of pointers to class X
> |> contains a serious flaw. Who can spot it.
>
> |> #include <set.h>
>
> |> class X
> |> {
> |> };
>
> |> typedef set<X*,less<X*> > setX;
>
> The normal default implementation of less invokes undefined behavior if
> the pointers involved do not point to objects in the same array.
>
> I presume that once implementations start supporting partial
> specialization, they will provide a partial specialization for pointers
> that will work.

There is an open proposal to provide something like this, but unless/until
it's mentioned in the Standard, it cannot be presumed.

Some implementors say that providing a total ordering among pointers
of random origin (not all pointing within a single array) could be an
order of magnitude more expensive. Hence, it should not be required for
built-in operator<() applied to pointers, because it would slow down
loops operating only within a single array.

Nathan Myers
n...@cantrip.org

J. Kanze

unread,
Jun 2, 1996, 3:00:00 AM6/2/96
to

In article <31AF3DEA...@cantrip.org> Nathan Myers
<n...@cantrip.org> writes:

|> James Kanze US/ESC 60/3/141 #40763 wrote:

|> > |> typedef set<X*,less<X*> > setX;
|> >
|> > The normal default implementation of less invokes undefined behavior if
|> > the pointers involved do not point to objects in the same array.
|> >
|> > I presume that once implementations start supporting partial
|> > specialization, they will provide a partial specialization for pointers
|> > that will work.

|> There is an open proposal to provide something like this, but unless/until
|> it's mentioned in the Standard, it cannot be presumed.

|> Some implementors say that providing a total ordering among pointers
|> of random origin (not all pointing within a single array) could be an
|> order of magnitude more expensive. Hence, it should not be required for
|> built-in operator<() applied to pointers, because it would slow down
|> loops operating only within a single array.

I would personally be against requiring it, too, for exactly these
reasons. A user normally has no business comparing arbitrary pointers.
What I was suggesting was specializing the "less" template for pointers
in environments where normal comparison doesn't work. In this way,
operator< is still efficient (but requires pointers into the same
array), and less<T*> works for arbitrary pointers (but is potentially
less efficient for pointers into the same array).
--
James Kanze (+33) 88 14 49 00 email: ka...@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs Bourgeois, 67000 Strasbourg, France
Conseils en informatique industrielle --
-- Beratung in industrieller Datenverarbeitung

Claude Quizel

unread,
Jun 4, 1996, 3:00:00 AM6/4/96
to

J. Kanze wrote:
[cut]

> I would personally be against requiring it, too, for exactly these
> reasons. A user normally has no business comparing arbitrary pointers.
[cut]

I had a similar problem when I stored a finite element mesh to disk. I
wanted to associate a unique identifier to every node in the mesh before
storing to disk. One solution was to associate this identifier to the
memory location of the node i.e. (same problem)

map<Node*, Id, less<Node*> >

This seemed like a valid solution. I whould write each node only once to
the file but every object associated to this node would store it's Id.
But this was impossible for the same reason: the map could not sort
Node* (only == and != are legal). I did test this on many compilers and
it always worked but deep inside I know it's wrong. This seems like a
valid example where sorting arbitrary pointers whould be helpfull.
(of course the resulting order is unimportant). Any Suggestions?

Claude
---

Nathan Myers

unread,
Jun 4, 1996, 3:00:00 AM6/4/96
to

Claude Quizel wrote:
>
> J. Kanze wrote:
> [cut]
> > I would personally be against requiring it, too, for exactly these
> > reasons. A user normally has no business comparing arbitrary pointers.
> [cut]
>
> I had a similar problem when I stored a finite element mesh to disk. I
> wanted to associate a unique identifier to every node in the mesh before
> storing to disk. One solution was to associate this identifier to the
> memory location of the node i.e. (same problem)
>
> map<Node*, Id, less<Node*> >
>
> This seemed like a valid solution. I would write each node only once to

> the file but every object associated to this node would store it's Id.
> But this was impossible for the same reason: the map could not sort
> Node* (only == and != are legal). I did test this on many compilers and
> it always worked but deep inside I know it's wrong. This seems like a
> valid example where sorting arbitrary pointers whould be helpfull.
> (of course the resulting order is unimportant). Any Suggestions?

I'm sorry if James and I have not been clear enough. It is perfectly
reasonable to want an (arbitrary) complete ordering on pointers, for
just the reason Claude suggests. However, it would overburden some users
for operator< to be required to provide this ordering.

The language does not currently provide *any* mechanism to generate a
total ordering of pointers. The obvious place for such an extension
is as a partial specialization less<T*>, because it wouldn't require
any change to language syntax. On implementations where operator<
already defines a total order, no explicit specialization is needed;
otherwise, the vendor would be obliged to provide it in the header
along with the definition of less<>.

There is no requirement in the draft, currently, for this behavior;
but there is an open issue in the Issues List for chapter 20 (Utilities)
that might be discussed in Stockholm. Until it is accepted, any code
that depends on a total order of pointers, like Claude's above, is only
portable to those implementations that define operator< to provide a total
order on pointers.

It's not a moral problem, but one of documentation and limited portability.
Fortunately, if the proposal is accepted Claude's code will become correct
and portable with no changes. In the meantime, there is no fully portable
alternative but to give each object a numeric ID and sort them on that
basis, or to hash the pointers and compare them for true equality during
lookup.

Nathan Myers
n...@cantrip.org
---
[ comp.std.c++ is moderated. To submit articles: Try just posting with your
newsreader. If that fails, use mailto:std...@ncar.ucar.edu
comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
Comments? mailto:std-c++...@ncar.ucar.edu
]

Gregory Bond

unread,
Jun 5, 1996, 3:00:00 AM6/5/96
to

As a corollary, would casting the pointers to an appropriate integral
type and then comparing the integers be a conforming total ordering?

The above discussions imply no. Why not?

Greg.
--
Gregory Bond <g...@bby.com.au>
Burdett Buckeridge & Young Ltd Melbourne Australia
``Efforts to maintain the "purity" of a language only succeed in establishing
an elite class of people who know the shibboleths. Ordinary folks know
better, even if they don't know what "shibboleth" means.'' - Larry Wall
---

Fergus Henderson

unread,
Jun 5, 1996, 3:00:00 AM6/5/96
to

Gregory Bond <g...@bby.com.au> writes:

>As a corollary, would casting the pointers to an appropriate integral
>type and then comparing the integers be a conforming total ordering?

Not strictly conforming, no.

>The above discussions imply no. Why not?

Two reasons. Firstly, there's no guarantee that there will be any
appropriate integral type. A system may have 32-bit longs and 64- or
128-bit pointers, for example. Secondly, even if there is a sufficiently
large integral type, the conversion from pointer to integer is
implementation-defined, and there's no guarantee that this conversion
will be one-to-one.

--
Fergus Henderson <f...@cs.mu.oz.au> | "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh> | of excellence is a lethal habit"
PGP: finger f...@128.250.37.3 | -- the last words of T. S. Garp.
---

Bill Wade

unread,
Jun 5, 1996, 3:00:00 AM6/5/96
to

In article <ww7mtmb...@nellie.bby.com.au>, g...@bby.com.au says...

>
>As a corollary, would casting the pointers to an appropriate integral
>type and then comparing the integers be a conforming total ordering?
>
>The above discussions imply no. Why not?

For built-in integral types there is no promise that a large enough integral
type exists. From the April 95 DWP:

"A pointer converted to an integer of sufficient size (if any such exists on
the implementation) ..."

Also, I believe there is nothing in the standard which requires all of the
bits in a pointer to be significant, or that there be a single unique
representation of a given address. For instance the orignal Motorolla 68000
had 32 bit pointers, but only a 24 bit address space. Only the low-order 24
bits of a pointer were significant. I believe a conforming implementation
could implement
T *p1, *p2;
assert(p1 == p2); // Look at low-order 24 bits.
assert(int(p1) == int(p2)); // Look at all 32 bits.
and the code above could fail at the second assertion.

There is a promise that if all of the bytes in two pointers (of the same
type) are the same, they point at the same object. This promise comes from
the promise that after
T p1, p2; // T is a scalar type, pointer types are scalar types.
// assume p2 has a valid value
memcpy(&p1, &p2, sizeof(p2));
// p1 and p2 must now have the same value.

So the value of a pointer can not, for instance, be a function of the address
of the pointer, because the memcpy above would not successfully copy the value
of p2 into p1.

Other scalar values may have the property that two objects with the same value
have different representations. In particular signed integer types using a
sign-magnitude representation might have two bit patterns which mean zero.

James Kanze US/ESC 60/3/141 #40763

unread,
Jun 5, 1996, 3:00:00 AM6/5/96
to std...@ncar.ucar.edu

In article <ww7mtmb...@nellie.bby.com.au> Gregory Bond
<g...@bby.com.au> writes:

|> As a corollary, would casting the pointers to an appropriate integral
|> type and then comparing the integers be a conforming total ordering?

|> The above discussions imply no. Why not?

What is the appropriate integral type. Consider that there are, for
example, implementations in which a pointer is larger than any integral
type. (I've worked on a system where pointers were 48 bits, but long's
only 32. Obviously, on such a system, there is no way to cast a pointer
to an integer without some loss of information.)
--

James Kanze Tel.: (+33) 88 14 49 00 email: ka...@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France

Conseils, études et réalisations en logiciel orienté objet --
-- A la recherche d'une activité dans une region francophone

Nathan Myers

unread,
Jun 6, 1996, 3:00:00 AM6/6/96
to

Nathan Myers wrote:

> The language does not currently provide *any* mechanism to generate a

> total ordering of pointers. ... there is no fully portable


> alternative but to give each object a numeric ID and sort them on that
> basis, or to hash the pointers and compare them for true equality during
> lookup.

A correction, in light of other answers...

Hashing pointer values, and then comparing for equality, is itself not
strictly portable, because two pointers to the same storage might hash
to different buckets. This is rarely (but still possibly) a problem on
real architectures if the pointers are simply copied, but is quite likely
if arithmetic is used on them.

For example:

p = q + 3;
--p; --p; --p;
assert(hash(p) == hash(q)); // might fail!

This sort of problem is pretty common on PC compilers, and I have
even seen (on a Borland compiler from a couple of years ago) the
following loop fail to terminate:

int a[100];
int* p = a + 100;
while (p != a) { --p; ... }

(Note that the behavior of the apparently similar

while (p-- != a) { ... }

is undefined, because p gets a value outside the range [a,a+100].)

Nathan Myers
n...@cantrip.org http://www.cantrip.org/
---

Eric Gindrup

unread,
Jun 11, 1996, 3:00:00 AM6/11/96
to

James Kanze US/ESC 60/3/141 #40763 wrote:
> In article <ww7mtmb...@nellie.bby.com.au> Gregory Bond
> <g...@bby.com.au> writes:
[... cast pointers to integrals and compare? ...]

> > The above discussions imply no. Why not?
>
> What is the appropriate integral type. Consider that there are, for
> example, implementations in which a pointer is larger than any
> integral type. (I've worked on a system where pointers were 48 bits,
> but long's only 32. Obviously, on such a system, there is no way to
> cast a pointer to an integer without some loss of information.)
> --
> James Kanze email: ka...@gabi-soft.fr

Unless I have failed to remember correctly, such a system would be
incapable of supporting the type ptrdiff_t. This type is (still) standard
C, and I recall it being transplanted into standard C++ (still). ptrdiff_t
would also seem to address the original question.
-- Eric Gindrup ! gin...@okway.okstate.edu
---

Nathan Myers

unread,
Jun 11, 1996, 3:00:00 AM6/11/96
to

Eric Gindrup wrote:
>
> James Kanze US/ESC 60/3/141 #40763 wrote:
> > In article <ww7mtmb...@nellie.bby.com.au> Gregory Bond
> > <g...@bby.com.au> writes:
> [... cast pointers to integrals and compare? ...]
> > > The above discussions imply no. Why not?

> > What is the appropriate integral type. Consider that there are, for
> > example, implementations in which a pointer is larger than any
> > integral type. (I've worked on a system where pointers were 48 bits,
> > but long's only 32. Obviously, on such a system, there is no way to
> > cast a pointer to an integer without some loss of information.)

> Unless I have failed to remember correctly, such a system would be


> incapable of supporting the type ptrdiff_t. This type is (still) standard
> C, and I recall it being transplanted into standard C++ (still). ptrdiff_t
> would also seem to address the original question.

ptrdiff_t need only be big enough to represent differences within a single
array; but an implementation might have a 48-bit pointer, but only permit
arrays of size 2^32 -- or in the extreme case (:-) a 20-bit pointer, and
allow arrays of only 2^16.

It might also have a 64-bit int, and a 48-bit pointer, so converting from
pointer to int leaves 16 bits of random garbage in the int, and two equal
pointers don't compare equal as ints. (The 68000 had 32-bit int and 24-bit
pointers, with a potential for the same problem; Apple actually used that,
and encoded extra data in their pointers -- to their later chagrin.)

Nathan Myers
n...@cantrip.org

Jonathan de Boyne Pollard

unread,
Jun 12, 1996, 3:00:00 AM6/12/96
to

Eric Gindrup (gin...@Okway.okstate.edu) wrote:
| James Kanze US/ESC 60/3/141 #40763 wrote:
| > I've worked on a system where pointers were 48 bits,
| > but long's only 32. Obviously, on such a system, there is no way to
| > cast a pointer to an integer without some loss of information.)
|
| Unless I have failed to remember correctly, such a system would be
| incapable of supporting the type ptrdiff_t.

I don't have a copy of the C Standard, but isn't it true that ptrdiff_t is
defined to be an alias for the (implementation defined) signed integral
type that is returned from the subtraction of two pointers ?

[Moderator's note: yes, that's correct. -fjh.]

If so, then, by [expr.add], which states that pointer subtraction is only
defined for the case that the two pointers point to objects in the same
array (or one past the end thereof), the only constraint upon such a system
so that it be conforming is that arrays cannot exceed 4Tb in size.

[Moderator's note: I think you mean 2**32 bytes, i.e. 4Gb,
not 4Tb. -fjh.]

---

J. Kanze

unread,
Jun 12, 1996, 3:00:00 AM6/12/96
to

In article <31BDD5F7...@cantrip.org> Nathan Myers
<n...@cantrip.org> writes:

|> Eric Gindrup wrote:
|> >
|> > James Kanze US/ESC 60/3/141 #40763 wrote:

|> > > In article <ww7mtmb...@nellie.bby.com.au> Gregory Bond
|> > > <g...@bby.com.au> writes:
|> > [... cast pointers to integrals and compare? ...]
|> > > > The above discussions imply no. Why not?

|> > > What is the appropriate integral type. Consider that there are, for
|> > > example, implementations in which a pointer is larger than any

|> > > integral type. (I've worked on a system where pointers were 48 bits,


|> > > but long's only 32. Obviously, on such a system, there is no way to
|> > > cast a pointer to an integer without some loss of information.)

|> > Unless I have failed to remember correctly, such a system would be

|> > incapable of supporting the type ptrdiff_t. This type is (still)
|> > standard C, and I recall it being transplanted into standard C++
|> > (still). ptrdiff_t would also seem to address the original question.

|> ptrdiff_t need only be big enough to represent differences within a single
|> array; but an implementation might have a 48-bit pointer, but only permit
|> arrays of size 2^32 -- or in the extreme case (:-) a 20-bit pointer, and
|> allow arrays of only 2^16.

Is this actually required by the standard (that ptrdiff_t be big enough
to represent differences within a single array). I believe that most
MS-DOS implementations, for example, declare ptrdiff_t as (16 bit) int,
but allowed arrays of up to 64K. Although I've not tried it, I think
that a Sparc implementation will allow arrays of char larger than
2147483648. (I don't have enough virtual memory to actually test this
case.) This would mean that long is not big enough.


--
James Kanze (+33) 88 14 49 00 email: ka...@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs Bourgeois, 67000 Strasbourg, France
Conseils en informatique industrielle --
-- Beratung in industrieller Datenverarbeitung

Steve Clamage

unread,
Jun 12, 1996, 3:00:00 AM6/12/96
to

In article 1bda...@Okway.okstate.edu, gin...@Okway.okstate.edu (Eric Gindrup) writes:
>James Kanze US/ESC 60/3/141 #40763 wrote:
>> In article <ww7mtmb...@nellie.bby.com.au> Gregory Bond
>> <g...@bby.com.au> writes:
>[... cast pointers to integrals and compare? ...]
>> > The above discussions imply no. Why not?
>>
>> What is the appropriate integral type. Consider that there are, for
>> example, implementations in which a pointer is larger than any
>> integral type. (I've worked on a system where pointers were 48 bits,
>> but long's only 32. Obviously, on such a system, there is no way to
>> cast a pointer to an integer without some loss of information.)
>
>Unless I have failed to remember correctly, such a system would be
>incapable of supporting the type ptrdiff_t.

Not necessarily. Some systems have extra bits in pointers which are
descriptive rather than being a proper part of the "address". I once
worked on a word-addressed machine where half-words or quarter-words
(bytes) were expressed by extra bits in the pointer. In either case,
the distance between two addresses might still be representable as
a long (for example) even if a pointer contained more bits than a long.

Finally, you may take the difference between pointers only if they point
into the same object. An ordinary system with 48-bit or 64-bit pointers
might not allow objects bigger than 4Gb. Any legitimate pointer difference
would then fit in a 32-bit integral type.

---
Steve Clamage, stephen...@eng.sun.com
---

Tim Hollebeek

unread,
Jun 12, 1996, 3:00:00 AM6/12/96
to

Eric Gindrup (gin...@Okway.okstate.edu) wrote:

: James Kanze US/ESC 60/3/141 #40763 wrote:
: > In article <ww7mtmb...@nellie.bby.com.au> Gregory Bond
: > <g...@bby.com.au> writes:
: [... cast pointers to integrals and compare? ...]
: > > The above discussions imply no. Why not?
: >
: > What is the appropriate integral type. Consider that there are, for
: > example, implementations in which a pointer is larger than any
: > integral type. (I've worked on a system where pointers were 48 bits,
: > but long's only 32. Obviously, on such a system, there is no way to
: > cast a pointer to an integer without some loss of information.)
: > --
: > James Kanze email: ka...@gabi-soft.fr

: Unless I have failed to remember correctly, such a system would be
: incapable of supporting the type ptrdiff_t. This type is (still) standard

: C, and I recall it being transplanted into standard C++ (still). ptrdiff_t
: would also seem to address the original question.

I don't believe this is true. Remember pointer subtraction is only
defined between members of the same object, so it is impossible to run
into this problem unless (number of bytes of memory) > (max ptrdiff_t
value); i.e. there is enough memory to allocate a _single_ block that
large.

Neither C nor C++ force the idea of 'absolute address' to have _any
meaning whatsoever_ which is why the original question cannot be
answered.

Another point is that a pointer is _not_ required to simply be an
address. The extra 16 bits may hold alignment or other information.
In this case one could easily imagine 48 bit pointers with differences
that fit into 32 bits.

---------------------------------------------------------------------------
Tim Hollebeek | Disclaimer :=> Everything above is a true statement,
Electron Psychologist | for sufficiently false values of true.
Princeton University | email: t...@wfn-shop.princeton.edu
----------------------| http://wfn-shop.princeton.edu/~tim (NEW! IMPROVED!)

J. Kanze

unread,
Jun 12, 1996, 3:00:00 AM6/12/96
to

In article <1bda...@Okway.okstate.edu> gin...@Okway.okstate.edu (Eric
Gindrup) writes:

|> James Kanze US/ESC 60/3/141 #40763 wrote:
|> > In article <ww7mtmb...@nellie.bby.com.au> Gregory Bond
|> > <g...@bby.com.au> writes:
|> [... cast pointers to integrals and compare? ...]
|> > > The above discussions imply no. Why not?
|> >
|> > What is the appropriate integral type. Consider that there are, for
|> > example, implementations in which a pointer is larger than any
|> > integral type. (I've worked on a system where pointers were 48 bits,
|> > but long's only 32. Obviously, on such a system, there is no way to
|> > cast a pointer to an integer without some loss of information.)

|> Unless I have failed to remember correctly, such a system would be

|> incapable of supporting the type ptrdiff_t. This type is (still) standard
|> C, and I recall it being transplanted into standard C++ (still). ptrdiff_t
|> would also seem to address the original question.

Where is the problem supporting ptrdiff_t? On the system in question,
the largest any single object could be was 2^32 bytes, so a 32 bit
ptrdiff_t would be as valid for this system as it would be for any
normal 32 bit system. Remember that ptrdiff_t is the type resulting
from the subtraction of two pointers; this operation is only legal if
the pointers point into the same array. Consider that ptrdiff_t is
typically a 16 bit int on MS-DOS implementations, although in model far,
pointers are 32 bits.

It's also worth remembering that ptrdiff_t is not guaranteed to be able
to handle everything. On my system (Sun Sparc), I can theoretically
(and practically, with a large enough disk allocated to virtual memory)
declare an array "char a[3000000000]". The expression
"&a[2999999999]-&a[0]" is undefined, since it results in overflow of the
signed result returned by the substraction. (In fact, on the
implementations I use, it will result in a negative value, giving the
impression that a[2999999999] is below a[0] in memory.)


--
James Kanze (+33) 88 14 49 00 email: ka...@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs Bourgeois, 67000 Strasbourg, France
Conseils en informatique industrielle --
-- Beratung in industrieller Datenverarbeitung

James Kanze US/ESC 60/3/141 #40763

unread,
Jun 12, 1996, 3:00:00 AM6/12/96
to std...@ncar.ucar.edu

In article <4pkb24$l...@silver.jba.co.uk> Jd...@jba.co.uk (Jonathan de
Boyne Pollard) writes:

|> Eric Gindrup (gin...@Okway.okstate.edu) wrote:
|> | James Kanze US/ESC 60/3/141 #40763 wrote:
|> | > I've worked on a system where pointers were 48 bits,
|> | > but long's only 32. Obviously, on such a system, there is no way to
|> | > cast a pointer to an integer without some loss of information.)
|> |
|> | Unless I have failed to remember correctly, such a system would be
|> | incapable of supporting the type ptrdiff_t.

|> I don't have a copy of the C Standard, but isn't it true that ptrdiff_t is


|> defined to be an alias for the (implementation defined) signed integral
|> type that is returned from the subtraction of two pointers ?

|> [Moderator's note: yes, that's correct. -fjh.]

|> If so, then, by [expr.add], which states that pointer subtraction is only
|> defined for the case that the two pointers point to objects in the same
|> array (or one past the end thereof), the only constraint upon such a system
|> so that it be conforming is that arrays cannot exceed 4Tb in size.

|> [Moderator's note: I think you mean 2**32 bytes, i.e. 4Gb,
|> not 4Tb. -fjh.]

Actually, given his explination, I think he means 2^31 bytes (2GB).
Ptrdiff_t must be a signed type.

I also *think* (not sure) that in fact, the system can support larger
arrays, but that pointer substraction may cause overflow on them.

Which leads to another point: if my supposition is true... given two
pointers into the same array, is there any way of testing before
invoking undefined behavior (due to overflow in signed arithmetic)
whether the substraction is safe?


--
James Kanze Tel.: (+33) 88 14 49 00 email: ka...@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France
Conseils, études et réalisations en logiciel orienté objet --
-- A la recherche d'une activité dans une region francophone

Greg Silverman

unread,
Jun 19, 1996, 3:00:00 AM6/19/96
to

The ARM says that static objects in a translation unit are constructed
in the order their definitions appear in the file and that destructors
are called in reverse order. I did the following experiment on Solaris
2.5 using Sun's version 4.1 compiler:

//file x1.cc
...
extern int count;

class test {
public:
test()
{ _m = count++; cout << "Constructing object" << _m << " in file "
<< __FILE__ << "\n";}
~test()
{ cout << "Destructing object " << _m << " in file " << __FILE__
<< "\n";}
private:
int _m;
};

static test t1;
static test t2;

<eof>

I had files x2.cc, x3.cc, and x4.cc which are unaltered copies of x1.cc. I had
a file m.cc that looked as follows

//m.cc

int count;
int main()
{
}

I built the program x as follows :

CC -o x x1.cc x2.cc x3.cc x4.cc m.cc

where m.cc contains nothing more than main. I got following output

Constructing object 0 in file x1.cc

Constructing object 1 in file x1.cc

Constructing object 2 in file x2.cc

Constructing object 3 in file x3.cc
.
.
.
Constructing object 7 in file x4.cc

Destructing object 1 in file x1.cc

Destructing object 0 in file x1.cc

Destructing object 3 in file x2.cc

Destructing object 2 in file x2.cc

Destructing object 5 in file x3.cc

Destructing object 4 in file x3.cc

Destructing object 7 in file x4.cc

Destructing object 6 in file x4.cc


Naming the objects in the order the were constructed, the object were
constructed in the following order :


object 0, object 1, object 2, ..., object 7.

Listing the objects in the order the were destructed, we have:

object 1, object 0, object 3, object 2, object 5, object 4, object 7, object 6


Thus, in a global sense, the objects were not destructed in reverse of the
order in which they were constructed. Is this correct C++?


Greg Silverman
TRW/SIG
Sunnyvale, Ca
---

Jerry Coffin

unread,
Jun 19, 1996, 3:00:00 AM6/19/96
to

In article <4pkb24$l...@silver.jba.co.uk>, Jd...@jba.co.uk says...
[ ... ]

> If so, then, by [expr.add], which states that pointer subtraction is
> only defined for the case that the two pointers point to objects in
> the same array (or one past the end thereof), the only constraint
> upon such a system so that it be conforming is that arrays cannot
> exceed 4Tb in size.
>
> [Moderator's note: I think you mean 2**32 bytes, i.e. 4Gb,
> not 4Tb. -fjh.]

While 4 Gig is closer, since the result it signed, its range is only 2
Gig. E.g. with a 3 Gig array, subtracting the end element from the
start element overflows 2 Gig.

However, differences are returned as a number of elements, not a
number of bytes (unless it happens to be an array of char) so with,
say, 8 byte doubles, a 32 bit ptrdiff_t can be used with arrays up to
16 Gb in actual size...
--
Later,
Jerry.
---

Mike Ball

unread,
Jun 19, 1996, 3:00:00 AM6/19/96
to

In article 63...@smtp.svl.trw.com, Greg Silverman <greg_si...@smtp.svl.trw.com> writes:
> The ARM says that static objects in a translation unit are constructed
> in the order their definitions appear in the file and that destructors
> are called in reverse order. I did the following experiment on Solaris
> 2.5 using Sun's version 4.1 compiler:
>
> Thus, in a global sense, the objects were not destructed in reverse of the
> order in which they were constructed. Is this correct C++?

The Sun compile obeyed the FILO (or is that FCLD) rule only within a compilation
unit. This is a known bug that we elected not to fix because there was no way
that a "conforming" (using that term very loosly) program could tell about it and
because we fully expected some clarification by the Committee.

That clarification has now arrived, and we will change the runtime accordingly.

In the meantime, don't use the compiler's behavior in this area to indicate
anything at all about the standard.

---
Michael Ball
SunSoft Development Products

James Keesey

unread,
Jun 20, 1996, 3:00:00 AM6/20/96
to

Greg Silverman wrote:
>
> The ARM says that static objects in a translation unit are constructed
> in the order their definitions appear in the file and that destructors
^^^^

> are called in reverse order. I did the following experiment on Solaris
> 2.5 using Sun's version 4.1 compiler:
>
[snip...]

>
> Thus, in a global sense, the objects were not destructed in reverse of the
^^^^^^

> order in which they were constructed. Is this correct C++?
>

Static object construction/destruction order is only defined within
each file/translation unit. In your example, the destructors were
called in reverse order in each file so the behavior is correct.

There have been many "discussions" ;) about what way is "the right way" but
this is the way that the standard (and I believe the ARM) states.

[soapbox on]
I still believe that the overhead can't be so great as to preclude requiring
the compilers to remember the order of all static object construction and
destruct them in the reverse order.
[soapbox off]

--

James Keesey Internet: kee...@vnet.ibm.com
IBM Santa Teresa Labs VNET: KEESEY at STLPS
DB2 Multimedia Extenders
---

James Kanze US/ESC 60/3/141 #40763

unread,
Jun 22, 1996, 3:00:00 AM6/22/96
to std...@ncar.ucar.edu

In article <31C87E...@vnet.ibm.com> James Keesey
<kee...@vnet.ibm.com> writes:

|> Greg Silverman wrote:
|> >
|> > The ARM says that static objects in a translation unit are constructed
|> > in the order their definitions appear in the file and that destructors
|> ^^^^
|> > are called in reverse order. I did the following experiment on Solaris
|> > 2.5 using Sun's version 4.1 compiler:
|> >
|> [snip...]
|> >
|> > Thus, in a global sense, the objects were not destructed in reverse of

|> ^^^^^^
|> > the order in which they were constructed. Is this correct C++?
|> >

|> Static object construction/destruction order is only defined within
|> each file/translation unit. In your example, the destructors were
|> called in reverse order in each file so the behavior is correct.

Construction order was (and still is) only defined within a translation
unit. The order of destruction, as describe in the ARM, was (and still
is) the opposite of the order of construction. The only ambiguity was
whether the destructors for local static objects were included in this
ordering. (To be precise, the order of destruction is undefined
*because* the order of construction is undefined. The relationship
between the two *IS* defined.)

|> There have been many "discussions" ;) about what way is "the right way"
|> but this is the way that the standard (and I believe the ARM) states.

The standard (at least the last version I have access to) has clarified
the remaining point: local static objects are included in the ordering.

|> [soapbox on]
|> I still believe that the overhead can't be so great as to preclude
|> requiring the compilers to remember the order of all static object
|> construction and destruct them in the reverse order.
|> [soapbox off]

I believe that this is what is required. My interpretation of the ARM
was that this was what was required there. Most implementers, however,
seemed to disagree with me. (At least, I've yet to come across an
implementation which maintaint the LIFO ordering across both local and
non-local statics.) I suspect the main reason most implementers
maintained this point of view is the fact that it is generally possible
to maintain the correct ordering amonst non-local statics without
runtime registration. Adding local statics imposes the runtime
registration in all cases, since the order of construction can depend
on runtime program flow, and may vary from one run to the next.


--
James Kanze Tel.: (+33) 88 14 49 00 email: ka...@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France

Conseils, itudes et rialisations en logiciel orienti objet --
-- A la recherche d'une activiti dans une region francophone

Horst von Brand

unread,
Jun 25, 1996, 3:00:00 AM6/25/96
to

In article <31C0B9...@smtp.svl.trw.com>,

Greg Silverman <greg_si...@smtp.svl.trw.com> wrote:
>The ARM says that static objects in a translation unit are constructed
>in the order their definitions appear in the file and that destructors
>are called in reverse order. I did the following experiment on Solaris
>2.5 using Sun's version 4.1 compiler:
>[...]
>[Construction order: 0, 1 in x1.cc; 2, 3 in x2.cc; ...]
>[Destruction order: 1, 0 in x1.cc; 3, 2 in x2.cc; ...]
>Thus, in a global sense, the objects were not destructed in reverse of the

>order in which they were constructed. Is this correct C++?

Yes. As you quote above, the ARM talks about the order of
construction/destruction _in a translation unit_, not among translation
units. It would even be valid to construct:

0 in x1.cc, 2 in x2.cc, 1 in x1.cc, 3 in x2.cc, ...

as long as 0 is constructed before 1, 2 is constructed before 3, ... and
to destruct similarly. The matter is discussed by B. Stroustrup in his
"C++ Programming Language", 2ed and/or "Design and Evolution of C++". He
gives a solution for the (rare) case where you need to order stuff by
creating a static object of a class with a static counter member in each
translation unit. As a static member, it is guaranteed to start off at 0,
and in the constructor you test for that and increment, in the destructor
you decrement and test for 0. Something along the lines:

class serializer {
static int count ;
public:
serializer() {if(count++ == 0) do_initialize_the_world();};
~serializer() {if(--count == 0) do_destruct_the_world();};
}

--
Dr. Horst H. von Brand vonb...@inf.utfsm.cl
Departamento de Informatica Fono: +56 32 626364 x 431
Universidad Tecnica Federico Santa Maria Fax: +56 32 625217
Casilla 110-V, Valparaiso, Chile

0 new messages