static_cast vs. reinterpret_cast

92 просмотра
Перейти к первому непрочитанному сообщению

Frank A. Uepping

не прочитано,
12 авг. 2003 г., 19:01:1312.08.2003
Hello,
assume we have this definition:

void f(void* p)
{
...
// E.g.: Need to mangle *p
memcpy(static_cast<char*>(p) + i, ...);
...
}

The point to consider here is, that p points to a object not
necessarily a char stream.
That is, f() could be invoked with a `char*', `unsigend char*' or 'int*'.
(Of course, this question has a somewhat theoretical nature, however.)

Is the static_cast<> the right candidate for this job or reinterpret_cast<>?

/FAU


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Erik Max Francis

не прочитано,
13 авг. 2003 г., 03:39:1913.08.2003
"Frank A. Uepping" wrote:

> Is the static_cast<> the right candidate for this job or
> reinterpret_cast<>?

static_cast is the right operator. Remember that static_cast invokes a
well-defined conversion, and a conversion from T * to void * or back
(for some data type T) is always well-defined.

--
Erik Max Francis && m...@alcyone.com && http://www.alcyone.com/max/
__ San Jose, CA, USA && 37 20 N 121 53 W && &tSftDotIotE
/ \ I needed sunshine in my day / Something to wash away the pain
\__/ Zhane

Siemel Naran

не прочитано,
13 авг. 2003 г., 03:52:2413.08.2003
"Frank A. Uepping" <Frank....@t-online.de> wrote in message
news:bhbeht$oer$00

> void f(void* p)
> {
> ...
> // E.g.: Need to mangle *p
> memcpy(static_cast<char*>(p) + i, ...);
> ...
> }
>
> The point to consider here is, that p points to a object not
> necessarily a char stream.
> That is, f() could be invoked with a `char*', `unsigend char*' or 'int*'.
> (Of course, this question has a somewhat theoretical nature, however.)
>
> Is the static_cast<> the right candidate for this job or
reinterpret_cast<>?

Good question. Going from T* to char* requires a reinterpret_cast, which is
implementation defined, dangerous, etc. Going from T* to void* requires
nothing, then to go to char* to anything* can use static_cast.

Section 3.9.2 item 4 says:

Objects of cv-qualified or cv-unqualified type void* can be used to point to
objects of unknown type. A void* shall be able to hold any object pointer.
A cv-qualified or cv-unqualified void* shall have the same representation
and alignment requirements as a cv-qualified or cv-unqualified char*.

But it's still confusing.

--
+++++++++++
Siemel Naran

Jack Klein

не прочитано,
13 авг. 2003 г., 03:56:5213.08.2003
On 12 Aug 2003 19:01:13 -0400, "Frank A. Uepping"
<Frank....@t-online.de> wrote in comp.lang.c++.moderated:

> Hello,
> assume we have this definition:
>
> void f(void* p)
> {
> ...
> // E.g.: Need to mangle *p

You are mistaken. The memcpy() function takes two pointer arguments
of type void*.

> memcpy(static_cast<char*>(p) + i, ...);

Oh, you need to cast the pointer to character type to do pointer
arithmetic.

> ...
> }
>
> The point to consider here is, that p points to a object not
> necessarily a char stream.
> That is, f() could be invoked with a `char*', `unsigend char*' or 'int*'.
> (Of course, this question has a somewhat theoretical nature, however.)
>
> Is the static_cast<> the right candidate for this job or reinterpret_cast<>?
>
> /FAU

static_cast is the proper operator here. There is absolutely no
change in representation between pointer to void and pointer to char.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++ ftp://snurse-l.org/pub/acllc-c++/faq

Frank A. Uepping

не прочитано,
13 авг. 2003 г., 12:06:3713.08.2003
Jack Klein wrote:

> On 12 Aug 2003 19:01:13 -0400, "Frank A. Uepping"
> <Frank....@t-online.de> wrote in comp.lang.c++.moderated:
>
> > Hello,
> > assume we have this definition:
> >
> > void f(void* p)
> > {
> > ...
> > // E.g.: Need to mangle *p
>
> You are mistaken. The memcpy() function takes two pointer arguments
> of type void*.
>
> > memcpy(static_cast<char*>(p) + i, ...);
>
> Oh, you need to cast the pointer to character type to do pointer
> arithmetic.
>
> > ...
> > }
> >
> > The point to consider here is, that p points to a object not
> > necessarily a char stream.
> > That is, f() could be invoked with a `char*', `unsigend char*' or
> > 'int*'. (Of course, this question has a somewhat theoretical nature,
> > however.)
> >
> > Is the static_cast<> the right candidate for this job or
> > reinterpret_cast<>?
> >
> > /FAU
>
> static_cast is the proper operator here. There is absolutely no
> change in representation between pointer to void and pointer to char.
>

What if we want to cast to int*, for example:
memcpy(static_cast<int*>(p) + i, ...);
Is static_cast<> still appropriate?
Consider Section 3.9.2 item 4.

/FAU

John Potter

не прочитано,
13 авг. 2003 г., 12:07:4413.08.2003
On 12 Aug 2003 19:01:13 -0400, "Frank A. Uepping"
<Frank....@t-online.de> wrote:

> assume we have this definition:

> void f(void* p)
> {
> ...
> // E.g.: Need to mangle *p
> memcpy(static_cast<char*>(p) + i, ...);
> ...
> }

> The point to consider here is, that p points to a object not
> necessarily a char stream.
> That is, f() could be invoked with a `char*', `unsigend char*' or 'int*'.
> (Of course, this question has a somewhat theoretical nature, however.)

> Is the static_cast<> the right candidate for this job or reinterpret_cast<>?

The best hint I can find in the standard is 5.2.9/10 which states that
going from T* to void* to T* using static_cast will work. It says
nothing about anything else like U* to void* to T*. OTOH, 5.2.10/7
states that going from T* to void* to T* using reinterpret_cast will
work and that going from U* to T* is unspecified.

Since nothing is guarantied with either and both are assumed to work in
any reasonable implementation, the question becomes a matter of what
message you wish to send with the code.

int* intPun (void* p) { return static_cast<int*>(p); }

cout << hex;
float f(1f);
cout << *reinterpret_cast<int*>(&f);
cout << *static_cast<int*>(static_cast<void*>(&f));
cout << *intPun(&f);
int i;
memcpy(&i, &f, sizeof(i));
cout << i;

The first is an honest statement that nasty things are taking place.
The next three are attempts to hide the nasty things from view. In
your case of using memcpy to overwrite bytes at some offset into an
object, I suspect nasty things and would send the reinterpret message
to other programmers.

John

Bo Persson

не прочитано,
13 авг. 2003 г., 18:50:1313.08.2003

"Frank A. Uepping" <Frank....@t-online.de> skrev i meddelandet
news:bhbeht$oer$00$1...@news.t-online.com...

> Hello,
> assume we have this definition:
>
> void f(void* p)
> {
> ...
> // E.g.: Need to mangle *p
> memcpy(static_cast<char*>(p) + i, ...);

Here you tell the compiler "p actually points to a char". If that is
so, why not state that in the function header?

> ...
> }
>
> The point to consider here is, that p points to a object not
> necessarily a char stream.
> That is, f() could be invoked with a `char*', `unsigend char*' or
'int*'.
> (Of course, this question has a somewhat theoretical nature,
however.)
>
> Is the static_cast<> the right candidate for this job or
reinterpret_cast<>?

It depends on what you are trying to do. If you (for some reason) need
some pointer arithmetic in the function, why don't you just declare it
as

template<class T>
void f(T* p)
{

memcpy(p + i, ... );

}

and the compiler will fix it for any type that supports having i added
to it.

Usually the right question is not "What cast to use", but more often
"Why use a cast in the first place". Old C code often use casts
(especially casts to/from void*), where proper C++ code would use
overloading or inheritance.

Bo Persson
bo...@telia.com

Frank A. Uepping

не прочитано,
14 авг. 2003 г., 05:00:4214.08.2003
John Potter wrote:

Fundamentally, we have a conversion from T* -> char* over a void*;
and for this kind of conversions (T* -> U*) the reinterpret_cast<> is
the means.

However, perhaps we should change our perspective on what we want to
achieve. The objective of the conversion is to enable us to do (char)
pointer arithmetic. With that in mind and considering 3.9.2-4:

"Objects of cvqualified (3.9.3) or cvunqualified type void* (pointer to
void), can be used to point to objects of unknown type. A void* shall be
able to hold any object pointer. A cvqualified or cvunqualified (3.9.3)


void* shall have the same representation and alignment requirements as

a cvqualified or cvunqualified char*."

If a void* has the same representation and alignment requirements
as a char* then a conversion from void* -> char* can be deemed as
"safe" (with safe I mean the value is preserved); in this case a
static_cast<> is more appropriate.

What do you think?

/FAU

Roger Orr

не прочитано,
14 авг. 2003 г., 05:12:4114.08.2003
"Frank A. Uepping" <Frank....@t-online.de> wrote in message
news:bhbeht$oer$00$1...@news.t-online.com...

> Hello,
> assume we have this definition:
>
> void f(void* p)
> {
> ...
> // E.g.: Need to mangle *p
> memcpy(static_cast<char*>(p) + i, ...);
> ...
> }
>
> The point to consider here is, that p points to a object not
> necessarily a char stream.

If 'p' points to an _object_ then I'd be rather careful about trying to
memcpy to it, unless it is POD type.

void* is dangerous since almost everything fits and type checking is turned
off.
I'd prefer the method signature to be more specific, even if it means an
explicit cast at the call site.

Roger Orr
--
MVP in C++ at www.brainbench.com

Jack Klein

не прочитано,
14 авг. 2003 г., 05:26:5314.08.2003
On 13 Aug 2003 12:06:37 -0400, "Frank A. Uepping"
<Frank....@t-online.de> wrote in comp.lang.c++.moderated:

{Excessive quote snipped -mod}

> What if we want to cast to int*, for example:
> memcpy(static_cast<int*>(p) + i, ...);
> Is static_cast<> still appropriate?
> Consider Section 3.9.2 item 4.
>
> /FAU

Yes, when you are talking about built-in or POD types, static_cast is
correct. So is a C-style cast, although it is deprecated.

But you need to remember that there may be alignment issues if you
start out with a pointer to type A, and end up with a pointer to type
B, and type B has stricter alignment issues than type A. This is
regardless of the type of cast or whether or not you take a detour
through pointer to void.

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

John Potter

не прочитано,
14 авг. 2003 г., 22:11:0014.08.2003
On 14 Aug 2003 05:00:42 -0400, "Frank A. Uepping"
<Frank....@t-online.de> wrote:

> What do you think?

All casts bypass the type system. In the case of void* to char*,
both static_cast and reinterpret_cast should preserve the value.
Using static_cast<char*>(void*) when the void* was obtained from
other than a char* is sending a false message. In any reasonable
implementation, static_cast<char*>(static_cast<void*>(T*)) should
produce the same result as reinterpret_cast<char*>(T*) which
should not change the effective value.

I don't see anything other than ethics which differs. A
static_cast from void* in a container of T* implemented in terms
of a container of void* is a statement of knowledge that the void*
came from a T*. A reinterpret_cast from void* states that there
is something which has been shown to work that the compiler would
not allow. My logic is that if inlining would require adding a
static_cast to void*, then reinterpret_cast is appropriate. The
double static_cast to replace a reinterpret_cast is legal but
should be rejected as unethical.

John

Doug Harrison

не прочитано,
14 авг. 2003 г., 22:22:2714.08.2003
John Potter wrote:

>OTOH, 5.2.10/7
>states that going from T* to void* to T* using reinterpret_cast will
>work

Actually, it talks about pointers to object types, and void is not an
object type. AFAICT, the standard prohibits reinterpret_cast to and
from void*.

--
Doug Harrison
d...@mvps.org

Siemel Naran

не прочитано,
15 авг. 2003 г., 05:34:4315.08.2003
"Bo Persson" <bo...@telia.com> wrote in message news:2Ou_a.24020

> > void f(void* p)
> > {
> > ...
> > // E.g.: Need to mangle *p
> > memcpy(static_cast<char*>(p) + i, ...);
>
> Here you tell the compiler "p actually points to a char". If that is
> so, why not state that in the function header?

We don't want the caller to write stuff like

f(static_cast<char*>(&number));

with static_cast in the function call. We want just f(&number).

> It depends on what you are trying to do. If you (for some reason) need
> some pointer arithmetic in the function, why don't you just declare it
> as
>
> template<class T>
> void f(T* p)
> {
>
> memcpy(p + i, ... );
>
> }
>
> and the compiler will fix it for any type that supports having i added
> to it.
>
> Usually the right question is not "What cast to use", but more often
> "Why use a cast in the first place". Old C code often use casts
> (especially casts to/from void*), where proper C++ code would use
> overloading or inheritance.

For my binary_ostream class I use reinterpret_cast to get the char*
representation. The template function is hidden because we don't want the
user to call binary_ostream::operator<<(const NonPOD&). Is there a better
way?


class binary_ostream : public std::ios
{
public:
binary_ostream(std::streambuf * streambuf) : std::ios(streambuf) { }

binary_ostream& operator<<(const signed short &x) { return put(x); }
binary_ostream& operator<<(const unsigned short &x) { return put(x); }
binary_ostream& operator<<(const signed int &x) { return put(x); }
binary_ostream& operator<<(const unsigned int &x) { return put(x); }
... for all fundamental types ...

private:
template <class T> binary_ostream& put(const T&);
};


template <class T>
inline enhanced::binary_ostream& enhanced::binary_ostream::put(const T& x)
{
if (good() || eof())
{
const char * bits=reinterpret_cast<const char*>(&x);
const int num=sizeof(T);
std::streamsize write=rdbuf()->sputn(bits,num);
if (write!=num) clear(failbit);
}
return *this;
}


--
+++++++++++
Siemel Naran

Francis Glassborow

не прочитано,
15 авг. 2003 г., 13:38:1715.08.2003
In article <nhsnjvg8lo5l35pne...@4ax.com>, Doug Harrison
<d...@mvps.org> writes

>John Potter wrote:
>
>>OTOH, 5.2.10/7
>>states that going from T* to void* to T* using reinterpret_cast will
>>work
>
>Actually, it talks about pointers to object types, and void is not an
>object type. AFAICT, the standard prohibits reinterpret_cast to and
>from void*.

I think this is a defect Anyone like to justify the exclusion of
conversions between void* and T* with reinterpret_cast?

--
Francis Glassborow ACCU
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

Andriy Gapon

не прочитано,
16 авг. 2003 г., 04:04:3816.08.2003
Francis Glassborow wrote:
> I think this is a defect Anyone like to justify the exclusion of
> conversions between void* and T* with reinterpret_cast?

re-interpreting to void* doesn't make sense because void can not be interpreted ?

--
Andriy Gapon

Siemel Naran

не прочитано,
16 авг. 2003 г., 04:10:4816.08.2003
"Francis Glassborow" <fra...@robinton.demon.co.uk> wrote in message

> I think this is a defect Anyone like to justify the exclusion of
> conversions between void* and T* with reinterpret_cast?

That's what static_cast is for :).

--
+++++++++++
Siemel Naran

Francis Glassborow

не прочитано,
16 авг. 2003 г., 09:28:2016.08.2003
In article <xNc%a.75554$_R5.29...@news4.srv.hcvlny.cv.net>, Andriy
Gapon <agapon...@optonline.net> writes

>Francis Glassborow wrote:
> > I think this is a defect Anyone like to justify the exclusion of
> > conversions between void* and T* with reinterpret_cast?
>
>re-interpreting to void* doesn't make sense because void can not be interpreted ?

Language design isn't Linguistic Philosophy 101. The problem manifests
in templates because it means that I cannot write a conversion from U*
to V* where U and V are template type parameters. Of course you may
think that the programmer should not do that but is it the task of the
language to get in the way?


--
Francis Glassborow ACCU
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

Francis Glassborow

не прочитано,
16 авг. 2003 г., 09:30:0716.08.2003
In article
<Oij%a.102340$0v4.7...@bgtnsc04-news.ops.worldnet.att.net>, Siemel
Naran <Sieme...@REMOVE.att.net> writes

>"Francis Glassborow" <fra...@robinton.demon.co.uk> wrote in message
> > I think this is a defect Anyone like to justify the exclusion of
> > conversions between void* and T* with reinterpret_cast?
>
>That's what static_cast is for :).

Unfortunately that does not work for generic programming.

--
Francis Glassborow ACCU
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

Gabriel Dos Reis

не прочитано,
16 авг. 2003 г., 09:31:4316.08.2003
Andriy Gapon <agapon...@optonline.net> writes:

| Francis Glassborow wrote:
| > I think this is a defect Anyone like to justify the exclusion of
| > conversions between void* and T* with reinterpret_cast?
|
| re-interpreting to void* doesn't make sense because void can not be interpreted ?

A pointer of type "void*" does not mean a pointer to an object of type
void -- the value set of the void type is empty.

"void*" is C++'s spelling for "generic pointer to object". There ought
to be possible reinterpret a pointer of type T* as being of the
general type void*.

--
Gabriel Dos Reis, g...@integrable-solutions.net

John Potter

не прочитано,
16 авг. 2003 г., 11:57:0616.08.2003
On 14 Aug 2003 22:22:27 -0400, Doug Harrison <d...@mvps.org> wrote:

> John Potter wrote:

> >OTOH, 5.2.10/7
> >states that going from T* to void* to T* using reinterpret_cast will
> >work

> Actually, it talks about pointers to object types, and void is not an
> object type. AFAICT, the standard prohibits reinterpret_cast to and
> from void*.

It talks about pointers to objects, not pointers to object types. An
rvalue of type void* may only be a pointer to object. With no voids,
it is impossible to point at a void. No problem.

John

Gabriel Dos Reis

не прочитано,
16 авг. 2003 г., 19:49:2616.08.2003
"Siemel Naran" <Sieme...@REMOVE.att.net> writes:

| "Francis Glassborow" <fra...@robinton.demon.co.uk> wrote in message
| > I think this is a defect Anyone like to justify the exclusion of
| > conversions between void* and T* with reinterpret_cast?
|
| That's what static_cast is for :).

Not really. Think template codes.
Rules with special cases tend to be needlessly annoying.

--
Gabriel Dos Reis, g...@integrable-solutions.net

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

David Abrahams

не прочитано,
16 авг. 2003 г., 19:56:3516.08.2003
Francis Glassborow <fra...@robinton.demon.co.uk> writes:

> In article
> <Oij%a.102340$0v4.7...@bgtnsc04-news.ops.worldnet.att.net>, Siemel
> Naran <Sieme...@REMOVE.att.net> writes
>>"Francis Glassborow" <fra...@robinton.demon.co.uk> wrote in message
>> > I think this is a defect Anyone like to justify the exclusion of
>> > conversions between void* and T* with reinterpret_cast?
>>
>>That's what static_cast is for :).
>
> Unfortunately that does not work for generic programming.

It seems to me the need for reinterpret_cast<> in a generic program
must be incredibly rare. You can always

template <class T, class X>
T* f(X* p)
{
return static_cast<T*>(static_cast<void*>(p))
}

if you happen know that p is a pointer to a T as well as a pointer to
an X. reinterpret_cast<T*>(p) seems a lot vaguer in its meaning, to
me.

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

John Potter

не прочитано,
16 авг. 2003 г., 21:46:3016.08.2003
On 16 Aug 2003 04:10:48 -0400, "Siemel Naran"
<Sieme...@REMOVE.att.net> wrote:

> "Francis Glassborow" <fra...@robinton.demon.co.uk> wrote in message
> > I think this is a defect Anyone like to justify the exclusion of
> > conversions between void* and T* with reinterpret_cast?

> That's what static_cast is for :).

Bad logic. Static_cast may downcast to a derived, but that does not
stop reinterpret_cast from doing the same thing possibly with
different results.

5.2.10/7 covers all pointers to object and /8 covers the null pointer
value. Those are the only values that a void* may contain.

John

John Potter

не прочитано,
17 авг. 2003 г., 07:34:1617.08.2003
On 16 Aug 2003 19:56:35 -0400, David Abrahams
<da...@boost-consulting.com> wrote:

> Francis Glassborow <fra...@robinton.demon.co.uk> writes:

> > In article
> > <Oij%a.102340$0v4.7...@bgtnsc04-news.ops.worldnet.att.net>, Siemel
> > Naran <Sieme...@REMOVE.att.net> writes

> >>"Francis Glassborow" <fra...@robinton.demon.co.uk> wrote in message

> >> > I think this is a defect Anyone like to justify the exclusion of
> >> > conversions between void* and T* with reinterpret_cast?

> >>That's what static_cast is for :).

> > Unfortunately that does not work for generic programming.

> It seems to me the need for reinterpret_cast<> in a generic program
> must be incredibly rare.

I can agree with that, but ...

> You can always
>
> template <class T, class X>
> T* f(X* p)
> {
> return static_cast<T*>(static_cast<void*>(p))
> }

But 5.2.9/10 makes it clear that this is not required to do anything
useful. Every reinterpret_cast in any code that does not involve the
pointer to integral to pointer conversions could be written this way.

> if you happen know that p is a pointer to a T as well as a pointer to
> an X. reinterpret_cast<T*>(p) seems a lot vaguer in its meaning, to
> me.

Reinterpret_cast is clearly a loud statement that one knows what they
are doing. Static_cast seems to say that it should just work.

Once upon a time long long ago 9.2/17 said "suitably cast". I asked in
comp.std.c++ whether "suitably" meant
static_cast<M*>(static_cast<void*>(&s)) or reinterpret_cast<M*>(&s).
I don't think the question was answered in the newsgroup; however, the
answer is obvious in the new wording of that paragraph.

One cast is suspect, but nested casts may be a capital offense. :)

John

Gabriel Dos Reis

не прочитано,
17 авг. 2003 г., 07:39:3217.08.2003
David Abrahams <da...@boost-consulting.com> writes:

| Francis Glassborow <fra...@robinton.demon.co.uk> writes:
|
| > In article
| > <Oij%a.102340$0v4.7...@bgtnsc04-news.ops.worldnet.att.net>, Siemel
| > Naran <Sieme...@REMOVE.att.net> writes
| >>"Francis Glassborow" <fra...@robinton.demon.co.uk> wrote in message
| >> > I think this is a defect Anyone like to justify the exclusion of
| >> > conversions between void* and T* with reinterpret_cast?
| >>
| >>That's what static_cast is for :).
| >
| > Unfortunately that does not work for generic programming.
|
| It seems to me the need for reinterpret_cast<> in a generic program
| must be incredibly rare. You can always

still, I don't find that a compelling reason to make a special rule to
disallow it.

| template <class T, class X>
| T* f(X* p)
| {
| return static_cast<T*>(static_cast<void*>(p))
| }
|
| if you happen know that p is a pointer to a T as well as a pointer to
| an X. reinterpret_cast<T*>(p) seems a lot vaguer in its meaning, to
| me.

Why would anyone have to use that circumvolution when one would like
to make the intent of this

return (T*) p;

much clearer? Sometimes, reinterpret_cast<> is just what one wants.

--
Gabriel Dos Reis, g...@integrable-solutions.net

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

David Abrahams

не прочитано,
18 авг. 2003 г., 06:54:4918.08.2003
John Potter <jpo...@falcon.lhup.edu> writes:

> On 16 Aug 2003 19:56:35 -0400, David Abrahams
> <da...@boost-consulting.com> wrote:
>
> > It seems to me the need for reinterpret_cast<> in a generic program
> > must be incredibly rare.
>
> I can agree with that, but ...
>
> > You can always
> >
> > template <class T, class X>
> > T* f(X* p)
> > {
> > return static_cast<T*>(static_cast<void*>(p))
> > }
>
> But 5.2.9/10 makes it clear that this is not required to do anything
> useful.

I know what it says there. However, I claim that it *is* required to
do something useful. See if you can follow:

4.10

-2- An rvalue of type ``pointer to cv T,'' where T is an object type,
can be converted to an rvalue of type ``pointer to cv void.'' The
result of converting a ``pointer to cv T'' to a ``pointer to cv
void'' points to the start of the storage location where the object
of type T resides, as if the object is a most derived object
(intro.object) of type T (that is, not a base class subobject).


Next, Annex C Compatibility C.1.2 Clause 3: basic concepts sez:

6 Change: Converting void* to a pointer-to-object type requires
casting

char a[10];
void *b=a;
void foo() {
char *c=b;
}

ISO C will accept this usage of pointer to void being assigned to
a pointer to object type. C++ will not.

Rationale: C++ tries harder than C to enforce compile-time type
safety.

Effect on original feature: Deletion of semantically well-defined
feature.

Difficulty of converting: Could be automated. Violations will be
diagnosed by the C++ translator. The fix is to add a cast. For
example:

char *c = (char *) b;

Clearly, the intention was never to eliminate C's ability to convert a
void* pointing to the start of some object's storage back into a
pointer to that object (though I definitely think the standard
language should be strengthened here -- it's way too implicit).

Ergo, if you happen to know that the start of storage of some object
x of type X is also the start of storage of an object of type Y, you
can write:

(Y*)(void*)&x [expression 1]

to reach that Y object.

Now by 5.4:

-5- The conversions performed by

a const_cast (expr.const.cast),
a static_cast (expr.static.cast),
a static_cast followed by a const_cast,
a reinterpret_cast (expr.reinterpret.cast), or
a reinterpret_cast followed by a const_cast,

can be performed using the cast notation of explicit type
conversion. The same semantic restrictions and behaviors
apply. If a conversion can be interpreted in more than one of
the ways listed above, the interpretation that appears first in
the list is used...

So it should be clear now that [ignoring cv-qualifications and
const_cast for simplicity] (void*)p is equivalent
static_cast<void*>(p) and (T*)v is equivalent to static_cast<T*>(v)
for any object pointer p and any void pointer v.

Ergo, you can rewrite [expression 1] above as

static_cast<Y*>(static_cast<void*>(&x))

Note that this provides stronger guarantees than does

reinterpret_cast<Y*>(&x)

which AFAICT is completely implementation-defined.

> Every reinterpret_cast in any code that does not involve the
> pointer to integral to pointer conversions could be written this way.

Not neccessarily. A reinterpret_cast has arbitrary,
implementation-defined effects.

> > if you happen know that p is a pointer to a T as well as a pointer to
> > an X. reinterpret_cast<T*>(p) seems a lot vaguer in its meaning, to
> > me.
>
> Reinterpret_cast is clearly a loud statement that one knows what they
> are doing. Static_cast seems to say that it should just work.

I don't know what you mean by that.

> Once upon a time long long ago 9.2/17 said "suitably cast". I asked
> in comp.std.c++ whether "suitably" meant
> static_cast<M*>(static_cast<void*>(&s)) or reinterpret_cast<M*>(&s).
> I don't think the question was answered in the newsgroup; however,
> the answer is obvious in the new wording of that paragraph.

That's all about layout of PODs. You can do stuff with static_casts
(or C-style casts) and non-PODs which can't be done with
reinterpret_cast:

void f(Derived* x)
{
if ((void*)static_cast<Base*>(&x) == (void*)&x)
{
// not useful; I expect you to extrapolate
Base* p = static_cast<Base*>(static_cast<void*>(x));
}
}

> One cast is suspect, but nested casts may be a capital offense. :)

I'm not going to argue on the basis of morality. You're always right
in that dept. ;-)

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

David Abrahams

не прочитано,
18 авг. 2003 г., 06:55:3418.08.2003
Gabriel Dos Reis <g...@integrable-solutions.net> writes:

> David Abrahams <da...@boost-consulting.com> writes:
>
> | Francis Glassborow <fra...@robinton.demon.co.uk> writes:
> |
> | > In article
> | > <Oij%a.102340$0v4.7...@bgtnsc04-news.ops.worldnet.att.net>, Siemel
> | > Naran <Sieme...@REMOVE.att.net> writes
> | >>"Francis Glassborow" <fra...@robinton.demon.co.uk> wrote in message
> | >> > I think this is a defect Anyone like to justify the exclusion of
> | >> > conversions between void* and T* with reinterpret_cast?
> | >>
> | >>That's what static_cast is for :).
> | >
> | > Unfortunately that does not work for generic programming.
> |
> | It seems to me the need for reinterpret_cast<> in a generic program
> | must be incredibly rare. You can always
>
> still, I don't find that a compelling reason to make a special rule to
> disallow it.

Me neither. I'm just saying it doesn't create a terrible capability
gap.

> | template <class T, class X>
> | T* f(X* p)
> | {
> | return static_cast<T*>(static_cast<void*>(p))
> | }
> |
> | if you happen know that p is a pointer to a T as well as a pointer to
> | an X. reinterpret_cast<T*>(p) seems a lot vaguer in its meaning, to
> | me.
>
> Why would anyone have to use that circumvolution when one would like
> to make the intent of this
>
> return (T*) p;
>
> much clearer? Sometimes, reinterpret_cast<> is just what one wants.

template <class T, class X>

T pointer_cast(X* p)
{
return static_cast<T>((void*)p);
}
...

return pointer_cast<T*>(p);

??

Also, see my other post; you may yet be missing something about
static_cast.

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Francis Glassborow

не прочитано,
18 авг. 2003 г., 12:42:3218.08.2003
In article <un0e77...@boost-consulting.com>, David Abrahams
<da...@boost-consulting.com> writes

>Gabriel Dos Reis <g...@integrable-solutions.net> writes:
>
> > David Abrahams <da...@boost-consulting.com> writes:
> >
> > | Francis Glassborow <fra...@robinton.demon.co.uk> writes:
> > |
> > | > In article
> > | > <Oij%a.102340$0v4.7...@bgtnsc04-news.ops.worldnet.att.net>, Siemel
> > | > Naran <Sieme...@REMOVE.att.net> writes
>> > | >"Francis Glassborow" <fra...@robinton.demon.co.uk> wrote in message
> > | >> > I think this is a defect Anyone like to justify the exclusion of
> > | >> > conversions between void* and T* with reinterpret_cast?
> > | >>
> > | >>That's what static_cast is for :).
> > | >
> > | > Unfortunately that does not work for generic programming.
> > |
> > | It seems to me the need for reinterpret_cast<> in a generic program
> > | must be incredibly rare. You can always
> >
> > still, I don't find that a compelling reason to make a special rule to
> > disallow it.
>
>Me neither. I'm just saying it doesn't create a terrible capability
>gap.

I was just throwing in some vague motivation. However I think that the
problem lies elsewhere. A void* is required to point to an object or be
a null pointer in exactly the same way that a T* is. The only difference
is that we do not know the type of the object. I think that means that
there should be no reason to prevent applying a re-intepret_cast to/from
a void*.

--
Francis Glassborow ACCU
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation

Doug Harrison

не прочитано,
18 авг. 2003 г., 20:08:4918.08.2003
John Potter wrote:

>On 14 Aug 2003 22:22:27 -0400, Doug Harrison <d...@mvps.org> wrote:
>
>> John Potter wrote:
>
>> >OTOH, 5.2.10/7
>> >states that going from T* to void* to T* using reinterpret_cast will
>> >work
>
>> Actually, it talks about pointers to object types, and void is not an
>> object type. AFAICT, the standard prohibits reinterpret_cast to and
>> from void*.
>
>It talks about pointers to objects, not pointers to object types.

Oh. I guess I was confused by the part which talks about "pointer to
T1" where "T1 is an object type".

>An
>rvalue of type void* may only be a pointer to object. With no voids,
>it is impossible to point at a void. No problem.

Look up the definition of "object" and "object type". See 1.8/1,
(FWIW, see also footnote 73 in 5.3.5.) Now read 5.2.10/7 again, and
explain how going from T* to void* to T* is not unspecified. I believe
you'll have to show that void is an object type.

See also 3.9.2/4. It says that a void* can "hold" a pointer to any
object type. I don't see how this allows 5.2.10/7 to apply to void*,
as void is not an object by 1.8/1, so again, I think the standard


prohibits reinterpret_cast to and from void*.

You may be correct about the intent, but the wording of 5.2.10/7 is
unclear at best, and I think you have to read a lot into it to draw
your conclusions.

--
Doug Harrison
d...@mvps.org

John Potter

не прочитано,
18 авг. 2003 г., 20:10:4418.08.2003
On 18 Aug 2003 06:54:49 -0400, David Abrahams
<da...@boost-consulting.com> wrote:

> John Potter <jpo...@falcon.lhup.edu> writes:

> Ergo, you can rewrite [expression 1] above as

> static_cast<Y*>(static_cast<void*>(&x))

> Note that this provides stronger guarantees than does

> reinterpret_cast<Y*>(&x)

> which AFAICT is completely implementation-defined.

> > Every reinterpret_cast in any code that does not involve the
> > pointer to integral to pointer conversions could be written this way.

> Not neccessarily. A reinterpret_cast has arbitrary,
> implementation-defined effects.

There are two points here. First is syntax. Any well-formed pointer to
pointer reinterpret_cast may be rewritten as a well-formed double
static_cast through void*. Also note that it is not true for references
because there is no void&.

I think we disagree on "arbitrary". It is expected that all
reinterpret_casts in any reasonable implementation will follow the same
unsurprising intentions of the note in 5.2.10/4. I don't think that
more can be expected from the double static_cast.

long a;
short* p((short*)&a);

Assume size and alignment for long is 4 and short is 2. I expect

(void*)&a == (void*)p
(void*)&a == (void*)reinterpret_cast<short*>(&a)
(void*)&a == (void*)static_cast<short*>(static_cast<void*>(a))
(void*)&a ==
(void*)reinterpret_cast<short*>(reinterpret_cast<char*>(&a))

I claim that the single reinterpret_cast best expresses what is being
done. The value of *p is endian dependant. I have no expectations for
the value of any of the following expressions but expect the same value.

(long*)++p
reinterpret_cast<long*>(++p)
static_cast<long*>(static_cast<void*>(++p))



> > > if you happen know that p is a pointer to a T as well as a pointer to
> > > an X. reinterpret_cast<T*>(p) seems a lot vaguer in its meaning, to
> > > me.

> > Reinterpret_cast is clearly a loud statement that one knows what they
> > are doing. Static_cast seems to say that it should just work.

> I don't know what you mean by that.

It is common to assign trust to the type of cast in decreasing order for
dynamic, static, const, reinterpret. The conotation of static_cast is
that it is likely a reasonable thing to do. The conotation of
reinterpret_cast is that it is nasty, but will work in this case. Using
two static_casts in place of a reinterpret_cast hides the nasty thing
from grep.

There are a few ways to know that p is a pointer to both a T and an X.
One is the pod struct and first element where the standard states that
reinterpret is the cast to use. Another is a union which reduces to
the first. Another is base/derived relationship where one static is
appropriate. Did I miss any?

Given an arbitrary void*, I see no way to know that it is also a T*
for any T. Given the knowledge that a pointer is a T*, there is no
reason to have a void*.

> That's all about layout of PODs. You can do stuff with static_casts
> (or C-style casts) and non-PODs which can't be done with
> reinterpret_cast:

Ignore the non-issues such as short to long and consider pointer only.

I agree that a static_cast may do something other than a
reinterpret_cast in some cases, but I expect a double static_cast
through void* to do the same thing as a reinterpret_cast.

> void f(Derived* x)
> {
> if ((void*)static_cast<Base*>(&x) == (void*)&x)
> {
> // not useful; I expect you to extrapolate
> Base* p = static_cast<Base*>(static_cast<void*>(x));
> }
> }

Sorry, I see no sense here and can't extrapolate to anything. Since
the first static_cast is invalid as written (Derived** to Base*), I
assume that the & should be removed for both. It is a test to see
if the offset of Base within Derived is zero in this implementation.
I suspect that replacing static with reinterpret would produce a
tautology. Given that the test passes, initialization of p could
be performed without the intermediate cast to void*.

It seems that there are two reasons for the new style casts. One is
to force a cast other than what would happen with the old style and
the other is better documentation. I claim that using a double
static_cast through void* produces the same thing as reinterpret_cast
and destroys the documentation. Note that it is also the same as a
double old style cast through void*. I think that you disagree with the
first and consider the second of no value since it is morality.

On the issue of reinterpret_cast to and from void*, I think that the
standard allows it. I think it should to support documenting what
is being done. The template argument is no different from any other
case where it can be replaced by two static casts.

For the original question about using reinterpret or static for a cast
from void* to char*, I can't imagine an implementation doing anything
different. The only reason to do anything would be to detect casting
to something other than the original type with static_cast. Since the
intent was to then modify the char* to point to some offset into an
object and use memcpy to overwrite some number of bytes there, only
reinterpret_cast has a chance in the testing implementation. Since the
question appeared using "should", I assume that the poster had tested
and proven that they did the same thing and was asking about morality.
I think that most answers were of the form use static_cast because it
is valid syntax and every non-nul pointer points to some char.

If anyone has an example of an implementation where the following
fails, it would be appreciated.

template <class T, class U>
void expectations (U* p) {
assert(static_cast<T*>(static_cast<void*>(p)))
== reinterpret_cast<T*>(p));
}


or even

template <class T, class U>
void great_expectations (U* p) {
assert((void*)reinterpret_cast<T*>(p) == (void*)p);
}

John

Torsten Robitzki

не прочитано,
18 авг. 2003 г., 20:31:3718.08.2003
Francis Glassborow wrote:

You can store allong with the void* pointer some pointers to funktions
who know how to interpret a void* because they where stored with the
void*. This functions will will know for sure what type that void* is
and will cast it with a static_cast without danger. I'm using this
technic for implementing some kind of template singletons.

regards Torsten

David Abrahams

не прочитано,
18 авг. 2003 г., 20:33:4318.08.2003
Francis Glassborow <fra...@robinton.demon.co.uk> writes:

I don't think anyone is claiming it's prevented. It does, however,
have non-portable implementation-defined effects.

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Gabriel Dos Reis

не прочитано,
19 авг. 2003 г., 05:16:0519.08.2003
David Abrahams <da...@boost-consulting.com> writes:

[...]

| > | template <class T, class X>
| > | T* f(X* p)
| > | {
| > | return static_cast<T*>(static_cast<void*>(p))
| > | }
| > |
| > | if you happen know that p is a pointer to a T as well as a pointer to
| > | an X. reinterpret_cast<T*>(p) seems a lot vaguer in its meaning, to
| > | me.
| >
| > Why would anyone have to use that circumvolution when one would like
| > to make the intent of this
| >
| > return (T*) p;
| >
| > much clearer? Sometimes, reinterpret_cast<> is just what one wants.
|
| template <class T, class X>
| T pointer_cast(X* p)
| {
| return static_cast<T>((void*)p);
| }

so, we've got to reinvent new syntax for something that could be
expressed cleanly with a single cast. The syntax is there, the
semantics is missing because of an arbitrary rule. Which begs the
question you didn't answer:

Why would anyone have to use that circumvolution when one would like

to make the intent of this [...]


[...]

| Also, see my other post; you may yet be missing something about
| static_cast.

???

--
Gabriel Dos Reis, g...@integrable-solutions.net

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Gabriel Dos Reis

не прочитано,
19 авг. 2003 г., 05:17:2719.08.2003
David Abrahams <da...@boost-consulting.com> writes:

[...]

| > I was just throwing in some vague motivation. However I think that the

| > problem lies elsewhere. A void* is required to point to an object or be
| > a null pointer in exactly the same way that a T* is. The only difference
| > is that we do not know the type of the object. I think that means that
| > there should be no reason to prevent applying a re-intepret_cast to/from
| > a void*.
|
| I don't think anyone is claiming it's prevented.

Huh!??

| It does, however, have non-portable implementation-defined effects.

just like accepting "int main() { }" is implemented-defined.

While there is no dispute that the wording says
"implementation-defined", it would be a foolish implementation to
map T* to void* differently with static_cast<> and reinterpret_cast.

Can you name any benefit gained by disallowing reinterpret_cast<> to
void*?

--
Gabriel Dos Reis, g...@integrable-solutions.net

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

David Abrahams

не прочитано,
19 авг. 2003 г., 06:31:4719.08.2003
John Potter <jpo...@falcon.lhup.edu> writes:

> There are two points here. First is syntax. Any well-formed
> pointer to pointer reinterpret_cast may be rewritten as a
> well-formed double static_cast through void*.

If you meant that the rewrite has the same effects, please justify
your claim. I can see no basis in the standard for it.

> Also note that it is not true for references because there is no
> void&.

Sure.

> I think we disagree on "arbitrary". It is expected that all
> reinterpret_casts in any reasonable implementation will follow the
> same unsurprising intentions of the note in 5.2.10/4.

You mean:

4 A pointer can be explicitly converted to any integral type large
enough to hold it. The mapping function is implementation-defined
[Note: it is intended to be unsurprising to those who know the
addressing structure of the underlying machine. ]

??

This has no bearing on converting between pointers using
reinterpret_casts AFAICT.

Furthermore, I can think of "reasonable implementations" which
undermine your expectation. The addressing structure of the machine
may encode type information in the bits of pointers, for example.

> I don't think that more can be expected from the double static_cast.

The standard says otherwise, AFAICT.

> long a;
> short* p((short*)&a);

That is equivalent to

short* p = reinterpret_cast<short*>(&a);

according to 5.4/5

> Assume size and alignment for long is 4 and short is 2. I expect
>
> (void*)&a == (void*)p

> (void*)&a == (void*)reinterpret_cast<short*>(&a)
> (void*)&a == (void*)static_cast<short*>(static_cast<void*>(a))
> (void*)&a ==
> (void*)reinterpret_cast<short*>(reinterpret_cast<char*>(&a))
>
> I claim that the single reinterpret_cast best expresses what is
> being done.

Probably. OTOH you get special guarantees about the effects of
reinterpret_cast on PODs. Change a to a non-POD type and much code
becomes non-portable, even if with knowledge of the object layout.

>> > Reinterpret_cast is clearly a loud statement that one knows what they
>> > are doing. Static_cast seems to say that it should just work.
>
>> I don't know what you mean by that.
>
> It is common to assign trust to the type of cast in decreasing order for
> dynamic, static, const, reinterpret.

Insert "C-style" between const and reinterpret.

> The conotation of static_cast is that it is likely a reasonable
> thing to do. The conotation of reinterpret_cast is that it is
> nasty, but will work in this case.

It's fine if you know what reinterpret_cast is doing, but the
standard says little about what it actually does. It says a little
more about what static_cast does.

> Using two static_casts in place of a reinterpret_cast hides the
> nasty thing from grep.

I never find nasty things with grep. I think two nested static_casts
are sufficiently noticeable to readers with a brain cell.

> There are a few ways to know that p is a pointer to both a T and an X.
> One is the pod struct and first element where the standard states that
> reinterpret is the cast to use. Another is a union which reduces to
> the first. Another is base/derived relationship where one static is
> appropriate. Did I miss any?

Yes, a runtime check:

if ((void*)&t == (void*)&x) ...

There are other things you can do with void* and static_cast, once
you find the most-derived type with dynamic_cast which can't legally
be done with simple reinterpret_cast.

> Given an arbitrary void*, I see no way to know that it is also a T*
> for any T. Given the knowledge that a pointer is a T*, there is no
> reason to have a void*.

Not true. Type erasure is very powerful. You can then encode the
static_cast which reconstitutes the T* in a runtime-polymorphic
structure. I use something like this in Boost.Python.

> Ignore the non-issues such as short to long and consider pointer only.
>
> I agree that a static_cast may do something other than a
> reinterpret_cast in some cases, but I expect a double static_cast
> through void* to do the same thing as a reinterpret_cast.

Based on which standard text?

>> void f(Derived* x)
>> {
>> if ((void*)static_cast<Base*>(&x) == (void*)&x)
>> {
>> // not useful; I expect you to extrapolate
>> Base* p = static_cast<Base*>(static_cast<void*>(x));
>> }
>> }
>
> Sorry, I see no sense here and can't extrapolate to anything. Since
> the first static_cast is invalid as written (Derived** to Base*), I
> assume that the & should be removed for both. It is a test to see
> if the offset of Base within Derived is zero in this implementation.

Yes.

> I suspect that replacing static with reinterpret would produce a
> tautology. Given that the test passes, initialization of p could be
> performed without the intermediate cast to void*.
>
> It seems that there are two reasons for the new style casts. One is
> to force a cast other than what would happen with the old style and
> the other is better documentation. I claim that using a double
> static_cast through void* produces the same thing as
> reinterpret_cast

Justification in the standard please? I asked about this yesterday
in the CWG and I've received a few responses supporting my POV -
nobody has contradicted it yet.

> and destroys the documentation.

Only if it's really equivalent.

> Note that it is also the same as a double old style cast through
> void*.

Yes.

> I think that you disagree with the first and consider the second of
> no value since it is morality.

I think destroying documentation is bad, and I care about that. But
if I disagree that they are guaranteed to do the same thing, it's not
destroying documentation in the first place.

> On the issue of reinterpret_cast to and from void*, I think that the
> standard allows it.

It does; implementation-defined behavior.

> I think it should to support documenting what is being done. The
> template argument is no different from any other case where it can
> be replaced by two static casts.
>
> For the original question about using reinterpret or static for a cast
> from void* to char*, I can't imagine an implementation doing anything
> different.

You should check the standard; I think there may be special
dispensation for casts from void cv* <-> char cv*.

> The only reason to do anything would be to detect casting
> to something other than the original type with static_cast. Since the
> intent was to then modify the char* to point to some offset into an
> object and use memcpy to overwrite some number of bytes there, only
> reinterpret_cast has a chance in the testing implementation. Since the
> question appeared using "should", I assume that the poster had tested
> and proven that they did the same thing and was asking about morality.
> I think that most answers were of the form use static_cast because it
> is valid syntax and every non-nul pointer points to some char.
>
> If anyone has an example of an implementation where the following
> fails, it would be appreciated.
>
> template <class T, class U>
> void expectations (U* p) {
> assert(static_cast<T*>(static_cast<void*>(p)))
> == reinterpret_cast<T*>(p));
> }

Now John. You know as well as I do that there are lots of tests like
that which succeed for all known implementations and yet are
not promised by the standard. What does it prove?

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

John Potter

не прочитано,
19 авг. 2003 г., 06:35:2119.08.2003
On 18 Aug 2003 20:33:43 -0400, David Abrahams
<da...@boost-consulting.com> wrote:

> I don't think anyone is claiming it's prevented.

["it" is reinterpret_cast to/from void*]

Reread the thread. It was claimed and taken seriously. It is also
claimed again with much more force.

John

John Potter

не прочитано,
19 авг. 2003 г., 06:36:0519.08.2003
On 18 Aug 2003 20:08:49 -0400, Doug Harrison <d...@mvps.org> wrote:

> John Potter wrote:

> >On 14 Aug 2003 22:22:27 -0400, Doug Harrison <d...@mvps.org> wrote:

> >> John Potter wrote:

> >> >OTOH, 5.2.10/7
> >> >states that going from T* to void* to T* using reinterpret_cast will
> >> >work

> >> Actually, it talks about pointers to object types, and void is not an
> >> object type. AFAICT, the standard prohibits reinterpret_cast to and
> >> from void*.

> >It talks about pointers to objects, not pointers to object types.

> Oh. I guess I was confused by the part which talks about "pointer to
> T1" where "T1 is an object type".

Only for specified results.

> >An
> >rvalue of type void* may only be a pointer to object. With no voids,
> >it is impossible to point at a void. No problem.

> Look up the definition of "object" and "object type". See 1.8/1,
> (FWIW, see also footnote 73 in 5.3.5.) Now read 5.2.10/7 again, and
> explain how going from T* to void* to T* is not unspecified. I believe
> you'll have to show that void is an object type.

I agree with this new statement that the result of a reinterpret_cast
to/from void* is unspecified as are almost all reinterpret_casts. We
always assume that the implementation will do the right thing as implied
by the note in 5.2.10/4.

I objected to the former claim that it is ill-formed. I see nothing new
to support that claim. Many unspecified things work fine. Ill-formed
expresions usually do not compile.

John

John Potter

не прочитано,
19 авг. 2003 г., 14:16:5419.08.2003
On 19 Aug 2003 06:31:47 -0400, David Abrahams
<da...@boost-consulting.com> wrote:

> John Potter <jpo...@falcon.lhup.edu> writes:

> > There are two points here. First is syntax. Any well-formed
> > pointer to pointer reinterpret_cast may be rewritten as a
> > well-formed double static_cast through void*.

> If you meant that the rewrite has the same effects, please justify
> your claim. I can see no basis in the standard for it.

Note "syntax".

> You mean:

> 4 A pointer can be explicitly converted to any integral type large
> enough to hold it. The mapping function is implementation-defined
> [Note: it is intended to be unsurprising to those who know the
> addressing structure of the underlying machine. ]

> ??

Yes.

> This has no bearing on converting between pointers using
> reinterpret_casts AFAICT.

I would like to think that it either suggests that implementations do
unsurprising things for reinterpret_casts or that the writers were
very confused. Consider a long* to short* on an implementation where
sizeof(object*) == sizeof(unsigned long). It is expected that
reinterpret_cast<short*>(reinterpret_cast<unsigned long>(longPtr))
will work; however, it is not expected that
reinterpret_cast<short*>(longPtr) will. Did someone really write that
into the standard?

> Furthermore, I can think of "reasonable implementations" which
> undermine your expectation. The addressing structure of the machine
> may encode type information in the bits of pointers, for example.

I understand that. It changes nothing. When cast to void*, they will
still compare equal if they point to the same byte. The purpose of
reinterpret_cast is to bypass the type system and play with raw bytes.
The reinterpret_cast might change the type bits to reflect the new
type while the static_cast to void* would not change them and abort
a program that attempted to static_cast to some other type.

> Justification in the standard please? I asked about this yesterday
> in the CWG and I've received a few responses supporting my POV -
> nobody has contradicted it yet.

Were you clear that the subject is general casting? Do you get
agreement that static_cast<short*>(static_cast<void*>(longPtr)) does
nothing as expected and reinterpret_cast<short*>(longPtr) does an
unknown thing?

Note that 5.2.9/6 states that static_cast<T*>(voidPtr) is valid. The
sole purpose of 5.2.9/10 is to state that in the case where T is the
same as the sourse of the void* it works. Does anyone remember why
it was added about the same time that the statement about
reinterpret_cast to/from first member was added? Related?

> > On the issue of reinterpret_cast to and from void*, I think that the
> > standard allows it.

> It does; implementation-defined behavior.

Then please take up the argument. The mapping used by reinterpret_cast
is implementation defined, but results of most actual casts are
unspecified. Amusing nonsense.

long x;
long* p1(&x);
short* p2(reinterpret_cast<short*>(p1));
long* p3(reinterpret_cast<long*>(p2));
short* p4(reinterpret_cast<short*>(p3));
long* p5(reinterpret_cast<long*>(p4));
assert(p1 == p3); // must pass
assert(p2 == p4); // unspecified
assert(p3 == p5); // must pass

Can you define a dreamed up implimentation mapping where the unspecified
one fails?

Replace short with void in the above and all are unspecified because
void is not an object type, says Doug Harrison. I think that he
originally said that it was ill-formed and may still.

> You should check the standard; I think there may be special
> dispensation for casts from void cv* <-> char cv*.

struct S {
long a
double b;
} s;
*(static_cast<char*>(static_cast<void*>(&s)) + 6) = 'b';

*(reinterpret_cast<char*>(&s) + 6) = 'b'

Are you advocating the first as standard supported over the
second? That is, the standard states what the first will do
and says nothing about the second.

> > If anyone has an example of an implementation where the following
> > fails, it would be appreciated.

> > template <class T, class U>
> > void expectations (U* p) {
> > assert(static_cast<T*>(static_cast<void*>(p)))
> > == reinterpret_cast<T*>(p));
> > }

> Now John. You know as well as I do that there are lots of tests like
> that which succeed for all known implementations and yet are
> not promised by the standard. What does it prove?

That the standard fails to support existing practice.

John

David Abrahams

не прочитано,
19 авг. 2003 г., 20:05:4919.08.2003
Gabriel Dos Reis <g...@integrable-solutions.net> writes:

> David Abrahams <da...@boost-consulting.com> writes:
>
> [...]
>
> | > | template <class T, class X>
> | > | T* f(X* p)
> | > | {
> | > | return static_cast<T*>(static_cast<void*>(p))
> | > | }
> | > |
> | > | if you happen know that p is a pointer to a T as well as a pointer to
> | > | an X. reinterpret_cast<T*>(p) seems a lot vaguer in its meaning, to
> | > | me.
> | >
> | > Why would anyone have to use that circumvolution when one would like
> | > to make the intent of this
> | >
> | > return (T*) p;
> | >
> | > much clearer? Sometimes, reinterpret_cast<> is just what one wants.
> |
> | template <class T, class X>
> | T pointer_cast(X* p)
> | {
> | return static_cast<T>((void*)p);
> | }
>
> so, we've got to reinvent new syntax for something that could be
> expressed cleanly with a single cast. The syntax is there, the
> semantics is missing because of an arbitrary rule. Which begs the
> question you didn't answer:
>
> Why would anyone have to use that circumvolution when one would like
> to make the intent of this [...]

The only reason to use the circumlocution is that the standard
doesn't seem to guarantee reinterpret_cast<> works in all the same
cases.


--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

David Abrahams

не прочитано,
19 авг. 2003 г., 20:07:2419.08.2003
Gabriel Dos Reis <g...@integrable-solutions.net> writes:

> David Abrahams <da...@boost-consulting.com> writes:
>
> [...]
>
> | > I was just throwing in some vague motivation. However I think that the
> | > problem lies elsewhere. A void* is required to point to an object or be
> | > a null pointer in exactly the same way that a T* is. The only difference
> | > is that we do not know the type of the object. I think that means that
> | > there should be no reason to prevent applying a re-intepret_cast to/from
> | > a void*.
> |
> | I don't think anyone is claiming it's prevented.
>
> Huh!??

Is something unclear about what I just wrote?

> | It does, however, have non-portable implementation-defined effects.
>
> just like accepting "int main() { }" is implemented-defined.

AFAIK that is defined to have the same effects as

int main() { return 0; }

Am I missing something?

> While there is no dispute that the wording says
> "implementation-defined", it would be a foolish implementation to
> map T* to void* differently with static_cast<> and reinterpret_cast.

If type information was encoded in pointer representations (not a
foolish idea, for a C++ interpreter with runtime error checking)

struct pointer
{
void* address;
std::type_info const* id;
};

it seems not foolish at all. reinterpret_cast<> would result in a
direct translation of the bits and static_cast would strip the type
information.

> Can you name any benefit gained by disallowing reinterpret_cast<> to
> void*?

No, and I'm not claiming it should be (or even is) disallowed. I am
claiming that there are reasonable implementations in which it has
different effects from static_cast to void*.

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Doug Harrison

не прочитано,
19 авг. 2003 г., 22:53:3119.08.2003
John Potter wrote:

>On 18 Aug 2003 20:08:49 -0400, Doug Harrison <d...@mvps.org> wrote:
>
> > John Potter wrote:
>
> > >On 14 Aug 2003 22:22:27 -0400, Doug Harrison <d...@mvps.org> wrote:
>
> > >> John Potter wrote:
>
> > >> >OTOH, 5.2.10/7
> > >> >states that going from T* to void* to T* using reinterpret_cast will
> > >> >work
>
> > >> Actually, it talks about pointers to object types, and void is not an
> > >> object type. AFAICT, the standard prohibits reinterpret_cast to and
> > >> from void*.
>
> > >It talks about pointers to objects, not pointers to object types.
>
> > Oh. I guess I was confused by the part which talks about "pointer to
> > T1" where "T1 is an object type".
>
>Only for specified results.
>
> > >An
> > >rvalue of type void* may only be a pointer to object. With no voids,
> > >it is impossible to point at a void. No problem.
>
> > Look up the definition of "object" and "object type". See 1.8/1,
> > (FWIW, see also footnote 73 in 5.3.5.) Now read 5.2.10/7 again, and
> > explain how going from T* to void* to T* is not unspecified. I believe
> > you'll have to show that void is an object type.
>
>I agree with this new statement that the result of a reinterpret_cast
>to/from void* is unspecified as are almost all reinterpret_casts.

But what's being discussed here is specifically the "void* and back"
sequence, not "almost all" reinterpret_casts.

>We
>always assume that the implementation will do the right thing as implied
>by the note in 5.2.10/4.

Now we're supposed to "assume" the "right thing" based on a note in
5.2.10/4, which actually refers only to conversions to integral types,
not pointers, and thus not void*?

>I objected to the former claim that it is ill-formed. I see nothing new
>to support that claim. Many unspecified things work fine. Ill-formed
>expresions usually do not compile.

Maybe it's because you clipped this part:

>>See also 3.9.2/4. It says that a void* can "hold" a pointer to any
>>object type. I don't see how this allows 5.2.10/7 to apply to void*,

>>as void is not an object by 1.8/1, so again, I think the standard


>>prohibits reinterpret_cast to and from void*.

Show me where I'm going wrong. For reinterpret_cast to or from void*
to be legal, I believe you'll have to show that void* is a "pointer to
an object", which is the term 5.2.10/7 uses to establish legality.
When I read this literally, it requires showing that void is an
object, which is clearly false. I don't see how I can interpret it
differently without second guessing what the clause says. Moreover, if
5.2.10/7 means to include "void* and back again" as a well-defined
conversion sequence, why does it state in its second sentence that T1
and T2 are "object types", which you've agreed makes the void*
conversion sequence unspecified?

--
Doug Harrison
d...@mvps.org

Gabriel Dos Reis

не прочитано,
20 авг. 2003 г., 09:58:1020.08.2003
David Abrahams <da...@boost-consulting.com> writes:

[...]

| > so, we've got to reinvent new syntax for something that could be


| > expressed cleanly with a single cast. The syntax is there, the
| > semantics is missing because of an arbitrary rule. Which begs the
| > question you didn't answer:
| >
| > Why would anyone have to use that circumvolution when one would like
| > to make the intent of this [...]
|
| The only reason to use the circumlocution is that the standard
| doesn't seem to guarantee reinterpret_cast<> works in all the same
| cases.

And I consider it a bug in the standard text that it outlaws
reinterpret_cast<> to void*. Whether one could use nested casts to
to finally come to express that missing case is irrelevant.

--
Gabriel Dos Reis, g...@integrable-solutions.net

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

llewelly

не прочитано,
20 авг. 2003 г., 13:03:1820.08.2003
Gabriel Dos Reis <g...@integrable-solutions.net> writes:

> David Abrahams <da...@boost-consulting.com> writes:
>
> [...]
>
> | > so, we've got to reinvent new syntax for something that could be
> | > expressed cleanly with a single cast. The syntax is there, the
> | > semantics is missing because of an arbitrary rule. Which begs the
> | > question you didn't answer:
> | >
> | > Why would anyone have to use that circumvolution when one would like
> | > to make the intent of this [...]
> |
> | The only reason to use the circumlocution is that the standard
> | doesn't seem to guarantee reinterpret_cast<> works in all the same
> | cases.
>
> And I consider it a bug in the standard text that it outlaws
> reinterpret_cast<> to void*. Whether one could use nested casts to
> to finally come to express that missing case is irrelevant.

I think somebody should submit a DR, because I for one have seen a lot
of code that relies on casting T* to void* and back.

David Abrahams

не прочитано,
20 авг. 2003 г., 19:53:3220.08.2003
John Potter <jpo...@falcon.lhup.edu> writes:

> On 19 Aug 2003 06:31:47 -0400, David Abrahams
> <da...@boost-consulting.com> wrote:
>
>> John Potter <jpo...@falcon.lhup.edu> writes:
>
>> > There are two points here. First is syntax. Any well-formed
>> > pointer to pointer reinterpret_cast may be rewritten as a
>> > well-formed double static_cast through void*.
>
>> If you meant that the rewrite has the same effects, please justify
>> your claim. I can see no basis in the standard for it.
>
> Note "syntax".
>
>> You mean:
>
>> 4 A pointer can be explicitly converted to any integral type large
>> enough to hold it. The mapping function is implementation-defined
>> [Note: it is intended to be unsurprising to those who know the
>> addressing structure of the underlying machine. ]
>
>> ??
>
> Yes.
>
>> This has no bearing on converting between pointers using
>> reinterpret_casts AFAICT.
>
> I would like to think that it either suggests that implementations do
> unsurprising things for reinterpret_casts or that the writers were
> very confused.

I hope the former. However, you and I probably have different ideas
of what "unsurprising" means. I always thought reinterpret_cast
meant "reinterpret the bits", something like:

template <class T, class U>

T reinterpret_cast(U const& u) // (**)
{
union { T t; U u } both;
memset(both, sizeof(both), 0);
memcpy(u, both.u, sizeof(u));
return both.t;
};

> Consider a long* to short* on an implementation where
> sizeof(object*) == sizeof(unsigned long). It is expected that
> reinterpret_cast<short*>(reinterpret_cast<unsigned long>(longPtr))
> will work;

What does "work" mean? If the standard says anything about what that
does, it says "it's implementation-defined".

> however, it is not expected that reinterpret_cast<short*>(longPtr)
> will. Did someone really write that into the standard?

You can read the words as easily as I can.

>> Furthermore, I can think of "reasonable implementations" which
>> undermine your expectation. The addressing structure of the machine
>> may encode type information in the bits of pointers, for example.
>
> I understand that. It changes nothing. When cast to void*, they will
> still compare equal if they point to the same byte.

Not unless reinterpret_cast has a surprising meaning (to me).

> The purpose of reinterpret_cast is to bypass the type system and
> play with raw bytes

Precisely. Why should it change a pointer's raw bytes when you cast
it to void*? reinterpret_cast says "I know something about the
underlying representation, let me handle it myself".

> The reinterpret_cast might change the type bits to reflect the new
> type while the static_cast to void* would not change them and abort
> a program that attempted to static_cast to some other type.

No, it's not allowed to do that, provided that "some other type"
really exists at that location. I've proven that.

>> Justification in the standard please? I asked about this yesterday
>> in the CWG and I've received a few responses supporting my POV -
>> nobody has contradicted it yet.
>
> Were you clear that the subject is general casting?

Yes.

> Do you get agreement that
> static_cast<short*>(static_cast<void*>(longPtr)) does nothing as
> expected

I never claimed it does nothing, but anyway...

> and reinterpret_cast<short*>(longPtr) does an unknown
> thing?

Apparently.

> Note that 5.2.9/6 states that static_cast<T*>(voidPtr) is valid. The
> sole purpose of 5.2.9/10 is to state that in the case where T is the
> same as the sourse of the void* it works. Does anyone remember why
> it was added about the same time that the statement about
> reinterpret_cast to/from first member was added?

I don't. Can't speak for others.

> Related?

Who knows?

>> > On the issue of reinterpret_cast to and from void*, I think that the
>> > standard allows it.
>
>> It does; implementation-defined behavior.
>
> Then please take up the argument.

What do you mean?

> The mapping used by reinterpret_cast is implementation defined, but
> results of most actual casts are unspecified. Amusing nonsense.

I'm not sure if I agree with the interpretation, but even if I did:
hey, I don't write the core language.

> long x;
> long* p1(&x);
> short* p2(reinterpret_cast<short*>(p1));
> long* p3(reinterpret_cast<long*>(p2));
> short* p4(reinterpret_cast<short*>(p3));
> long* p5(reinterpret_cast<long*>(p4));
> assert(p1 == p3); // must pass
> assert(p2 == p4); // unspecified
> assert(p3 == p5); // must pass
>
> Can you define a dreamed up implimentation mapping where the unspecified
> one fails?

I just did. It's the one where reinterpret_cast preserves bits.

> Replace short with void in the above and all are unspecified because
> void is not an object type, says Doug Harrison. I think that he
> originally said that it was ill-formed and may still.

I don't have an opinion about that.

>> You should check the standard; I think there may be special
>> dispensation for casts from void cv* <-> char cv*.
>
> struct S {
> long a
> double b;
> } s;
> *(static_cast<char*>(static_cast<void*>(&s)) + 6) = 'b';
>
> *(reinterpret_cast<char*>(&s) + 6) = 'b'
>
> Are you advocating the first as standard supported over the
> second? That is, the standard states what the first will do
> and says nothing about the second.

I think that's true, advocacy aside.

>> > If anyone has an example of an implementation where the following
>> > fails, it would be appreciated.
>
>> > template <class T, class U>
>> > void expectations (U* p) {
>> > assert(static_cast<T*>(static_cast<void*>(p)))
>> > == reinterpret_cast<T*>(p));
>> > }
>
>> Now John. You know as well as I do that there are lots of tests like
>> that which succeed for all known implementations and yet are
>> not promised by the standard. What does it prove?
>
> That the standard fails to support existing practice.

Actually, I can use the same kind of test to prove the opposite.
Are there any examples of implementations which do something very
different from my pseudocode for reinterpret_cast (**) above? If
not, it shows that existing practice is perfectly coherent with the
standard.

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

John Potter

не прочитано,
20 авг. 2003 г., 20:01:4320.08.2003
On 19 Aug 2003 20:07:24 -0400, David Abrahams
<da...@boost-consulting.com> wrote:

> Gabriel Dos Reis <g...@integrable-solutions.net> writes:

> > David Abrahams <da...@boost-consulting.com> writes:

> > | > I was just throwing in some vague motivation. However I think that the
> > | > problem lies elsewhere. A void* is required to point to an object or be
> > | > a null pointer in exactly the same way that a T* is. The only difference
> > | > is that we do not know the type of the object. I think that means that
> > | > there should be no reason to prevent applying a re-intepret_cast to/from
> > | > a void*.

> > | I don't think anyone is claiming it's prevented.

> > Huh!??

> Is something unclear about what I just wrote?

It is just clear that you have not been following the discussion. I
will assume that you are now on track.

> > While there is no dispute that the wording says
> > "implementation-defined", it would be a foolish implementation to
> > map T* to void* differently with static_cast<> and reinterpret_cast.

> If type information was encoded in pointer representations (not a
> foolish idea, for a C++ interpreter with runtime error checking)

Good. This is comp.lang not comp.std. Please shoot (phaser set to
stun :) the language lawyers and ask the implementers who wish to sell
that interpreter.

Major premise is that reinterpret_cast is considered to be the thing
which gives us the good old days when men were men, women were women,
and pointers were ints. It is the general do what I said regardless
of any silly high level language rules cast. That view is supported
by the pointer to integral and integral to pointer reinterpret_casts.

> it seems not foolish at all.

Agreed.

> reinterpret_cast<> would result in a
> direct translation of the bits and static_cast would strip the type
> information.

Does not compute. Static_cast is clean, reinterpret_cast is dirty.
Note that type information is lost when using reinterpret_cast with
an integral. Reinterpret_cast<char*>(42) is valid with no chance to
keep/lose type information. The result must be type char*.

> > Can you name any benefit gained by disallowing reinterpret_cast<> to
> > void*?

> No, and I'm not claiming it should be (or even is) disallowed. I am
> claiming that there are reasonable implementations in which it has
> different effects from static_cast to void*.

Assume allignment for all types is one to avoid distractions.

1 static_cast<short*>(longPtr) ill-formed
2 reinterpret_cast<short*>(longPtr) well-formed
3 static_cast<short*>(static_cast<void*>(longPtr)) well-formed
4 static_cast<short*>(reinterpret_cast<void*>(longPtr)) ?-formed
5 reinterpret_cast<short*>(static_cast<void*>(longPtr)) ?-formed
6 reinterpret_cast<short*>(reinterpret_cast<void*>(longPtr)) ?-formed

Assume that the interpreter will report a dereference of a pointer
to T that points to a U. Reinterpret_cast bypasses safety by design.
Pass and fail in the following refer to dereferencing the result.

Case 2 must pass. Produces a pointer to short with short info.
Case 3 must fail. Produces a pointer to short with long info.
Requires the static_cast to void* to retain the long info.
Case 4 must fail. Produces a pointer to short with long info.
Requires the reinterpret_cast to void* to retain the long info.
Case 5 must pass. Produces a pointer to short with short info.
Case 6 must pass. Produces a pointer to short with short info.

In general, static_cast sets the type to be the source type regardless
of the destination type. In general, reinterpret_cast sets the type to
be the destination type regardless of the source type. The only
exception is reinterpret_cast to void* which must retain the source
type because of case 4.

No reason to make reinterpret_cast _from_ void* ill-formed.

7 static_cast<Derived*>(basePtr) well-formed

This is a dynamic_cast which can be checked by an interpreter. It
should record the dynamic type for testing in dereference. Not a
problem for reinterpret_cast which records Derived regardless and
does not report an error on dereference. Reinterpret_cast is
designed to produce undefined behavior which might just be
interpreter defined behavior.

8 static_cast<short*>(reinterpret_cast<void*>(
reinterpret_cast<unsigned long>(longPtr))) ?-formed

This is a problem. It must fail. There is no way for the cast from
integral to recover the lost type into the void*. Either all
reinterpret_casts _to_ void* or just those _from_ integral must be
forbidden. For safe static_cast _from_ void*, void* must contain
source type.

I didn't want this, but it fell out the end.

The standard clearly allows reinterpret integral to/from any pointer
including void*. Should it?

I think it allows reinterpret object* to/from void*? Should it?

I leave any DR to language lawyers elsewhere.

John

Gabriel Dos Reis

не прочитано,
20 авг. 2003 г., 20:04:5120.08.2003