Topic: Major hole in CD2 pointer conversions


Author: dHarrison@worldnet.att.net (Doug Harrison)
Date: 1997/11/09
Raw View
On 07 Nov 97 01:45:49 GMT, Jerry Leichter <leichter@smarts.com> wrote:

>| You might think because the static_cast results in undefined behavior,
>| you should use reinterpret_cast, but as I described in my last
>| message, *any* use of reinterpret_cast involving void* results in
>| unspecified behavior....

(In case it isn't clear out of context, above I was speaking of
pointer-pointer conversions.)

>This strikes me as a bit bizzare.  Let's ask the more fundamental
>question:  Suppose we have a type T for which it is the case that
>both of the following casts are legal:
>
> T p1 = static_cast<T>(q);
> T p2 = reinterpret_cast<T>(q);
>
>Does the Standard really distinguish between p1 and p2, such that there
>could be conforming implementations for which there are operations on p1
>that have different results from the same operations on p2 (where
>"result" might be "undefined" or "implementation-specific")?  If so, is
>this really the intent?

Let's rewrite the above as:

 T* p1 = static_cast<T*>(q);
 T* p2 = reinterpret_cast<T*>(q);

and let's call q's type Q*. Then, 5.2.10/7 plainly states that the
only predictable thing one can do with p2 is to convert it back to Q*,
and even then, T and Q must be object types. If T or Q is void, then
the draft implies that a reinterpret_cast in either direction always
results in unspecified behavior (I don't know if that's the intent).
Based upon this, I would answer your question concerning p1 and p2 in
the affirmative.

Consider an example that doesn't involve void*, where T is a base
class of Q:

 struct Q : A, T {};

Converting a Q pointer to a T pointer may involve some pointer
adjustment, which will (may?) not be performed if you use
reinterpret_cast. However, it will definitely be performed if you use
static_cast. The draft does guarantee, however, that you can
reinterpret_cast the first reinterpret_cast result back to the
original type, and you get the original pointer, and that's the *only*
well-defined usage.

>My first, naive, reading of the Standard lead me to believe that, in any
>given situation, *exactly one* of the cast styles was valid.  This
>struck me as a good design.  Unfortunately, it's not the way things
>actually work - which adds to the difficulty in choosing the right cast
>for a given situation.  If different casts can actually have different
>semantics (ignoring dynamic_cast, which by design has additional error
>checking), the choice becomes even more subtle - and much more
>important!

It is important! static_cast can (statically) navigate class
hierarchies, but reinterpret_cast essentially just reinterprets bits.

--
Doug Harrison
dHarrison@worldnet.att.net
---
[ 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: dHarrison@worldnet.att.net (Doug Harrison)
Date: 1997/11/06
Raw View
On 05 Nov 97 17:31:23 GMT, Valentin Bonnard <bonnardv@pratique.fr>
wrote:

>Doug Harrison wrote:
>>
>> In summary, the main reason I think static_cast is the one to use when
>> casting from void* to another pointer type is that this usage is
>> consistent with the notion that static_cast performs the inverse of
>> standard conversions, and A* -> void* is a standard conversion.
>
>Only if the target type is the dynamic type (at least a cv-qualified
>base class of):
>
>class A {};
>class B {};
>
>A a;
>void* p = &a;
>B* pb = static_cast<B*> (p); // undefined result (or is it behaviour)

Because there is a standard conversion from B* -> void*, static_cast
is the one to use when going in the opposite direction; whether the
result is defined or not seems incidental to that consideration. I
guess I could clarify my statement from my last message by saying,
"... static_cast is the one to use when _validly_ casting from void*
to another pointer type". But why would you want to invalidly cast
void* to another pointer type?

You might think because the static_cast results in undefined behavior,
you should use reinterpret_cast, but as I described in my last
message, *any* use of reinterpret_cast involving void* results in
unspecified behavior.

So, that cast is unpredictable, no matter how you formulate it. My
point is that because it's the inverse of a standard conversion, you
would only consider static_cast, before deciding against it. :)

>char* pc = static_cast<char*> (p); // undefined result, same reason:
>                                   // the dynamic type of a isn't char

But the draft specifically allows that one. As I stated in my last
message, see 3.9/2 and 3.10/15.

>B* pb2 = reinterpret_cast<B*> (p); // accessing *pb2 is undefined
>                                   // except for some cases

Again, as I described in my last message, the results of such
reinterpret_casts are *always* unspecified, even if p contains a B*,
because p is a void*, and void isn't an "object type". If p does
contain a B*, you definitely should use static_cast, but I also have a
hard time imagining how reinterpret_cast could go wrong.

--
Doug Harrison
dHarrison@worldnet.att.net
---
[ 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: Jerry Leichter <leichter@smarts.com>
Date: 1997/11/07
Raw View
| You might think because the static_cast results in undefined behavior,
| you should use reinterpret_cast, but as I described in my last
| message, *any* use of reinterpret_cast involving void* results in
| unspecified behavior....

This strikes me as a bit bizzare.  Let's ask the more fundamental
question:  Suppose we have a type T for which it is the case that
both of the following casts are legal:

 T p1 = static_cast<T>(q);
 T p2 = reinterpret_cast<T>(q);

Does the Standard really distinguish between p1 and p2, such that there
could be conforming implementations for which there are operations on p1
that have different results from the same operations on p2 (where
"result" might be "undefined" or "implementation-specific")?  If so, is
this really the intent?

My first, naive, reading of the Standard lead me to believe that, in any
given situation, *exactly one* of the cast styles was valid.  This
struck me as a good design.  Unfortunately, it's not the way things
actually work - which adds to the difficulty in choosing the right cast
for a given situation.  If different casts can actually have different
semantics (ignoring dynamic_cast, which by design has additional error
checking), the choice becomes even more subtle - and much more
important!
       -- Jerry
---
[ 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: bparker@mailbox.uq.edu.au (Brian Parker)
Date: 1997/11/04
Raw View
On 03 Nov 97 07:27:20 GMT, Valentin Bonnard <bonnardv@pratique.fr>
wrote:
>...
>> I believe Brian's interpretation is correct, except that the first
>> example really is unspecified, per 5.2.10.7. The only well-defined
>> thing that can be done with the first 'p' is to reinterpret_cast it
>> back to an A*.
>
>Yes but the intent is that it's usable.
>
>> The second one illustrates what appears to be the only
>> legal way to convert an A* to a char*, that you can then use as a
>> char*.
>
>No can't do anything with the char* obtainned this way (at
>least it's the intent).
>
>The intent is that static_cast<A*> (p) has a well-defined
>result only if p points to something of type A.
>
>But we can only (try to) interpret the intent, as the std
>doesn't say anything.

It is interesting to note that "The C++ Programming Language 3rd
edition" has a similar example on page 503, where access to the raw
memory of an object is needed to compute a default hash function.
That example uses-

const T key;
...
// 1
const char* p = reinterpret_cast<const char*>(&key);

,rather than,

// 2
const char* p = static_cast<const char*>(static_cast<void*>(&key));

My impression, also, is that the original intent of the standard was
to allow the reinterpret_cast as a portable way to get access to the
memory of an object- certainly, at least,  it is non-intuitive to
require //2 above rather than //1

However, a strict reading of the current wording would suggest that
//2 is required.

Ideally, I think, this issue should be clarified one way or the other
in the final standard.

,Brian Parker.
---
[ 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: dHarrison@worldnet.att.net (Doug Harrison)
Date: 1997/11/05
Raw View
On 03 Nov 97 07:27:20 GMT, Valentin Bonnard <bonnardv@pratique.fr>
wrote:

To summarize, the question is, "Is void* -> char* value-preserving?"

>IMO 3.9.2/4 and doesn't say that there is a way to convert with
>any sort of cast:
>
>|>   A cv-qualified or cv-unqualified  (_basic.type.qualifier_)  void* shall
>|>   have the same representation and alignment requirements as a cv-quali-
>|>   fied or cv-unqualified char*.

With static_cast, one can surely convert between void* and char*. I
think the above sentence, from 3.9.2/4, strongly implies void* <->
char* conversions are value-preserving. The draft doesn't qualify the
"representation" it's talking about, object or value, per 3.9/4, so it
means both. Unless I've misunderstood what 3.9/4 means regarding
"value representation", void* and char* are represented exactly the
same way, and there's a complete, direct mapping between their values.
Thus, an implementation would have to deliberately flip some bits when
converting between the two types, for the conversion not to be
value-preserving, and I fail to see how that would make any sense, and
I don't see how that could be reconciled with 3.9.2/4. Also, if void*
-> char* wasn't value-preserving, then 3.9/2 would become bogus.

>Doug Harrison <dHarrison@worldnet.att.net> writes:

>> I believe Brian's interpretation is correct, except that the first
>> example really is unspecified, per 5.2.10.7. The only well-defined
>> thing that can be done with the first 'p' is to reinterpret_cast it
>> back to an A*.
>
>Yes but the intent is that it's usable.

I also have a hard time imagining how reinterpret_cast<char*>(p),
where p is void*, could fail to produce the expected char*, but I'm
just reading what the draft plainly says. In fact, if you take
5.2.10/7 at its face value, then the result of any reinterpret_cast
between void* and another pointer type is unspecified, because void
isn't an object type, per 3.9/9, 3.9/6, and 1.7/1.

>> The second one illustrates what appears to be the only
>> legal way to convert an A* to a char*, that you can then use as a
>> char*.
>
>No can't do anything with the char* obtainned this way (at
>least it's the intent).

I was referring to Brian's 2nd example:

>>> char* p = (char*)(void*)&a; // well-defined
>>> // copy memory of a ...

That's well-defined, at least if 'a' is a POD. See 3.9/2 and 3.10/15.

>The intent is that static_cast<A*> (p) has a well-defined
>result only if p points to something of type A.

In general, I don't disagree. However, 3.9/2 explicitly allows what
Brian described in his second example.

>But we can only (try to) interpret the intent, as the std
>doesn't say anything.

Intents aside, I think the draft is pretty clear on these issues, if a
little indirect at times. If part of the draft isn't consistent with
its intent, then that part is in need of revision.

>BTW, what does 'result' means ?

In what context? :) Consider 5.2.10/7. Does it mean the value
produced, or does it include side-effects? I would tend to think both.

In summary, the main reason I think static_cast is the one to use when
casting from void* to another pointer type is that this usage is
consistent with the notion that static_cast performs the inverse of
standard conversions, and A* -> void* is a standard conversion.
reinterpret_cast is used for things like char* <-> int and int* <->
char*, for which there's no standard conversion in either direction.
There is an interesting difference between integer/pointer conversions
and pointer/pointer conversions, as regards reinterpret_cast: where
the former is implementation-defined, the latter is unspecified.
(Compare 5.2.10/4-5 to 5.2.10/7.)

--
Doug Harrison
dHarrison@worldnet.att.net
---
[ 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/11/05
Raw View
Doug Harrison wrote:
>
> On 03 Nov 97 07:27:20 GMT, Valentin Bonnard <bonnardv@pratique.fr>
> wrote:
>
> To summarize, the question is, "Is void* -> char* value-preserving?"

> In summary, the main reason I think static_cast is the one to use when
> casting from void* to another pointer type is that this usage is
> consistent with the notion that static_cast performs the inverse of
> standard conversions, and A* -> void* is a standard conversion.

Only if the target type is the dynamic type (at least a cv-qualified
base class of):

class A {};
class B {};

A a;
void* p = &a;
B* pb = static_cast<B*> (p); // undefined result (or is it behaviour)
char* pc = static_cast<char*> (p); // undefined result, same reason:
                                   // the dynamic type of a isn't char
B* pb2 = reinterpret_cast<B*> (p); // accessing *pb2 is undefined
                                   // except for some cases

--

Valentin Bonnard                mailto:bonnardv@pratique.fr
info about C++/a propos du C++: http://www.pratique.fr/~bonnardv/
---
[ 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: dHarrison@worldnet.att.net (Doug Harrison)
Date: 1997/11/01
Raw View
On 31 Oct 97 14:11:05 GMT, Valentin Bonnard <bonnardv@pratique.fr>
wrote:

>Brian Parker wrote:
>>
>> On 30 Oct 97 21:15:42 GMT, dHarrison@worldnet.att.net (Doug Harrison)
>> wrote:
>>
>> >On 30 Oct 97 10:12:37 GMT, bparker@mailbox.uq.edu.au (Brian Parker)
>> >wrote:
>
>> >>This is well-defined under ANSI C as the void* can be implicitly cast
>> >>to the char* and is guaranteed to point to the same memory location as
>> >>the void*.
>> >>
>> >>Under C++, however, this implicit cast is, quite correctly, disallowed
>> >>and so a reinterpret_cast must be used.
>> >          ^^^^^^^^^^^^^^^^
>> >
>> >Make that static_cast.
>
>But why ? reinterpret_cast<char*> (object_pointer) is well-formed,
>but static_cast<char*> (object_pointer) isn't. I think
>reinterpret_cast is the right cast for this purpose.

The result of reinterpret_casting between void* and another pointer
type is unspecified, per the Dec-96 draft, 5.2.10.7 (void is not an
object type), and Brian's message concerns just such a conversion.
Even if void was an object type, the result, when casting from void*
to char*, would be well-defined only if you started out with char*.

However, as I said in my earlier message, static_cast can perform the
inverse of most standard conversions, and he's casting from void* to
char*. See the Dec-96 draft, 5.2.9.6. static_cast is thus the one to
use.

>> Of course, thanks for pointing that out. In the C++ Report article I
>> referenced, the example explicitly used a reinterpret cast.
>>
>> So that would give functions taking void* arguments well-defined
>> behaviour (although I still have a minor niggle in that even for
>> static_cast I don't think the draft specifies that a void* to char*
>> conversion will be value-preserving...or does it?).
>
>No it doesn't.

At the very least, I think it strongly implies it. See the Dec-96
draft, 3.9.2.4.

>> However, in cases not using a function this means that if one wants
>> direct acess to some object's memory using a char*, then a direct cast
>> to char* is undefined and so one must use (char*)(void*) (or the
>> static_cast equivalents) i.e.
>>
>> A a;
>>
>> char* p = (char*)&a;   // undefined (or, rather,
>> implementation-defined behaviour)
>>
>> char* p = (char*)(void*)&a; // well-defined
>> // copy memory of a ...
>>
>>  Is this by design? Does much existing C code use (char*) in this way?
>
>No, I think that the behaviour is undefined in both cases by
>omission, but the intent is that the first one is ok because it's
>a reinterpret_cast but not the second one which is a static_cast
>(that would break code, BTW).

I believe Brian's interpretation is correct, except that the first
example really is unspecified, per 5.2.10.7. The only well-defined
thing that can be done with the first 'p' is to reinterpret_cast it
back to an A*. The second one illustrates what appears to be the only
legal way to convert an A* to a char*, that you can then use as a
char*.

--
Doug Harrison
dHarrison@worldnet.att.net
---
[ 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/11/03
Raw View
Doug Harrison <dHarrison@worldnet.att.net> writes:

> On 31 Oct 97 14:11:05 GMT, Valentin Bonnard <bonnardv@pratique.fr>
> wrote:
>
> >Brian Parker wrote:
> >>
> >> So that would give functions taking void* arguments well-defined
> >> behaviour (although I still have a minor niggle in that even for
> >> static_cast I don't think the draft specifies that a void* to char*
> >> conversion will be value-preserving...or does it?).
> >
> >No it doesn't.
>
> At the very least, I think it strongly implies it. See the Dec-96
> draft, 3.9.2.4.

IMO 3.9.2/4 and doesn't say that there is a way to convert with
any sort of cast:

|>   A cv-qualified or cv-unqualified  (_basic.type.qualifier_)  void* shall
|>   have the same representation and alignment requirements as a cv-quali-
|>   fied or cv-unqualified char*.

> I believe Brian's interpretation is correct, except that the first
> example really is unspecified, per 5.2.10.7. The only well-defined
> thing that can be done with the first 'p' is to reinterpret_cast it
> back to an A*.

Yes but the intent is that it's usable.

> The second one illustrates what appears to be the only
> legal way to convert an A* to a char*, that you can then use as a
> char*.

No can't do anything with the char* obtainned this way (at
least it's the intent).

The intent is that static_cast<A*> (p) has a well-defined
result only if p points to something of type A.

But we can only (try to) interpret the intent, as the std
doesn't say anything.

BTW, what does 'result' means ?

--

Valentin Bonnard                mailto:bonnardv@pratique.fr
info about C++/a propos du C++: http://www.pratique.fr/~bonnardv/
---
[ 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/10/31
Raw View
Brian Parker wrote:
>
> On 30 Oct 97 21:15:42 GMT, dHarrison@worldnet.att.net (Doug Harrison)
> wrote:
>
> >On 30 Oct 97 10:12:37 GMT, bparker@mailbox.uq.edu.au (Brian Parker)
> >wrote:

> >>This is well-defined under ANSI C as the void* can be implicitly cast
> >>to the char* and is guaranteed to point to the same memory location as
> >>the void*.
> >>
> >>Under C++, however, this implicit cast is, quite correctly, disallowed
> >>and so a reinterpret_cast must be used.
> >          ^^^^^^^^^^^^^^^^
> >
> >Make that static_cast.

But why ? reinterpret_cast<char*> (object_pointer) is well-formed,
but static_cast<char*> (object_pointer) isn't. I think
reinterpret_cast is the right cast for this purpose.

> Of course, thanks for pointing that out. In the C++ Report article I
> referenced, the example explicitly used a reinterpret cast.
>
> So that would give functions taking void* arguments well-defined
> behaviour (although I still have a minor niggle in that even for
> static_cast I don't think the draft specifies that a void* to char*
> conversion will be value-preserving...or does it?).

No it doesn't.

> However, in cases not using a function this means that if one wants
> direct acess to some object's memory using a char*, then a direct cast
> to char* is undefined and so one must use (char*)(void*) (or the
> static_cast equivalents) i.e.
>
> A a;
>
> char* p = (char*)&a;   // undefined (or, rather,
> implementation-defined behaviour)
>
> char* p = (char*)(void*)&a; // well-defined
> // copy memory of a ...
>
>  Is this by design? Does much existing C code use (char*) in this way?

No, I think that the behaviour is undefined in both cases by
omission, but the intent is that the first one is ok because it's
a reinterpret_cast but not the second one which is a static_cast
(that would break code, BTW).

static_cast is for casting back to the original type. char isn't
the original type, it's a view of the memory, so reinterpret_cast
should be used.

This is a problem in the std C++ memory model. I'll speak about
that in the article I'll post here about alignement.

--

Valentin Bonnard                mailto:bonnardv@pratique.fr
info about C++/a propos du C++: http://www.pratique.fr/~bonnardv/
---
[ 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: bparker@mailbox.uq.edu.au (Brian Parker)
Date: 1997/10/31
Raw View
On 31 Oct 97 07:51:28 GMT, "Paul D. DeRocco" <pderocco@ix.netcom.com>
wrote:

>Brian Parker wrote:
>>
>> (1) 3.9.1/1-  the sentence that reads
>> "For unsigned character types, all possible bit patterns of the value
>> representation represent numbers" should be "For unsigned char AND
>> TYPE CHAR, all possible bit patterns of the value representation
>> represent DISTINCT numbers.")
>
>The standard can guarantee this for a signed char and still work
>properly on a one's-complement machine, since all 0s and all 1s
>both represent zero.

(Did you mean to say that "The standard *can't* guarantee this..." ?)

The C++ Report article that I cited explicitly discussed this issue.
As you say, signed chars can't be (portably) used for examining memory
as on one's complement machines there are multiple representations for
0. Unsigned chars can unequivocably be used even on one's complement
machines as there are no shared values. The remaining issue is whether
plain char should be guaranteed to have no shared values (and hence be
usable for copying memory)-- the C++ Report article stated that it was
decided to require this in the standard (and hence require that one's
complement machines define char as unsigned char), mainly because all
extant one's complement machines did that anyway because plain char*
was often used for copying memory in C.

The situation may have changed since the article was published, but I
suspect that this is a bug in the standard. (What does "For unsigned
character types, all possible bit patterns of the value representation
represent numbers" mean otherwise? What does it mean to represent a
number?)

Thanks for your comments,
Brian Parker.
---
[ 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: bparker@mailbox.uq.edu.au (Brian Parker)
Date: 1997/10/31
Raw View
On 30 Oct 97 21:15:42 GMT, dHarrison@worldnet.att.net (Doug Harrison)
wrote:

>On 30 Oct 97 10:12:37 GMT, bparker@mailbox.uq.edu.au (Brian Parker)
>wrote:
>
><snip>
>
>>This is well-defined under ANSI C as the void* can be implicitly cast
>>to the char* and is guaranteed to point to the same memory location as
>>the void*.
>>
>>Under C++, however, this implicit cast is, quite correctly, disallowed
>>and so a reinterpret_cast must be used.
>          ^^^^^^^^^^^^^^^^
>
>Make that static_cast.
>...

Of course, thanks for pointing that out. In the C++ Report article I
referenced, the example explicitly used a reinterpret cast.

So that would give functions taking void* arguments well-defined
behaviour (although I still have a minor niggle in that even for
static_cast I don't think the draft specifies that a void* to char*
conversion will be value-preserving...or does it?).

However, in cases not using a function this means that if one wants
direct acess to some object's memory using a char*, then a direct cast
to char* is undefined and so one must use (char*)(void*) (or the
static_cast equivalents) i.e.

A a;

char* p = (char*)&a;   // undefined (or, rather,
implementation-defined behaviour)

char* p = (char*)(void*)&a; // well-defined
// copy memory of a ...

 Is this by design? Does much existing C code use (char*) in this way?

,Brian Parker.
---
[ 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: bparker@mailbox.uq.edu.au (Brian Parker)
Date: 1997/10/31
Raw View
On 31 Oct 97 14:11:05 GMT, Valentin Bonnard <bonnardv@pratique.fr>
wrote:
>>...
>>
>> char* p = (char*)&a;   // undefined (or, rather,
>> implementation-defined behaviour)
>>
>> char* p = (char*)(void*)&a; // well-defined
>> // copy memory of a ...
>>
>>  Is this by design? Does much existing C code use (char*) in this way?
>
>No, I think that the behaviour is undefined in both cases by
>omission, but the intent is that the first one is ok because it's
>a reinterpret_cast but not the second one which is a static_cast
>(that would break code, BTW).
>
>static_cast is for casting back to the original type. char isn't
>the original type, it's a view of the memory, so reinterpret_cast
>should be used.
>
>This is a problem in the std C++ memory model. I'll speak about
>that in the article I'll post here about alignement.

Hmmm, there is obviously room for some clarifications in the standard
on this issue. As well as the problems discussed in this thread, there
are serious problems with 9.2 [class.mem]/12 's not specifying that
only the *minimum* padding necessary for alignment can be added in a
class layout, (and also, possibly, its not specifying that adjacent
elements cannot overlap.)

Are there any plans for these C++ memory model issues to be discussed
at the next standards meeting?

,Brian Parker.
---
[ 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: "Paul D. DeRocco" <pderocco@ix.netcom.com>
Date: 1997/10/31
Raw View
Brian Parker wrote:
>
> (1) 3.9.1/1-  the sentence that reads
> "For unsigned character types, all possible bit patterns of the value
> representation represent numbers" should be "For unsigned char AND
> TYPE CHAR, all possible bit patterns of the value representation
> represent DISTINCT numbers.")

The standard can guarantee this for a signed char and still work
properly on a one's-complement machine, since all 0s and all 1s
both represent zero.

--

Ciao,
Paul
---
[ 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: bparker@mailbox.uq.edu.au (Brian Parker)
Date: 1997/10/30
Raw View
C & C++ carefully define the underlying memory model to allow portable
low level memory manipulation code. Specifically, it defines that
char* and unsigned char* can be used to manipulate raw memory by
virtue of these types having matching object and value representations
and no duplicated values (3.9.1 Para 1), and guaranteeing that an
implicit conversion of T* to a void* gives a pointer to the *start* of
the underlying object of T (4.10/2), and that void* and char* have the
same representation and can store any object pointer (3.9.2/4).

The intent is to allow portable memory-manipulation code such as-

void f(void * mem, size_t n)
{
 char * pc = (char*)mem;

 // process memory
 for (char *p = pc; p != pc+n; p++) {
  //...
 }
}
, as used in the implementation of memcpy et al.

This is well-defined under ANSI C as the void* can be implicitly cast
to the char* and is guaranteed to point to the same memory location as
the void*.

Under C++, however, this implicit cast is, quite correctly, disallowed
and so a reinterpret_cast must be used.

The problem is that a reinterpret cast of void* to char* or unsigned
char* is implementation defined behaviour and so *any* value can be
given to the char*, so all memory manipulating functions such as f( )
above are, as far as I can tell,  not portable!

This was clearly not the intent of the standard; whilst doing a
reinterpret cast between unrelated types should be implementation
defined, 3.9.2/4 is there specifically to allow void*, char* and
unsigned char* to point to anywhere and so have well-defined behaviour
when being cast to from other types.

5.2.10 [expr.reinterpret.cast] needs to have a paragraph added stating
the same as 4.10/2 does for implicit cast to void* i.e.. "The result
of a reinterpret_cast from a pointer to cv T (or a void* resulting
from a previous cast from a pointer to cv T)  to a pointer to cv char
or unsigned char points to the start of the storage location where the
object of type T resides, as if the object is a most derived object of
type T (that is not a base class subobject)."

Without this paragraph, vast amounts of C code would be
non-conforming. This, of course, just clarifies the implict intent of
the draft, and I believe it is just an oversight.

Some related bugs in the CD2-
(1) 3.9.1/1-  the sentence that reads
"For unsigned character types, all possible bit patterns of the value
representation represent numbers" should be "For unsigned char AND
TYPE CHAR, all possible bit patterns of the value representation
represent DISTINCT numbers.")

(2) 9.2/12 is not very clear in indicating that an implementation can
only add the *minimum* necessary padding to meet alignment
requirements. As it stands, one could interpret this paragraph as
allowing an implementation to gratuitously add padding in multiples of
the alignment requirements. This would, of course, destroy the
existing determinism of POD layout (alignment requirements are
implementation-defined, allowing POD layout to determined and
manipulated the programmer.)

(N.B. for an excellent overview of the C++ memory model, see C++
Report, May 96 "Standard Update".)


Any comments? Have I missed something?

,Brian Parker.
---
[ 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: dHarrison@worldnet.att.net (Doug Harrison)
Date: 1997/10/30
Raw View
On 30 Oct 97 10:12:37 GMT, bparker@mailbox.uq.edu.au (Brian Parker)
wrote:

<snip>

>This is well-defined under ANSI C as the void* can be implicitly cast
>to the char* and is guaranteed to point to the same memory location as
>the void*.
>
>Under C++, however, this implicit cast is, quite correctly, disallowed
>and so a reinterpret_cast must be used.
          ^^^^^^^^^^^^^^^^

Make that static_cast.

>The problem is that a reinterpret cast of void* to char* or unsigned
>char* is implementation defined behaviour and so *any* value can be
>given to the char*, so all memory manipulating functions such as f( )
>above are, as far as I can tell,  not portable!

<snip>

>Any comments? Have I missed something?

I think so. Read up on static_cast, which can perform the inverse of
most implicit conversions, such as char* -> void*.

--
Doug Harrison
dHarrison@worldnet.att.net
---
[ 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                             ]