Topic: reinterpret_cast vs. static_cast


Author: jpotter@falcon.lhup.edu (John E. Potter)
Date: 1997/03/01
Raw View
Jason Merrill (jason@cygnus.com) wrote:
: >>>>> John E Potter <jpotter@falcon.lhup.edu> writes:

: > Jason Merrill (jason@cygnus.com) wrote:

: > : Except that implicit conversions are not performed on the operand
: > : of static_cast in order to find a type that can be cast from; the
: > : new-style casts only perform a single conversion.

: > I like that.  But does CD2 say that?

: On the contrary, it does not say that implicit conversions are performed,
: so they are not.

: > I find in 4/5 that in some cases, standard conversions are suppressed
: > in some contexts and that exceptions are given in the descriptions.

: That paragraph is non-normative.

Yep.  No ;-[ on paragraph 5 and I missed the ;-] at the end which matched
the ;-[ at the beginning of paragraph 4.  And the example of unary & does
not in fact list any exceptions.  Since it expects an lvalue, it would be
redundant to exclude lvalue to rvalue conversions.

: > In 5.2.9/4 exceptions are given for static_cast<void>.  It seems that
: > these exceptions would not be required if all standard conversions
: > were suppressed.

: Those exceptions are to 5/8.

Yep.  Didn't read the line above correctly the first time; however, I
found 5/8 myself.  Now I'm off to find another counter example...

: I will readily agree that the WP is unclear on this point.  However,
: the various operators that do perform implicit conversions say the
: operands "shall" be of some type.  I suggest that this implies the
: implicit conversions, and no such language appears in the description
: of static_cast.

 ... Many hours of enjoyable reading later.

Sold!  Thanks for the education.  Since you educated an educator, it
may have a ripple effect.  Reading the CD2 is now like reading a
mystery novel for the second time.  All (well most) of the clues are
obvious.  Still not much of a plot though.

One part of this thread has been the unsafety of static_cast.
Paragraphs 5-9 (6 void* ==> anything*) are all things which if
used properly are quite safe and reasonable.  Like anything in the
language, they can be misused.  With this issue cleared up, I am
content with the relative safety of static_cast.  I will pick up the
other part, which involves safe things which can not be done, elsewhere.

John
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ 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++-request@ncar.ucar.edu                             ]





Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/02/26
Raw View
jpotter@falcon.lhup.edu (John E. Potter) writes:

|>  This has been a very confused thread.  I have no problem with any of the
|>  stuff on reinterpret_cast because the rules are clear in the section on
|>  that subject.  Reinterpret_cast is implementation defined, yet there are
|>  some things guarantied.  If the size and alignment of short are two
|>  and long are four then
|>     reinterpret_cast<long*>(reinterpret_cast<short*>(longPtr))
|>  is a no-op.

Is it required to be a no-op, or only that there is no loss of
information, and that the results compare equal to the original pointer.
(I think that an implementation on a segmented architecture, for
example, could legally canonize the pointers in the cast.)

|>     [ No, an implementation may not return null or abort ]

Others have pointed this out to me in email.

My C standard is not here, or I'd verify, but from memory, the C
standard only guarantees meaning if the cast is to void*, or back to the
original type.  (I could be wrong, though.)

I don't like the wording in the current draft, because it seems to me to
be missing the real point.  The *intent* is, I think, that
reinterpret_cast act as expected whenever possible.  To me, as expected
means that the results of the cast point to the same byte, and that
there is no information lost.  I think that the real condition has to do
with the actual size of the pointers, and not with alignment (but it
should allow changes to force alignment as well).  Of course, I cannot
conceive of an implementation where a larger pointer has stricter
alignment requirements than a smaller one, so maybe it is all right
anyway.

IMHO, however, a guarantee of to void* and back, with the rest
implementation defined (and perhaps a note as to the intent, e.g.: that
the results should be unsurprising for someone familiar with the
architecture), would be sufficient.  Market preasure would take care of
the rest.  I'm not adament about this though, and don't have any real
problems with the current wording.

BTW: it is probably worth pointing out that this entire discussion only
concerns data pointers.  The draft speaks of "pointers to objects", and
a strict interpretation of the definition of object in the draft does
not exclude functions.  In all cases where functions are intended to be
included, however, the draft speaks of objects AND functions, so I think
that the intent is clear.

|>  And the following is unspecified
|>     reinterpret_cast<short*>(reinterpret_cast<long*>(shortPtr))
|>
|>  I can't find anything that says that
|>     static_cast<short*>(static_cast<void*>(shortPtr))
|>  is a no-op.

What is the definition of "(short*)(void*)shortPtr"?  If it is defined
as using static_cast, then this has to be a no-op, or we have broken C
compatibility.

|>  The inner one is covered by standard conversions and I guess
|>  the outer one is covered by malloc.

By malloc?  What does malloc have to do with any of this?  The only
particularity of malloc is that the void* it returns must be appropriate
for a cast to any type.  Since the void* in the above is not a return
value of malloc, however, this doesn't apply here.

|>  It then seems to follow that
|>     static_cast<short*>(static_cast<void*>(longPtr))
|>  is a no-op.  That seems to say that the answer to the original question
|>  is that they are the same.
|>
|>  But what about
|>     static_cast<long*>(static_cast<void*>(shortPtr))
|>  I can't find it as defined, undefined, unspecified, or implementation
|>  defined.  Is it unmentioned or can someone give me a pointer?  And what
|>  does unmentioned mean (pointer please)?
|>
|>  And a long unanswered question, "is
|>     static_cast<short*>(longPtr)
|>  well-formed?"  The inner cast to void* is a standard conversion.  If so,
|>  it seems that (short*)longPtr is a static_cast.
|>
|>  Any enlightenment appreciated,

This is what is bothering me, too, and the main reason why I think that
casting from a void* should be a reinterpret_cast (with, exceptionally,
well defined semantics if either the void* is a return value of malloc,
calloc, realloc or operator new, or the cast is a return to the original
type), and not a static_cast.

In practice, except when performance is an issue, I think that I would
recommend never using a static_cast on a pointer or reference.  For
casting to a base class, a dynamic_cast is just as good (and shouldn't
have any effect on performance, since the compiler can trivially
determine that it cannot fail); for casting to a derived class,
dynamic_cast is safer.  And IMHO, any pointer or reference cast except
those navigating an inheritance lattice are inherently dangerous; use a
reinterpret_cast, which is (or should be) a red flag, signally the
danger.  (FWIW: this idea just occurred to me.  So I don't really know
what it is worth.)

Now if there were only some way to distinguish between a narrowing and a
widening conversion...

--
James Kanze      home:     kanze@gabi-soft.fr        +33 (0)1 39 55 85 62
                 office:   kanze@vx.cit.alcatel.fr   +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
     -- Conseils en informatique industrielle --
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ 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++-request@ncar.ucar.edu                             ]





Author: jpotter@falcon.lhup.edu (John E. Potter)
Date: 1997/02/27
Raw View
Jason Merrill (jason@cygnus.com) wrote:
: >>>>> J Kanze <kanze@gabi-soft.fr> writes:

: > This answers the first question.  But should it be.  Practically, it
: > means that static_cast can be used to cast between arbitrary pointer
: > types, since there is an implicit cast from any pointer type to void*,
: > which will allow the compiler to invoke the static cast.

: Except that implicit conversions are not performed on the operand of
: static_cast in order to find a type that can be cast from; the new-style
: casts only perform a single conversion.

I like that.  But does CD2 say that?

I find in 4/5 that in some cases, standard conversions are suppressed
in some contexts and that exceptions are given in the descriptions.

In 5.2.9/4 exceptions are given for static_cast<void>.  It seems that
these exceptions would not be required if all standard conversions
were suppressed.

No idea what the repercussions would be if that change were made.

John
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@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++-request@ncar.ucar.edu
]





Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/02/27
Raw View
Stephen.Clamage@eng.sun.com (Steve Clamage) writes:

|>  In article fsf@gabi-soft.fr, kanze@gabi-soft.fr (J. Kanze) writes:
|>  >Stephen.Clamage@Eng (Steve Clamage) writes:
|>  >
|>  >|>  >(This leads to a further question: is a static_cast FROM void* legal?
|>  >|>  >Should it be?)
|>  >|>
|>  >|>  Yes, since a static cast can reverse most implicit conversions. (I think
|>  >|>  the only exception is reversing a conversion to a virtual base class.)
|>  >|>  In particular, you can static_cast any data pointer to void* and back to
|>  >|>  the original type and get a pointer that compares equal to the original.
|>  >
|>  >This answers the first question.  But should it be.  Practically, it
|>  >means that static_cast can be used to cast between arbitrary pointer
|>  >types, since there is an implicit cast from any pointer type to void*,
|>  >which will allow the compiler to invoke the static cast.  I don't think
|>  >that this is what the committee wanted, and IMHO, it breaks one of the
|>  >largest single reasons for the new cast syntax: a "sideways" cast within
|>  >a class hierarchy is no longer an error.
|>
|>  I'm not sure what you are trying to say.

That I'm confused:-).  Jason Merrill has since posted, stating that no
implicit conversions may occur on the argument of a static cast; I've
been unable to locate text to this effect in the draft.  This would
solve the problem.

|>  You can't use static_cast
|>  to cast directly sideways in a hierarchy. That is, given
|>   class VB { ... };
|>   class A : public virtual VB { ... };
|>   class B : public virtual VB { ... };
|>   class C : public A, public B { ... };
|>   A* pa = new C;
|>   B* pb = static_cast<B*>(pa); // ERROR

I know that this is the intent; it is one of the major motivations for
the new style casts. My impression (to be confirmed) is that in fact,
since a static_cast<B*>( void* ) is legal, and the compiler can
implicitly convert pa to a void*, this line becomes illegal.

If someone can show me the place in the standard which confirms Jason's
assertion, I'll be happy.  If not, it is an adequate solution (I think),
and perhaps should be adopted.

|>  An old-style cast here would not result in an error message (it is
|>  treated as a reinterpet_cast under the new rules, and is quietly accepted
|>  under the old rules), but would not give a useful answer. The static_cast
|>  must yield an error. Isn't that what we want?
|>
|>  You can write the "double cast"
|>   B* pb = static_cast<B*>(static_cast<void*>(pa));
|>  which is perhaps your complaint.

My complaint (or rather my worry -- let's not call it a complaint until
we're sure that I'm right) is that I think that the compiler will do the
inner static cast implicitly, without my writing it.

|>  But programmers must be educated
|>  never to write code like this, just as they must be educated to
|>  avoid old-style casts: you can easily wind up with syntactically
|>  valid code with unknown or even undefined semantics.
|>
|>  Safe casts in C++ may all be implicit. If you write an explicit cast,
|>  it should always be considered a flag that something unportable might be
|>  occuring. Many of the new-style casts are in fact safe if they compile.
|>  A "double cast", it seems to me, must always signal something dangerous
|>  and definitely unportable, even moreso than a reinterpret_cast.

Agreed.  The next step would be to make the opposite true: implicit
casts in C++ are always safe:-).  Of course, this would break some
common (and safe) C idioms: assigning an int to a char is definitly not
safe in the general case, but if the int is a return value from getc,
and I've already tested for EOF, it is safe, and is standard practice.

(This actually leads to a second question: is it guaranteed that I can
assign a non-EOF return value of getc, or istream::get, to a char, then
convert the char to unsigned char, and get the same results?  I don't
think so: the conversion to char can still be "lossy".  This is
definitly the case in C, in any case.)

Anyway, that's another question.  While I think that there are
weaknesses here, they're inherited from C, we're not going to fix them
in this version of the standard, and market presure will ensure that in
fact, my programs will work on all commercial compilers, even if the
standard doesn't guarantee it.

--
James Kanze      home:     kanze@gabi-soft.fr        +33 (0)1 39 55 85 62
                 office:   kanze@vx.cit.alcatel.fr   +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
     -- Conseils en informatique industrielle --
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@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++-request@ncar.ucar.edu
]





Author: Jason Merrill <jason@cygnus.com>
Date: 1997/02/28
Raw View
>>>>> John E Potter <jpotter@falcon.lhup.edu> writes:

> Jason Merrill (jason@cygnus.com) wrote:

> : Except that implicit conversions are not performed on the operand of
> : static_cast in order to find a type that can be cast from; the new-style
> : casts only perform a single conversion.

> I like that.  But does CD2 say that?

On the contrary, it does not say that implicit conversions are performed,
so they are not.

> I find in 4/5 that in some cases, standard conversions are suppressed
> in some contexts and that exceptions are given in the descriptions.

That paragraph is non-normative.

> In 5.2.9/4 exceptions are given for static_cast<void>.  It seems that
> these exceptions would not be required if all standard conversions
> were suppressed.

Those exceptions are to 5/8.

I will readily agree that the WP is unclear on this point.  However, the
various operators that do perform implicit conversions say the operands
"shall" be of some type.  I suggest that this implies the implicit
conversions, and no such language appears in the description of
static_cast.

Jason
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ 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++-request@ncar.ucar.edu                             ]





Author: kanze@gabi-soft.fr (J. Kanze)
Date: 1997/02/23
Raw View
Stephen.Clamage@Eng (Steve Clamage) writes:

|>  >(This leads to a further question: is a static_cast FROM void* legal?
|>  >Should it be?)
|>
|>  Yes, since a static cast can reverse most implicit conversions. (I think
|>  the only exception is reversing a conversion to a virtual base class.)
|>  In particular, you can static_cast any data pointer to void* and back to
|>  the original type and get a pointer that compares equal to the original.

This answers the first question.  But should it be.  Practically, it
means that static_cast can be used to cast between arbitrary pointer
types, since there is an implicit cast from any pointer type to void*,
which will allow the compiler to invoke the static cast.  I don't think
that this is what the committee wanted, and IMHO, it breaks one of the
largest single reasons for the new cast syntax: a "sideways" cast within
a class hierarchy is no longer an error.

On the other hand, if casting from void* requires a reinterpret_cast, we
have (or at least should have) a case where the semantics of
reinterpret_cast are NOT implementation defined.

--
James Kanze        +33 (0)1 39 55 85 62        email: kanze@gabi-soft.fr
GABI Software, Sarl., 22 rue Jacques-Lemercier, 78000 Versailles, France
     -- Conseils en informatique industrielle --
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ 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++-request@ncar.ucar.edu                             ]





Author: Valentin Bonnard <bonnardv@pratique.fr>
Date: 1997/02/24
Raw View
John E. Potter wrote:
> This has been a very confused thread.

I agree.

> I have no problem with any of the
> stuff on reinterpret_cast because the rules are clear in the section on
> that subject.  Reinterpret_cast is implementation defined, yet there are
> some things guarantied.  If the size and alignment of short are two
> and long are four then
>    reinterpret_cast<long*>(reinterpret_cast<short*>(longPtr))
> is a no-op.
>
>    [ No, an implementation may not return null or abort ]

Why should 'assert (reinterpret_cast<short*>(longPtr))' work ?

With tagged pointer, the actual value could be saved in
the tag and restored after.

> And the following is unspecified
>    reinterpret_cast<short*>(reinterpret_cast<long*>(shortPtr))

Right

> I can't find anything that says that
>    static_cast<short*>(static_cast<void*>(shortPtr))
> is a no-op.  The inner one is covered by standard conversions and I guess
> the outer one is covered by malloc.  It then seems to follow that
>    static_cast<short*>(static_cast<void*>(longPtr))
> is a no-op.  That seems to say that the answer to the original question
> is that they are the same.

IMO void type has been forgotten in the [expr.static.cast]
clause but the intent is quite clear; while well-formed
(no diagnostic needed), it's undefined behaviour to do
that.

> But what about
>    static_cast<long*>(static_cast<void*>(shortPtr))
> I can't find it as defined, undefined, unspecified, or implementation
> defined.  Is it unmentioned or can someone give me a pointer?  And what
> does unmentioned mean (pointer please)?

The same. static_cast should only be used to go back to the
dynamic type of the object (this is != const_cast where you
need not to know the dynamic type [dynamic constness] of
the object).

So while (1) is well-defined, (2) is not in the following:

class Base {};
class Der : public Base {};

Base        b;
const Base  cb;
Der         d;

void*       pv; // void is a direct base class for everything
Base*       pb;
const Base* pcb;
Der*        pd;

pcb = &b;
const_cast<Base*> (pcb); // OK

pcb = &cb;
const_cast<Base*> (pcb); // (1) well-formed, well-defined

pb = &d;
static_cast<Der*> (pb); // OK

pb = &b;
static_cast<Der*> (pb); // (2) well-formed, undefined

pv = &b;
static_cast<Der*> (pv); // (2b) well-formed, undefined

So the programmer must make sure static_cast doesn't go
under it's dynamic type in the class latice but there
aren't any such restrictions on const_cast.

> And a long unanswered question, "is
>    static_cast<short*>(longPtr)
> well-formed?"  The inner cast to void* is a standard conversion.  If so,
> it seems that (short*)longPtr is a static_cast.

Has I replied James, IMO it's not.

I wrote:

>
> I don't think so; the Dec 96 DWP says:
>
>   5.2.9  Static cast                                  [expr.static.cast]
>
> 3 Otherwise, the static_cast shall perform one of the conversions listed
>   below.   No  other  conversion  shall  be performed explicitly using a
>   static_cast.
>
> IMO it means that static_cast can't perform std conversions
> unless explicitly stated.
>
> So any* -> void* is a compile-time correct static_cast
> void* -> other* is a compile-time correct static_cast
>
> but any* -> other* is NOT

I'll add that the compilers I use things the same and if
it's not true then the static/reinterpret casts has the
practical value of a simple comment.

--

Valentin Bonnard
mailto:bonnardv@pratique.fr
http://www.pratique.fr/~bonnardv (Informations sur le C++ en Francais)
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ 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++-request@ncar.ucar.edu                             ]





Author: Jason Merrill <jason@cygnus.com>
Date: 1997/02/24
Raw View
>>>>> J Kanze <kanze@gabi-soft.fr> writes:

> This answers the first question.  But should it be.  Practically, it
> means that static_cast can be used to cast between arbitrary pointer
> types, since there is an implicit cast from any pointer type to void*,
> which will allow the compiler to invoke the static cast.

Except that implicit conversions are not performed on the operand of
static_cast in order to find a type that can be cast from; the new-style
casts only perform a single conversion.

Jason
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ 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++-request@ncar.ucar.edu                             ]





Author: Stephen.Clamage@eng.sun.com (Steve Clamage)
Date: 1997/02/25
Raw View
In article fsf@gabi-soft.fr, kanze@gabi-soft.fr (J. Kanze) writes:
>Stephen.Clamage@Eng (Steve Clamage) writes:
>
>|>  >(This leads to a further question: is a static_cast FROM void* legal?
>|>  >Should it be?)
>|>
>|>  Yes, since a static cast can reverse most implicit conversions. (I think
>|>  the only exception is reversing a conversion to a virtual base class.)
>|>  In particular, you can static_cast any data pointer to void* and back to
>|>  the original type and get a pointer that compares equal to the original.
>
>This answers the first question.  But should it be.  Practically, it
>means that static_cast can be used to cast between arbitrary pointer
>types, since there is an implicit cast from any pointer type to void*,
>which will allow the compiler to invoke the static cast.  I don't think
>that this is what the committee wanted, and IMHO, it breaks one of the
>largest single reasons for the new cast syntax: a "sideways" cast within
>a class hierarchy is no longer an error.

I'm not sure what you are trying to say. You can't use static_cast
to cast directly sideways in a hierarchy. That is, given
 class VB { ... };
 class A : public virtual VB { ... };
 class B : public virtual VB { ... };
 class C : public A, public B { ... };
 A* pa = new C;
 B* pb = static_cast<B*>(pa); // ERROR
An old-style cast here would not result in an error message (it is
treated as a reinterpet_cast under the new rules, and is quietly accepted
under the old rules), but would not give a useful answer. The static_cast
must yield an error. Isn't that what we want?

You can write the "double cast"
 B* pb = static_cast<B*>(static_cast<void*>(pa));
which is perhaps your complaint. But programmers must be educated
never to write code like this, just as they must be educated to
avoid old-style casts: you can easily wind up with syntactically
valid code with unknown or even undefined semantics.

Safe casts in C++ may all be implicit. If you write an explicit cast,
it should always be considered a flag that something unportable might be
occuring. Many of the new-style casts are in fact safe if they compile.
A "double cast", it seems to me, must always signal something dangerous
and definitely unportable, even moreso than a reinterpret_cast.

I think the only legitimate use for a "double cast" is when one of
the pair is a const_cast, since the other three casts cannot remove
constness. But removing const is in general dangerous, of course.
You must somehow know that the object referred to is not really const.
---
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ 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++-request@ncar.ucar.edu                             ]





Author: rmashlan@r2m.com (Robert Mashlan)
Date: 1997/02/12
Raw View
Stephen.Clamage@eng.sun.com (Steve Clamage) wrote:

>I was speaking always of the general case. Given pointers to unrelated
>types T1 and T2, converting a T2* to a T1* is in general not portable
>in C or in C++. (And that was the original question that started this
>thread.) In C++ the conversion can be performed in general only
>with a reinterpret_cast. The conversion semantics of a reinterpret_cast
>are defined by the implementation, and so will be in general non-portable.
>(Suppose the pointers are different sizes, and you convert a big
>pointer to a little pointer. The standard can't make any promises
>about the result.)
>
>Malloc is not a general case, but a specific case. Type void* is also
>a special case. Type void* is required to have the characteristic that
>any data pointer can be converted to void* without loss of information.
>The requirements on malloc include that it return a pointer to a block
>of memory suitably aligned for any object of the requested size, and
>that the void* returned be convertible to a pointer to any such data
>object.
>
>Your malloc example is correct and portable code, because of the
>special requirements on malloc and void*. The conversion is
>performed via a static_cast.

Hello Steve,

Ok, that makes sense.  Now consider the following code:

   struct T { unsigned refcnt; } ;

   T* CreateT( char *text )
   {
       T* tp static_cast<T*>( malloc( sizeof(T) + strlen(text) + 1 );

       char* cp = static_cast<char*>( static_cast<void*>( tp+1)  );
       strcpy(cp,text);
       return tp;
   }

In what way could this code not be portable?    The potential
problems, as I see it are:

1)  The requested size given to malloc could cause a problem with
producing a pointer of suitable alignment for T.  If this is the case,
the requested size could be rounded up such that size % sizeof(T) ==
0, which would make the memory block of suitable alignment for an
array of T. (Is it conceivable for an implementation to require this?)

2) There is some completely oddball implementation that needs char* to
be more strictly aligned than T*.   I would think this possibility is
not worth worrying about.

If you make the leap of faith that 2 is not going to be a problem,
should I stick with using the double static_cast to convert the T* to
char*, or would reinterpret_cast do the same job, to have the example
be portable?

A related question -- in terms of new style casts, what does the old
style cast  cp = (char *)tp  do?  Is this a double static_cast or a
single reinterpret_cast, if in fact the two are different?

rm



---
Robert Mashlan  R2M Software  rmashlan@r2m.com
Internet Resources for Windows Developers http://www.r2m.com/windev/
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@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++-request@ncar.ucar.edu
]





Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/02/13
Raw View
rmashlan@r2m.com (Robert Mashlan) writes:

|>  Stephen.Clamage@eng.sun.com (Steve Clamage) wrote:
|>
|>  >For unrelated pointers, assuming it makes sense to perform the
|>  >conversion at all, you have no choice but to use reinterpret_cast.
|>
|>  The two types are unrelated.   Should this work on an implementation
|>  where char * uses a different representation than a pointer to a
|>  class?
|>
|>  The situation where I am using this is as follows:
|>
|>  class string_ref {
|>     private:
|>         unsigned  refcnt;
|>     public:
|>         char *textp() {  return reinterpret_cast<char*>(this+1); }
|>         static string_ref* Create( const char *text ) {
|>              void *p = malloc( sizeof(string_ref) + strlen(text) + 1 );
|>              return new(p) string_ref(text);
|>         }
|>     private:
|>         string_ref( const char *text ) : refcnt(1) {
|>            strcpy( textp(), text );
|>         }
|>  };
|>
|>  Assuming that on any given implementation, the alignment restriction
|>  for string_ref* is stricter or equal to a char* (it would be really
|>  hard to imagine where this wouldn't be the case),  is the
|>  reinterpret_cast be properly being used, in that it should do what I
|>  intend?

At least in the C standard, char* is a special case, and is required to
be compatible with void*.  So you are safe.  The real question: what
happens here if the results are wchar_t*, instead of char*?
Theorecally, wchar_t could have stricter alignment requirements than
string_ref, and the wchar_t* could be misaligned.

--
James Kanze      home:     kanze@gabi-soft.fr        +33 (0)1 39 55 85 62
                 office:   kanze@vx.cit.alcatel.fr   +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
     -- Conseils en informatique industrielle --
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ 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++-request@ncar.ucar.edu                             ]





Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/02/13
Raw View
rmashlan@r2m.com (Robert Mashlan) writes:

|>  Stephen.Clamage@eng.sun.com (Steve Clamage) wrote:
|>
|>  >I was speaking always of the general case. Given pointers to unrelated
|>  >types T1 and T2, converting a T2* to a T1* is in general not portable
|>  >in C or in C++. (And that was the original question that started this
|>  >thread.) In C++ the conversion can be performed in general only
|>  >with a reinterpret_cast. The conversion semantics of a reinterpret_cast
|>  >are defined by the implementation, and so will be in general non-portable.
|>  >(Suppose the pointers are different sizes, and you convert a big
|>  >pointer to a little pointer. The standard can't make any promises
|>  >about the result.)
|>  >
|>  >Malloc is not a general case, but a specific case. Type void* is also
|>  >a special case. Type void* is required to have the characteristic that
|>  >any data pointer can be converted to void* without loss of information.
|>  >The requirements on malloc include that it return a pointer to a block
|>  >of memory suitably aligned for any object of the requested size, and
|>  >that the void* returned be convertible to a pointer to any such data
|>  >object.
|>  >
|>  >Your malloc example is correct and portable code, because of the
|>  >special requirements on malloc and void*. The conversion is
|>  >performed via a static_cast.
|>
|>  Hello Steve,
|>
|>  Ok, that makes sense.  Now consider the following code:
|>
|>     struct T { unsigned refcnt; } ;
|>
|>     T* CreateT( char *text )
|>     {
|>         T* tp static_cast<T*>( malloc( sizeof(T) + strlen(text) + 1 );
|>
|>         char* cp = static_cast<char*>( static_cast<void*>( tp+1)  );
|>         strcpy(cp,text);
|>         return tp;
|>     }
|>
|>  In what way could this code not be portable?

No.  The static_cast< T* > could add tagging information, which would be
maintained through further static_cast's.

Presumably, if you rewrote the line with the two static_cast's:

    char* cp = reinterpret_cast< char* >( tp + 1 ) ;

the implementation would also have the right to generate a null pointer,
either because the implementation's definition of reinterpret_cast is
to always return a null pointer, or because the implementation checks
the tagging information in reinterpret_cast, and generates a null
pointer in case of mismatch.

Such an implementation may be complying with the letter of the standard,
but it is certainly going against the intent.  I actually have code like
the above (with the reinterpret_cast); I consider it "portable enough",
in that I don't think it will fail on any "reasonable" implementation.

|>  The potential
|>  problems, as I see it are:
|>
|>  1)  The requested size given to malloc could cause a problem with
|>  producing a pointer of suitable alignment for T.  If this is the case,
|>  the requested size could be rounded up such that size % sizeof(T) ==
|>  0, which would make the memory block of suitable alignment for an
|>  array of T. (Is it conceivable for an implementation to require this?)

malloc must produce a pointer suitably aligned for all types.  In all
cases.

|>  2) There is some completely oddball implementation that needs char* to
|>  be more strictly aligned than T*.   I would think this possibility is
|>  not worth worrying about.

Agreed.  I seem to recall some sort of discussion in comp.std.c which
implied that the alignment requirement for T couldn't be greater than
"sizeof( T )".  (I think it had to do with the fact that legal pointer
arithmetic could result in misaligned pointers otherwise.)  Since
sizeof( char ) is by definition 1, and all sizeof must be integral, I
think that you can conclude that an implementation in which char
requires more strict alignment than some type T is not conforming.
(That is to say: there is no such rule directly, but any possible
implementation with this characteristic would fail on some other rule.)

|>  If you make the leap of faith that 2 is not going to be a problem,
|>  should I stick with using the double static_cast to convert the T* to
|>  char*, or would reinterpret_cast do the same job, to have the example
|>  be portable?

I'd go with the reinterpret_cast.  IMHO, there is no way that is
guaranteed portable, but the reinterpret_cast will work on any
implementation that conforms to the intent of the standard, whereas the
double static_cast could fail on an implementation with tagged pointers.

|>  A related question -- in terms of new style casts, what does the old
|>  style cast  cp = (char *)tp  do?  Is this a double static_cast or a
|>  single reinterpret_cast, if in fact the two are different?

A reinterpret_cast, I think.

--
James Kanze      home:     kanze@gabi-soft.fr        +33 (0)1 39 55 85 62
                 office:   kanze@vx.cit.alcatel.fr   +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
     -- Conseils en informatique industrielle --
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ 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++-request@ncar.ucar.edu                             ]





Author: Stephen.Clamage@Eng.Sun.COM (Steve Clamage)
Date: 1997/02/13
Raw View
In article 43088252@library.airnews.net, rmashlan@r2m.com (Robert Mashlan) writes:
>
>Ok, that makes sense.  Now consider the following code:
>
>   struct T { unsigned refcnt; } ;
>
>   T* CreateT( char *text )
>   {
>       T* tp = static_cast<T*>( malloc( sizeof(T) + strlen(text) + 1 );
>
>       char* cp = static_cast<char*>( static_cast<void*>( tp+1)  );
>       strcpy(cp,text);
>       return tp;
>   }
>
>In what way could this code not be portable?

Anyone who wrote code like that who works for me would be shot^H^H^H
encouraged to write something more straightforward and reliable. Who can
understand and maintain code that depends on unnamed and invisible parts
of a struct? How will you ensure that anyone using a T object will never
do anything based on sizeof T? (Those are rhetorical questions. I don't
really want to know.)

Also please note that T is not really a type, since no two instances of
a T have the same size or structure, except accidently. Yet T looks like
a type and the compiler will treat it as a type. Why would this be an
advantage, except for entries in an Obfuscated C++ contest?

I assume you want to avoid for efficiency reasons the obvious and portable
solution of storing a pointer to an allocated object. There are ways
around that problem without resorting to code that is likely to get
you in trouble down the road.

>    The potential
>problems, as I see it are:
>
>1)  The requested size given to malloc could cause a problem with
>producing a pointer of suitable alignment for T. If this is the case,
>the requested size could be rounded up such that size % sizeof(T) ==
>0, which would make the memory block of suitable alignment for an
>array of T.

That is not allowed to be an issue. When you request N bytes from
malloc, it returns a pointer to a region of memory that contains
at least N bytes, and which is suitably aligned for any object
that can fit in N bytes. That is an absolute requirement on malloc.
(Unless malloc fails because the free store cannot supply a contiguous
region of suitable size.)  C and C++ assume that smaller basic types
never have stricter alignment requirements than larger basic types.
(I don't know if that is stated explicitly in the standard, but it
it follows implicitly from various requirements.)

>A related question -- in terms of new style casts, what does the old
>style cast  cp = (char *)tp  do?  Is this a double static_cast or a
>single reinterpret_cast, if in fact the two are different?

It is a reinterpret_cast. The double static_cast (T1 to void* to T2,
where T1 and T2 are unrelated and neither is void*) has no portable
semantics. (Neither does reinterpret_cast, of course.)

As a practical matter, when T1 is pointer-to-struct and T2 is char*, the
double cast is likely to behave as you expect, and is likely to have the
same result as reinterpret_cast. If that happens not to be true on
some platform, you are out of luck. The standard does not promise that
it is so, as far as I can tell.
---
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@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++-request@ncar.ucar.edu
]





Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/02/13
Raw View
rmashlan@r2m.com (Robert Mashlan) writes:

|>  Stephen.Clamage@Eng.Sun.COM (Steve Clamage) wrote:
|>
|>  >I'm not trying to make this sound difficult. If you work on a
|>  >system where pointers are all the same size and have the same
|>  >representation, casting to another pointer type should work the
|>  >way you expect (as long as you don't violate alignment restrictions).
|>  >
|>  >But if you are asking if code like that is portable, the answer
|>  >has to be "no".
|>
|>  I'm still not clear on this.
|>
|>  One of the things that is confusing me, is that in C, you can write:
|>
|>        typedef struct { ... } T;
|>
|>        T* p = (T* )malloc( sizeof(T) );
|>
|>  malloc is supposed to return a void* that is of suitable alignment for
|>  any data object, and I had thought this is portable code.

It is, BECAUSE malloc is required to return a pointer which can
correctly be cast to any type.  This is defined because of a property of
malloc, however, and NOT because the cast is inherantly portable.  It is
up to malloc to ensure that the *particular* void* value that it returns
is valid for the cast; this is not a characteristic of void*'s in
general.

|>  I think you said in another post that you can't use static_cast to
|>  cast a void * to a T*,  so this would imply that the C style cast
|>  above is a reinterpret_cast.   Since you say the results of using
|>  reinterpret_cast are non-portable, that means the code above would be
|>  non-portable in C++.   Is this conclusion correct?

I think he said that you can use static_cast to cast from void* to T*.
I presume that in this case, the semantics of the static_cast are
usually the same as those of reinterpret_cast, but I don't think that
this is required.  See my example using tagged pointers in another
posting.  (In such an implementation, of course, malloc must return an
untagged pointer; the static_cast would add the tagging information.)

--
James Kanze      home:     kanze@gabi-soft.fr        +33 (0)1 39 55 85 62
                 office:   kanze@vx.cit.alcatel.fr   +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
     -- Conseils en informatique industrielle --
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@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++-request@ncar.ucar.edu
]





Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/02/13
Raw View
Valentin Bonnard <bonnardv@pratique.fr> writes:

|>  Steve Clamage wrote:
|>  >
|>  > In article 145300615@library.airnews.net, rmashlan@r2m.com (Robert
|>  > Mashlan) writes:
|>  > >
|>  > >Assuming that type T2 has stricter or equal alignment restrictions
|>  > >than type T1,  Is it possible that the following two expressions
|>  > >return different results?
|>  > >
|>  > >        T2*  pt2;
|>  > >
|>  > >        T1* pt1 = static_cast<T1 *>(static_cast<void *>(pt2));
|>  > >
|>  > >vs.
|>  > >
|>  > >        T1* pt1 = reinterpret_cast<T1*>(pt2);
|>  >
|>  > It is always possible for static_cast and reinterpret_cast to produce
|>  > different results, because the results of a reinterpret_cast are
|>  > implementation-defined.
|>
|>  In practice I don't see any reasons why they may return different
|>  result.

In an implementation with tagged pointers, the static_cast from void*
could verify the initial tag, and generate a core dump (or some
equivalent) if they didn't match.  (The code invokes undefined
behavior.)  Any reasonable implementation of reinterpret_cast with
tagged pointers would have the cast throw the tagging information away.

|>  > The example static_cast above is invalid if T1 and T2 are unrelated
|>  > types, which they appear to be. The compiler must issue a diagnostic.
|>
|>  Are you sure ? (I mean after re-reading the original post.)

I think that Steve Clamage was referring to some other example, which
somehow got cut, and not the actual example presented here.  In another
posting, he specifically confirms that static_cast to and from void* are
legal.  (On the other hand, the above example with static_cast does
invokee undefined behavior, since the type extracted from the void* is
not the same as the type inserted.)

--
James Kanze      home:     kanze@gabi-soft.fr        +33 (0)1 39 55 85 62
                 office:   kanze@vx.cit.alcatel.fr   +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
     -- Conseils en informatique industrielle --
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@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++-request@ncar.ucar.edu
]





Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/02/13
Raw View
rmashlan@r2m.com (Robert Mashlan) writes:

|>  Question:
|>
|>  Assuming that type T2 has stricter or equal alignment restrictions
|>  than type T1,  Is it possible that the following two expressions
|>  return different results?
|>
|>          T2*  pt2;
|>
|>          T1* pt1 = static_cast<T1 *>(static_cast<void *>(pt2));
|>
|>  vs.
|>
|>          T1* pt1 = reinterpret_cast<T1*>(pt2);

Yes.  The first is undefined behavior, the second is implementation
defined.  The implementation is not required to define the undefined
behavior.

As a simple example of how this could occur, think of an architecture
with tagged pointers.  Static_cast'ing to void* would retain the tagging
information, and static_cast'ing the void* could check it, and generate
a core dump in case of incompatibility.  Such an implementation would
probably define reinterpret_cast to throw away the tagging information,
however.

I don't think that the implementation's freedom in defining
reinterpret_cast goes to the point of generating a core dump.  It could
certainly define that the results of a reinterpret_cast is always a null
pointer, however, although I would consider this a very poor
implementation, which complied only to the letter of the standard, and
not its intent.

--
James Kanze      home:     kanze@gabi-soft.fr        +33 (0)1 39 55 85 62
                 office:   kanze@vx.cit.alcatel.fr   +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
     -- Conseils en informatique industrielle --
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@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++-request@ncar.ucar.edu
]





Author: rmashlan@r2m.com (Robert Mashlan)
Date: 1997/02/15
Raw View
Stephen.Clamage@Eng.Sun.COM (Steve Clamage) wrote:

>Anyone who wrote code like that who works for me would be shot^H^H^H
>encouraged to write something more straightforward and reliable. Who can
>understand and maintain code that depends on unnamed and invisible parts
>of a struct? How will you ensure that anyone using a T object will never
>do anything based on sizeof T? (Those are rhetorical questions. I don't
>really want to know.)

Steve, even if you don't want to know - I've used them for two
different applications,  1) as a a string reference for a string like
class, and 2) as a header for a memory blocks in a  heap.  The
resulting class are not be derived from, and in my implementation of
them, they have private constructors and destructors, and object
creation and destruction is handled through other members functions.
In practice, these types of objects are only used privately by some
other object, and are not for public consumption for the rest of the
program.

In any case, what kinds of things are you thinking of that someone
would want to use sizeof(T) with the class?    Even with normal
classes, there are very few uses for it.

>Also please note that T is not really a type, since no two instances of
>a T have the same size or structure, except accidently. Yet T looks like
>a type and the compiler will treat it as a type. Why would this be an
>advantage, except for entries in an Obfuscated C++ contest?

>I assume you want to avoid for efficiency reasons the obvious and portable
>solution of storing a pointer to an allocated object. There are ways
>around that problem without resorting to code that is likely to get
>you in trouble down the road.

The reason that I prefer this approach is that it's more object
oriented.  I think the alternatives you have in mind don't even use
objects -- such as storing a reference count in the first few chars of
an allocated char array (which would also involve a cast between two
types, unless you're happy with a char sized reference count)  The
intent of my approach is to create a class which binds all the data
together -- rather than write a bunch of code not as tightly bound
with the data involved.  In my opinion, the alternatives are just
difficult as this approach.

>>A related question -- in terms of new style casts, what does the old
>>style cast  cp = (char *)tp  do?  Is this a double static_cast or a
>>single reinterpret_cast, if in fact the two are different?
>
>It is a reinterpret_cast. The double static_cast (T1 to void* to T2,
>where T1 and T2 are unrelated and neither is void*) has no portable
>semantics. (Neither does reinterpret_cast, of course.)
>
>As a practical matter, when T1 is pointer-to-struct and T2 is char*, the
>double cast is likely to behave as you expect, and is likely to have the
>same result as reinterpret_cast. If that happens not to be true on
>some platform, you are out of luck. The standard does not promise that
>it is so, as far as I can tell.

I'm still not understanding why this would be so -- is there something
magical about the void* returned from malloc, as compared to a void*
(assumed to have the appropriate alignment) that points to a middle of
a malloc'ed block?

Robert


---
Robert Mashlan  R2M Software  rmashlan@r2m.com
Internet Resources for Windows Developers http://www.r2m.com/windev/
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ 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++-request@ncar.ucar.edu                             ]





Author: Valentin Bonnard <bonnardv@pratique.fr>
Date: 1997/02/15
Raw View
Steve Clamage wrote:
> Yes, since a static cast can reverse most implicit conversions. (I think
> the only exception is reversing a conversion to a virtual base class.)

There is annother special case but it's not clearly addressed
in the april 95 DWP (in fact I think it's not addressed at
all :-( ); it's static_cast<T*>(bool (p)).

This seems legal, but its behaviour isn't described in the draft;
does it mean that it's unspecified behaviour ?

Note: g++ allow it and convert true to a pointer with value 1,
like (T*) 1.

--

Valentin Bonnard
mailto:bonnardv@pratique.fr
http://www.pratique.fr/~bonnardv (Informations sur le C++ en Francais)
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ 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++-request@ncar.ucar.edu                             ]





Author: Stephen.Clamage@eng.sun.com (Steve Clamage)
Date: 1997/02/16
Raw View
In article 2530703@library.airnews.net, rmashlan@r2m.com (Robert
Mashlan) writes:
>Stephen.Clamage@Eng.Sun.COM (Steve Clamage) wrote:
>
>>As a practical matter, when T1 is pointer-to-struct and T2 is char*, the
>>double cast is likely to behave as you expect, and is likely to have the
>>same result as reinterpret_cast. If that happens not to be true on
>>some platform, you are out of luck. The standard does not promise that
>>it is so, as far as I can tell.
>
>I'm still not understanding why this would be so -- is there something
>magical about the void* returned from malloc, as compared to a void*
>(assumed to have the appropriate alignment) that points to a middle of
>a malloc'ed block?

The cast from the void* returned from malloc works (within specified
limits) because a language rule says it must. If you call malloc to get
N bytes, the returned address must be suitably aligned for any data type
of N bytes. If you adjust the pointer to point inside that memory space,
whether it is suitably aligned for your particular purpose depends on the
details of the platform, the implementation, and the particular value of
the pointer.

Many popular platforms feature byte-addressed memory, and pointers which
are simple addresses, all pointers being the same size and having the
same representation. For such a platform, it would be reasonable to
expect the result of any reinterpret_cast, or any static_cast to or
from void*, to leave the bits unchanged. It would also be reasonable to
expect that if you obey alignment constraints, any pointer conversion
will have a simple and predictable result.

Properties like "all pointers look alike", "all pointers are the same size",
"reinterpret_cast doesn't change the bits" are not universal, however,
and the standard doesn't require them to hold.

One of the major reasons for adding the four new cast types to C++ was
to make it easy to find casts in a program, so that each cast could
be inspected when porting code to determine whether it makes unportable
assumptions. All safe and portable type conversions can occur implicitly.
Any conversion requiring a cast is potentially unportable or is otherwise
potentially dangerous.
---
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ 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++-request@ncar.ucar.edu                             ]





Author: rmashlan@r2m.com (Robert Mashlan)
Date: 1997/02/16
Raw View
James Kanze <james-albert.kanze@vx.cit.alcatel.fr> wrote:

>As a simple example of how this could occur, think of an architecture
>with tagged pointers.  Static_cast'ing to void* would retain the tagging
>information, and static_cast'ing the void* could check it, and generate
>a core dump in case of incompatibility.  Such an implementation would
>probably define reinterpret_cast to throw away the tagging information,
>however.

I can see your point that such an implementation be allowed by the
standard, but I'm wondering what would be the practicality or
usefulness of such an implementation?  Since in C++, the majority of
type checking is done at compile time (with the exception of RTTI).
The only usefulness I see to to thrawt the programmer from doing
certain types of tricky casting, which they should know what they are
doing in the first place.

rm



---
Robert Mashlan  R2M Software  rmashlan@r2m.com
Internet Resources for Windows Developers http://www.r2m.com/windev/
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@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++-request@ncar.ucar.edu
]





Author: Valentin Bonnard <bonnardv@pratique.fr>
Date: 1997/02/16
Raw View
John E. Potter wrote:
>int i;
>int* pi(&i);
[...]
> pd = static_cast<double*>(static_cast<void*>(pi)); // follows
>
> It even seems that since the inner cast is a standard conversion, it
> could be omitted.  It then follows that any reinterpret_cast of
> pointers, other than pointer to member, can be performed with
> static_cast.  What am I missing?

You mean that the expression can be rewritten:

static_cast<double*> (implicit_cast<void*> (pi))

[I assume you understand the meaning of implicit_cast,
 which is sometimes called convert and is not std]

Right, but

static_cast<double*> (p)

is ill-formed. static_cast should only be used to get back to
the original pointer type, but the compiler in general can't
know that (this case [your example] is very special, and won't
occur in real programs).

Also you are missing conversion integer -> ptr and
ptr -> integer which can only be done with reinterpret_cast
or an old style cast.

--

Valentin Bonnard
mailto:bonnardv@pratique.fr
http://www.pratique.fr/~bonnardv (Informations sur le C++ en Francais)
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@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++-request@ncar.ucar.edu
]





Author: Alexandre Oliva <oliva@dcc.unicamp.br>
Date: 1997/02/17
Raw View
Valentin Bonnard writes:

> Steve Clamage wrote:
>> Yes, since a static cast can reverse most implicit conversions. (I think
>> the only exception is reversing a conversion to a virtual base class.)

> There is annother special case but it's not clearly addressed
> in the april 95 DWP (in fact I think it's not addressed at
> all :-( ); it's static_cast<T*>(bool (p)).

The Nov'96 DWP explicitly disallows this in [expr.static.cast]:

6 The  inverse  of any standard conversion sequence (_conv_), other than
  [...]  boolean (_conv.bool_) conversions,  can be performed explicitly
  using static_cast [...]

--
Alexandre Oliva
mailto:oliva@dcc.unicamp.br mailto:aoliva@acm.org
Universidade Estadual de Campinas, SP, Brasil
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ 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++-request@ncar.ucar.edu                             ]





Author: jpotter@falcon.lhup.edu (John E. Potter)
Date: 1997/02/18
Raw View
Fergus Henderson (fjh@murlibobo.cs.mu.OZ.AU) wrote:
: James Kanze <james-albert.kanze@vx.cit.alcatel.fr> writes:
: >Stephen.Clamage@eng.sun.com (Steve Clamage) writes:
: >|>  rmashlan@r2m.com (Robert Mashlan) writes:
: >|>  >        T2*  pt2;
: >|>  >        T1* pt1 = static_cast<T1 *>(static_cast<void *>(pt2));
: >|>  The example static_cast above is invalid if T1 and T2 are unrelated
: >|>  types, which they appear to be. The compiler must issue a diagnostic.
: >Something I don't understand: how does the fact that T1 and T2 are or
: >are not related affect the static_cast's in the example?  There is no
: >direct static_cast between the two classes, only to and from void*.
: No, a direct static_cast between a pointer to a derived class and
: a pointer to its base class is fine (in either direction).
: See [expr.static.cast] paragraphs 6-8.

See also paragraph 9 which allows the above cast from void* to any T1*.

: >(This leads to a further question: is a static_cast FROM void* legal?
: Yes, it is well-formed, since that's the inverse of a standard conversion.
: >Should it be?)
: Yes.

This has been a very confused thread.  I have no problem with any of the
stuff on reinterpret_cast because the rules are clear in the section on
that subject.  Reinterpret_cast is implementation defined, yet there are
some things guarantied.  If the size and alignment of short are two
and long are four then
   reinterpret_cast<long*>(reinterpret_cast<short*>(longPtr))
is a no-op.

   [ No, an implementation may not return null or abort ]

And the following is unspecified
   reinterpret_cast<short*>(reinterpret_cast<long*>(shortPtr))

I can't find anything that says that
   static_cast<short*>(static_cast<void*>(shortPtr))
is a no-op.  The inner one is covered by standard conversions and I guess
the outer one is covered by malloc.  It then seems to follow that
   static_cast<short*>(static_cast<void*>(longPtr))
is a no-op.  That seems to say that the answer to the original question
is that they are the same.

But what about
   static_cast<long*>(static_cast<void*>(shortPtr))
I can't find it as defined, undefined, unspecified, or implementation
defined.  Is it unmentioned or can someone give me a pointer?  And what
does unmentioned mean (pointer please)?

And a long unanswered question, "is
   static_cast<short*>(longPtr)
well-formed?"  The inner cast to void* is a standard conversion.  If so,
it seems that (short*)longPtr is a static_cast.

Any enlightenment appreciated,
John
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ 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++-request@ncar.ucar.edu                             ]





Author: "Bill Wade" <bill.wade@stoner.com>
Date: 1997/02/19
Raw View
Steve Clamage <Stephen.Clamage@Eng.Sun.COM> wrote in article

> C and C++ assume that smaller basic types
> never have stricter alignment requirements than larger basic types.
> (I don't know if that is stated explicitly in the standard, but it
> it follows implicitly from various requirements.)

I'd never noticed that.  Is there a simple example (in either language)
which shows why my machine can't have
   sizeof(int) == 2
   sizeof(float) == 3
and require 2 char alignment for ints and no particular alignment (1 char)
for floats?
  union{ int a; float b; }
has sizeof == 4, but that seems ok.  From the point of the memcpy rules it
seems to have the same semantics as
  union{ int a; char b[3]; }
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@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++-request@ncar.ucar.edu
]





Author: Valentin Bonnard <bonnardv@pratique.fr>
Date: 1997/02/21
Raw View
Robert Mashlan wrote:
>
> James Kanze <james-albert.kanze@vx.cit.alcatel.fr> wrote:
>
> >As a simple example of how this could occur, think of an architecture
> >with tagged pointers.  Static_cast'ing to void* would retain the tagging
> >information, and static_cast'ing the void* could check it, and generate
> >a core dump in case of incompatibility.  Such an implementation would
> >probably define reinterpret_cast to throw away the tagging information,
> >however.
>
> I can see your point that such an implementation be allowed by the
> standard, but I'm wondering what would be the practicality or
> usefulness of such an implementation?  Since in C++, the majority of
> type checking is done at compile time (with the exception of RTTI).
> The only usefulness I see to to thrawt the programmer from doing
> certain types of tricky casting, which they should know what they are
> doing in the first place.

For debugging, not to anoy the programmer. While the programmer
may be aware of the fact that casts are dangerous, he certainly
also know that accessing arrays out of bound is bad and he
sometimes does so :-(

Because static_cast really means cast BACK to the original
lost pointer type, it could be a plus to check that in an
interpretor.

OTOH, if an implementation did the same with reinterpret_cast,
it would break many programs since reinterpret_cast is
typically used in non-portables ways.

void* pv;

int i;
pv = &i; // loose type information

void  foo ()
{
    char  c;
    void* pv = &c;

....
    int*  pi = static_cast<int*>(pv); // Oops ! wrong pv
    *pi = 4; // trash the stack frame
}

Such an implementation would detect this bug before the
program crash misteriously.

--

Valentin Bonnard
mailto:bonnardv@pratique.fr
http://www.pratique.fr/~bonnardv (Informations sur le C++ en Francais)
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@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++-request@ncar.ucar.edu
]





Author: rmashlan@r2m.com (Robert Mashlan)
Date: 1997/02/11
Raw View
Stephen.Clamage@eng.sun.com (Steve Clamage) wrote:

>For unrelated pointers, assuming it makes sense to perform the
>conversion at all, you have no choice but to use reinterpret_cast.

The two types are unrelated.   Should this work on an implementation
where char * uses a different representation than a pointer to a
class?

The situation where I am using this is as follows:

class string_ref {
   private:
       unsigned  refcnt;
   public:
       char *textp() {  return reinterpret_cast<char*>(this+1); }
       static string_ref* Create( const char *text ) {
            void *p = malloc( sizeof(string_ref) + strlen(text) + 1 );
            return new(p) string_ref(text);
       }
   private:
       string_ref( const char *text ) : refcnt(1) {
          strcpy( textp(), text );
       }
};

Assuming that on any given implementation, the alignment restriction
for string_ref* is stricter or equal to a char* (it would be really
hard to imagine where this wouldn't be the case),  is the
reinterpret_cast be properly being used, in that it should do what I
intend?

---
Robert Mashlan  R2M Software  rmashlan@r2m.com
Internet Resources for Windows Developers http://www.r2m.com/windev/



[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ 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++-request@ncar.ucar.edu                             ]





Author: jpotter@falcon.lhup.edu (John E. Potter)
Date: 1997/02/11
Raw View
Steve Clamage (Stephen.Clamage@eng.sun.com) wrote:
: In article 145300615@library.airnews.net, rmashlan@r2m.com (Robert
: Mashlan) writes:
: >
: >Assuming that type T2 has stricter or equal alignment restrictions
: >than type T1,  Is it possible that the following two expressions
: >return different results?
: >
: >        T2*  pt2;
: >
: >        T1* pt1 = static_cast<T1 *>(static_cast<void *>(pt2));
: >
: >vs.
: >
: >        T1* pt1 = reinterpret_cast<T1*>(pt2);

: It is always possible for static_cast and reinterpret_cast to produce
: different results, because the results of a reinterpret_cast are
: implementation-defined.

: The example static_cast above is invalid if T1 and T2 are unrelated
: types, which they appear to be. The compiler must issue a diagnostic.

: If T1 and T2 are pointers into a class hierarchy, you should not
: use reinterpret cast, since it might not perform a needed pointer
: adjustment. You should use either static_cast or dynamic_cast.

: For unrelated pointers, assuming it makes sense to perform the
: conversion at all, you have no choice but to use reinterpret_cast.

I agree that what you say should be correct; however, I can't find it
in the FCD.  I have been known to miss things, can you help?

int i;
int* pi(&i);

void* pv(pi);  // 4.10[conv.ptr]/2

pv = static_cast<void*>(pi); // 5.2.9[expr.static.cast]/2

double* pd(static_cast<double*>(pv));  // 5.2.9[expr.static.cast]/6

  // void* to double* is the inverse of standard conversion 4.10/2 which
  // is not excluded in 5.2.9/6 nor the following restrictions.

pd = static_cast<double*>(static_cast<void*>(pi)); // follows

It even seems that since the inner cast is a standard conversion, it
could be omitted.  It then follows that any reinterpret_cast of
pointers, other than pointer to member, can be performed with
static_cast.  What am I missing?

TIA,
John


[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ 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++-request@ncar.ucar.edu                             ]





Author: rmashlan@r2m.com (Robert Mashlan)
Date: 1997/02/11
Raw View
Question:

Assuming that type T2 has stricter or equal alignment restrictions
than type T1,  Is it possible that the following two expressions
return different results?

        T2*  pt2;

        T1* pt1 = static_cast<T1 *>(static_cast<void *>(pt2));

vs.

        T1* pt1 = reinterpret_cast<T1*>(pt2);

---
Robert Mashlan  R2M Software  rmashlan@r2m.com
Internet Resources for Windows Developers http://www.r2m.com/windev/



[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ 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++-request@ncar.ucar.edu                             ]





Author: rmashlan@r2m.com (Robert Mashlan)
Date: 1997/02/11
Raw View
Stephen.Clamage@Eng.Sun.COM (Steve Clamage) wrote:

>I'm not trying to make this sound difficult. If you work on a
>system where pointers are all the same size and have the same
>representation, casting to another pointer type should work the
>way you expect (as long as you don't violate alignment restrictions).
>
>But if you are asking if code like that is portable, the answer
>has to be "no".

I'm still not clear on this.

One of the things that is confusing me, is that in C, you can write:

      typedef struct { ... } T;

      T* p = (T* )malloc( sizeof(T) );

malloc is supposed to return a void* that is of suitable alignment for
any data object, and I had thought this is portable code.

I think you said in another post that you can't use static_cast to
cast a void * to a T*,  so this would imply that the C style cast
above is a reinterpret_cast.   Since you say the results of using
reinterpret_cast are non-portable, that means the code above would be
non-portable in C++.   Is this conclusion correct?



---
Robert Mashlan  R2M Software  rmashlan@r2m.com
Internet Resources for Windows Developers http://www.r2m.com/windev/
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@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++-request@ncar.ucar.edu
]





Author: Stephen.Clamage@eng.sun.com (Steve Clamage)
Date: 1997/02/12
Raw View
In article 23086798@library.airnews.net, rmashlan@r2m.com (Robert
Mashlan) writes:
>Stephen.Clamage@Eng.Sun.COM (Steve Clamage) wrote:
>
>>I'm not trying to make this sound difficult. If you work on a
>>system where pointers are all the same size and have the same
>>representation, casting to another pointer type should work the
>>way you expect (as long as you don't violate alignment restrictions).
>>
>>But if you are asking if code like that is portable, the answer
>>has to be "no".
>
>I'm still not clear on this.
>
>One of the things that is confusing me, is that in C, you can write:
>
>      typedef struct { ... } T;
>
>      T* p = (T* )malloc( sizeof(T) );
>
>malloc is supposed to return a void* that is of suitable alignment for
>any data object, and I had thought this is portable code.
>
>I think you said in another post that you can't use static_cast to
>cast a void * to a T*,

I hope I didn't say that, because it isn't true. You can use a
static_cast to cast from void* to any data pointer type, but the
usefulness of the result depends on several factors. The cast
itself is allowed.

>  so this would imply that the C style cast
>above is a reinterpret_cast.   Since you say the results of using
>reinterpret_cast are non-portable, that means the code above would be
>non-portable in C++.   Is this conclusion correct?

I was speaking always of the general case. Given pointers to unrelated
types T1 and T2, converting a T2* to a T1* is in general not portable
in C or in C++. (And that was the original question that started this
thread.) In C++ the conversion can be performed in general only
with a reinterpret_cast. The conversion semantics of a reinterpret_cast
are defined by the implementation, and so will be in general non-portable.
(Suppose the pointers are different sizes, and you convert a big
pointer to a little pointer. The standard can't make any promises
about the result.)

Malloc is not a general case, but a specific case. Type void* is also
a special case. Type void* is required to have the characteristic that
any data pointer can be converted to void* without loss of information.
The requirements on malloc include that it return a pointer to a block
of memory suitably aligned for any object of the requested size, and
that the void* returned be convertible to a pointer to any such data
object.

Your malloc example is correct and portable code, because of the
special requirements on malloc and void*. The conversion is
performed via a static_cast.

---
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ 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++-request@ncar.ucar.edu                             ]





Author: James Kanze <james-albert.kanze@vx.cit.alcatel.fr>
Date: 1997/02/10
Raw View
Stephen.Clamage@eng.sun.com (Steve Clamage) writes:

|>  In article 145300615@library.airnews.net, rmashlan@r2m.com (Robert
|>  Mashlan) writes:
|>  >
|>  >Assuming that type T2 has stricter or equal alignment restrictions
|>  >than type T1,  Is it possible that the following two expressions
|>  >return different results?
|>  >
|>  >        T2*  pt2;
|>  >
|>  >        T1* pt1 = static_cast<T1 *>(static_cast<void *>(pt2));
|>  >
|>  >vs.
|>  >
|>  >        T1* pt1 = reinterpret_cast<T1*>(pt2);
|>
|>  It is always possible for static_cast and reinterpret_cast to produce
|>  different results, because the results of a reinterpret_cast are
|>  implementation-defined.
|>
|>  The example static_cast above is invalid if T1 and T2 are unrelated
|>  types, which they appear to be. The compiler must issue a diagnostic.

Something I don't understand: how does the fact that T1 and T2 are or
are not related affect the static_cast's in the example?  There is no
direct static_cast between the two classes, only to and from void*.

(This leads to a further question: is a static_cast FROM void* legal?
Should it be?)

--
James Kanze      home:     kanze@gabi-soft.fr        +33 (0)1 39 55 85 62
                 office:   kanze@vx.cit.alcatel.fr   +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
     -- Conseils en informatique industrielle --
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ 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++-request@ncar.ucar.edu                             ]





Author: Stephen.Clamage@Eng.Sun.COM (Steve Clamage)
Date: 1997/02/10
Raw View
In article 211129137@library.airnews.net, rmashlan@r2m.com (Robert Mashlan) writes:
>Stephen.Clamage@eng.sun.com (Steve Clamage) wrote:
>
>>For unrelated pointers, assuming it makes sense to perform the
>>conversion at all, you have no choice but to use reinterpret_cast.
>
>The two types are unrelated.   Should this work on an implementation
>where char * uses a different representation than a pointer to a
>class?

As I said in my previous post, the effect of reinterpret_cast is
implementation-defined. You can't predict what the effect will be
without reading the documentation for the system.

A note in the draft standard says the effect of a pointer cast is
"intended to be unsurprising to those who know the addressing structure
of the underlying machine."

Arbitrary fiddling with pointers is never going to be portable.
The reinterpret_cast is intended to allow "type punning" in those
cases where it makes sense. That varies case by case, system by
system. In the end, it is up to the C++ implementor to decide.

I'm not trying to make this sound difficult. If you work on a
system where pointers are all the same size and have the same
representation, casting to another pointer type should work the
way you expect (as long as you don't violate alignment restrictions).

But if you are asking if code like that is portable, the answer
has to be "no".
---
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@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++-request@ncar.ucar.edu
]





Author: Stephen.Clamage@Eng.Sun.COM (Steve Clamage)
Date: 1997/02/10
Raw View
In article 69CE@pratique.fr, Valentin Bonnard <bonnardv@pratique.fr> writes:
>Steve Clamage wrote:
>>
>> The example static_cast above is invalid if T1 and T2 are unrelated
>> types, which they appear to be. The compiler must issue a diagnostic.
>
>Are you sure ? (I mean after re-reading the original post.)
>
>Can you justify this (DWP citation) ?

Section 5.2.9 "Static cast" of the draft is too lengthy to reproduce here.
It lists the casts which are allowed, and says, "No other conversion shall
be performed explicitly using a static_cast." The "shall" means that
a diagnostic is required if the condition is violated. Pointers to
unrelated types (not part of the same heirarchy) are not in the list
of allowable casts.

>> For unrelated pointers, assuming it makes sense to perform the
>> conversion at all, you have no choice but to use reinterpret_cast.
>
>Right, but that was not the original poster's question.

The original question asked if a particluar static_cast and reinterpret
cast could yield different results. The static_cast isn't allowed, and
IMHO that is part of the answer.

---
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@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++-request@ncar.ucar.edu
]





Author: Stephen.Clamage@Eng (Steve Clamage)
Date: 1997/02/11
Raw View
In article fsf@vx.cit.alcatel.fr, James Kanze <james-albert.kanze@vx.cit.alcatel.fr> writes:
>Stephen.Clamage@eng.sun.com (Steve Clamage) writes:
>
>|>  In article 145300615@library.airnews.net, rmashlan@r2m.com (Robert
>|>  Mashlan) writes:
>|>  >
>|>  >Assuming that type T2 has stricter or equal alignment restrictions
>|>  >than type T1,  Is it possible that the following two expressions
>|>  >return different results?
>|>  >
>|>  >        T2*  pt2;
>|>  >
>|>  >        T1* pt1 = static_cast<T1 *>(static_cast<void *>(pt2));
>|>  >
>|>  >vs.
>|>  >
>|>  >        T1* pt1 = reinterpret_cast<T1*>(pt2);
>|>
>|>  It is always possible for static_cast and reinterpret_cast to produce
>|>  different results, because the results of a reinterpret_cast are
>|>  implementation-defined.
>|>
>|>  The example static_cast above is invalid if T1 and T2 are unrelated
>|>  types, which they appear to be. The compiler must issue a diagnostic.
>
>Something I don't understand: how does the fact that T1 and T2 are or
>are not related affect the static_cast's in the example?  There is no
>direct static_cast between the two classes, only to and from void*.

Sorry, I was compressing two things into one sentence. Here's an
expanded version.

In the submitted example, the cast to void* loses type information that
might be important. The casts might do the right thing, but you can't
depend on it. (For example, if T1 and T2 are part of the same hierarchy,
the T1 and T2 sub-objects have the same address, and the pointers have
the same representation, the cast will work. It will probably also work
for unrelated pointer types which have the same representation, given
the alignment preconditions.)

Contrarywise, a cast like
 static_cast<T1*>(pt2)
might be valid if T1 and T2 are part of the same hierarchy, but
not otherwise. (It is valid if T1 is a base class of T2, or if
T2 is a non-virtual base class of T1.)

>(This leads to a further question: is a static_cast FROM void* legal?
>Should it be?)

Yes, since a static cast can reverse most implicit conversions. (I think
the only exception is reversing a conversion to a virtual base class.)
In particular, you can static_cast any data pointer to void* and back to
the original type and get a pointer that compares equal to the original.

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




[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ 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++-request@ncar.ucar.edu                             ]





Author: fjh@murlibobo.cs.mu.OZ.AU (Fergus Henderson)
Date: 1997/02/11
Raw View
James Kanze <james-albert.kanze@vx.cit.alcatel.fr> writes:

>Stephen.Clamage@eng.sun.com (Steve Clamage) writes:
>
>|>  rmashlan@r2m.com (Robert Mashlan) writes:
>|>  >
>|>  >        T2*  pt2;
>|>  >        T1* pt1 = static_cast<T1 *>(static_cast<void *>(pt2));
>|>
>|>  The example static_cast above is invalid if T1 and T2 are unrelated
>|>  types, which they appear to be. The compiler must issue a diagnostic.
>
>Something I don't understand: how does the fact that T1 and T2 are or
>are not related affect the static_cast's in the example?  There is no
>direct static_cast between the two classes, only to and from void*.

No, a direct static_cast between a pointer to a derived class and
a pointer to its base class is fine (in either direction).
See [expr.static.cast] paragraphs 6-8.

>(This leads to a further question: is a static_cast FROM void* legal?

Yes, it is well-formed, since that's the inverse of a standard conversion.

>Should it be?)

Yes.

--
Fergus Henderson <fjh@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 fjh@128.250.37.3         |     -- the last words of T. S. Garp.


[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ 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++-request@ncar.ucar.edu                             ]





Author: Valentin Bonnard <bonnardv@pratique.fr>
Date: 1997/02/11
Raw View
Steve Clamage wrote:
>
> In article 145300615@library.airnews.net, rmashlan@r2m.com (Robert
> Mashlan) writes:
> >
> >Assuming that type T2 has stricter or equal alignment restrictions
> >than type T1,  Is it possible that the following two expressions
> >return different results?
> >
> >        T2*  pt2;
> >
> >        T1* pt1 = static_cast<T1 *>(static_cast<void *>(pt2));
> >
> >vs.
> >
> >        T1* pt1 = reinterpret_cast<T1*>(pt2);
>
> It is always possible for static_cast and reinterpret_cast to produce
> different results, because the results of a reinterpret_cast are
> implementation-defined.

In practice I don't see any reasons why they may return different
result.

I even suggested that reinterpret_cast could be user defined (or
std lib defined):

template <class To, class From>
To*     reinterpret_cast<To*> (From* p)
{
    // put some test of the cv qualification
    // of From and To here (use template functions)
    return (To*) (void*) p;
}

> The example static_cast above is invalid if T1 and T2 are unrelated
> types, which they appear to be. The compiler must issue a diagnostic.

Are you sure ? (I mean after re-reading the original post.)

Can you justify this (DWP citation) ?

> If T1 and T2 are pointers into a class hierarchy, you should not
> use reinterpret cast, since it might not perform a needed pointer
> adjustment. You should use either static_cast or dynamic_cast.
>
> For unrelated pointers, assuming it makes sense to perform the
> conversion at all, you have no choice but to use reinterpret_cast.

Right, but that was not the original poster's question.

--

Valentin Bonnard
mailto:bonnardv@pratique.fr
http://www.pratique.fr/~bonnardv (Informations sur le C++ en Francais)


[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ 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++-request@ncar.ucar.edu                             ]





Author: Stephen.Clamage@eng.sun.com (Steve Clamage)
Date: 1997/02/07
Raw View
In article 145300615@library.airnews.net, rmashlan@r2m.com (Robert
Mashlan) writes:
>
>Assuming that type T2 has stricter or equal alignment restrictions
>than type T1,  Is it possible that the following two expressions
>return different results?
>
>        T2*  pt2;
>
>        T1* pt1 = static_cast<T1 *>(static_cast<void *>(pt2));
>
>vs.
>
>        T1* pt1 = reinterpret_cast<T1*>(pt2);

It is always possible for static_cast and reinterpret_cast to produce
different results, because the results of a reinterpret_cast are
implementation-defined.

The example static_cast above is invalid if T1 and T2 are unrelated
types, which they appear to be. The compiler must issue a diagnostic.

If T1 and T2 are pointers into a class hierarchy, you should not
use reinterpret cast, since it might not perform a needed pointer
adjustment. You should use either static_cast or dynamic_cast.

For unrelated pointers, assuming it makes sense to perform the
conversion at all, you have no choice but to use reinterpret_cast.
---
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ 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++-request@ncar.ucar.edu                             ]