I've noticed that on pg. 342 of Stroustups "C++ Programming Language"
book his example of using partial specialization for a custom vector
template where T is a pointer to anything to avoid bloat, he uses
reinterpret_cast to go back and forth between void* and T* pointers.
Why? Why not use static_cast?
1) he made a mistake (unlikely)
2) does static_cast with complicated T* to void* cause address shifts?
If so this could cause problems with preservation of a NULL value as
well as a round-trip inconsistency. My feeling is that
reinterpret_cast simply will not change the actual pointer value stored
from the assembly perspective in either direction, which is desired.
3) is it because of ever so slightly better performance?
As a result of his example, I have decided throughout my program to use
reinterpret_cast to go back and forth between void* and typed pointers.
I use static_cast only when narrowing to a derived type from a base
class type.
Can anyone give me the decisive opinion on this issue with
reinterpret_cast and void*, and in particular, why Stroustup uses
reinterpret_cast instead of static_cast?
Thanks,
Andy
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
> As a result of his example, I have decided throughout my program to use
> reinterpret_cast to go back and forth between void* and typed pointers.
Why adopt someone else's style before you even understand why? It is not a
good way to learn.
If you want to convert a void* to any other pointer type, use static_cast:
void *pv = 0;
int *pi = static_cast<int*>(pv);
I don't have Bjarne's book here with me, so I can't look over the code
snippet. Perhaps he used reinterpret_cast simply because it slipped his mind
that static_cast would get the job done.
--
Frederick Gotham
This has been fixed in the "Special Edition" to use static_cast.
And I'd have to agree with Mr. Gotham that you should probably verify
_before_ making blanket code changes ;)
josef
In the following code, is null preserved?
main () {
void* pv = 0;
if (static_cast<int*>(pv) == 0)
cout << "null preserved";
It can get pretty complex for me, namely I also want to store 2 types
of pointers in a virtual hierarchy as void* and then retrieve it and
narrow it to the base class pointer. The question in the following
hierarchy of widening to an interface from 2 different derivations, and
then further widening to void*, and then being able to narrow back from
void* to the interface, what's better than my use of
reinterpret_cast<>.
class Arc;
struct ArcLink {
virtual Arc* ThisArc () = 0;
virtual ArcLink* NextLink () = 0;
};
class Arc : public ArcLink {
... some data members
public:
virtual Arc* ThisArc () { return this; }
virtual ArcLink* NextLink() { return 0; }
... additional functionality
};
class ArcLinkImpl : public ArcLink {
Arc* arc;
ArcLink* tail;
public:
virtual Arc* ThisArc () { return arc; }
virtual ArcLink* NextLink () { return tail; }
};
// from utilities for a red-black set
struct IntSetElem {
void* obj;
private:
IntSetElem* left;
IntSetElem* right;
int key;
bool color;
};
// and the question is, what casts to and from elem->obj
main () {
IntSetElem* elem1 = ...; // get an element from a set
IntSetElem* elem2 = ...; // get another
Arc* arc = ...; // create an arc
ArcLinkImpl* arclink = ...; // create an arclink
ArcLink* intf = arc; // widen
elem1->obj = reinterpret_cast<void*>(intf);
//??? can I pass arc directly to the cast here
intf = arclink; // a different widen
elem2->obj = reinterpret_cast<void*>(intf);
//??? can I pass arclink directly to the cast here
// now fetch Arc* as ArcLink*
intf = reinterpret_cast<ArcLink*>(elem1->obj);
// I bet this is ok because I made sure to stuff it
// as an ArcLink* interface pointer
// what's better???
// now fetch ArcLinkImpl* as ArcLink*
intf = reinterpret_cast<ArcLink*>(elem2->obj);
// again ok because I stuffed the ArcLinkImpl*
// as an ArcLink*
// what's better???
}
Andy
> good way to learn.
Because it's from a textbook, a very renowned one.
Looking back on my early days, I realise that reading others' codes
(including the style) and imitating them have been quite an effective way
to learn programming, even when I could not understand all of them.
Hence the importance of good-style examples in textbooks, and it comes to
me as a surprise that Stroustrup used reinterpret_cast where he didn't
have to, while explaining in another part of the same book that the new-
style casts were named so ugly that it could discourage their use.
--
Seungbeom Kim
> As a result of his example, I have decided throughout my program to use
> reinterpret_cast to go back and forth between void* and typed pointers.
> I use static_cast only when narrowing to a derived type from a base
> class type.
>
> Can anyone give me the decisive opinion on this issue with
> reinterpret_cast and void*, and in particular, why Stroustup uses
> reinterpret_cast instead of static_cast?
It's a typo. It's corrected in later editions; the snippet of code on
p. 342 of my third edition doesn't contain any reinterpret_casts.
In other words, it's a pretty good idea to make sure you understand
_why_ something is done before you start doing it yourself. Even
respected texts contain errors, and furthermore, even experts sometimes
get things wrong.
--
Erik Max Francis && m...@alcyone.com && http://www.alcyone.com/max/
San Jose, CA, USA && 37 20 N 121 53 W && AIM erikmaxfrancis
Would a harvest from my heart / Find its season
-- Sandra St. Victor
A mistake is indeed unlikely. So logically, we can speculate that using
reinterpret_cast at the time this example was written - was likely not
a mistake in C++.
> 2) does static_cast with complicated T* to void* cause address shifts?
> If so this could cause problems with preservation of a NULL value as
> well as a round-trip inconsistency. My feeling is that
> reinterpret_cast simply will not change the actual pointer value stored
> from the assembly perspective in either direction, which is desired.
If changing the actual pointer value is undesirable - why would a
static_cast change the pointer value any more than reinterpret_cast? A
void pointer can be a pointer to any type - so with each type just as
likely as the other, would we not expect either cast to leave the
pointer's value unchanged?
> 3) is it because of ever so slightly better performance?
Not a chance.
> As a result of his example, I have decided throughout my program to use
> reinterpret_cast to go back and forth between void* and typed pointers.
> I use static_cast only when narrowing to a derived type from a base
> class type.
I'm afraid you that you fell victim to the "argument from authority"
logical fallacy (or, for those more familiar with its Latin name: the
"argumentum ad verecundiam'' or simply the "ipse dixit" line of
reasoning. Wikipedia has a good article on this all-too-common fallacy:
http://en.wikipedia.org/wiki/Appeal_to_authority .
> Can anyone give me the decisive opinion on this issue with
> reinterpret_cast and void*, and in particular, why Stroustup uses
> reinterpret_cast instead of static_cast?
I have no idea of the actual reason - nor do I think the the issue is
particularly significant. But I would guess that - at the time - the
names of the cast operators were not yet final, so a retroactive
"mistake" was always a possibillity once the cast operators were given
names. Furthermore, choosing one or the other cast won't make any
difference to how the program runs. So rather than spend too much time
agonizing over whether to use reinterpret_cast or static_cast is the
correct choice - just pick one and move on to finishing the rest of the
program. Because - while it is true that every programmer should strive
to write correct code - it also true that no program good enough to
ship has likely ever been delayed in order for its programmers to have
time to make editorial corrections and other cosmetic improvements to
its source code.
Greg
> In the following code, is null preserved?
>
> main () {
> void* pv = 0;
> if (static_cast<int*>(pv) == 0)
> cout << "null preserved";
> }
Yes, of course.
So long,
Thomas
> In the following code, is null preserved?
> main () {
> void* pv = 0;
> if (static_cast<int*>(pv) == 0)
> cout << "null preserved";
> }
All pointer casts preserve null.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
> It can get pretty complex for me, namely I also want to store 2 types
> of pointers in a virtual hierarchy as void* and then retrieve it and
> narrow it to the base class pointer.
Possibly a not very C++ way to approach the problem. Is this time
critical? If not, you could provide a virtual "getPointer()" method
(please choose a better name) in the base class that returns the common
base of the two objects, and use covariance to specialize this method in
the derived classes. Alternatively, use a container that is templated to
the class you want to store in.
This should probably be a template where the templare parameter
describes the object pointed to. Alternatively, why not use an
already available container from the STL or Boost? Which kind of
operations do you need for IntSetElem?
> // and the question is, what casts to and from elem->obj
> main () {
> IntSetElem* elem1 = ...; // get an element from a set
> IntSetElem* elem2 = ...; // get another
> Arc* arc = ...; // create an arc
> ArcLinkImpl* arclink = ...; // create an arclink
>
> ArcLink* intf = arc; // widen
> elem1->obj = reinterpret_cast<void*>(intf);
> //??? can I pass arc directly to the cast here
No cast is needed here, though maybe you would like to use an
IntSetElem<ArcLink> here?
> intf = arclink; // a different widen
This works.
> elem2->obj = reinterpret_cast<void*>(intf);
> //??? can I pass arclink directly to the cast here
No cast required. Every pointer can be converted to a void *
without a cast.
> // now fetch Arc* as ArcLink*
> intf = reinterpret_cast<ArcLink*>(elem1->obj);
Bad idea. Either use the template idea, or use a static_cast<>.
You do not care about the "raw bit pattern" of the pointer here. What
you want to tell the compiler is to narrow the pointer to a more
specific object.
> // I bet this is ok because I made sure to stuff it
> // as an ArcLink* interface pointer
> // what's better???
To use an already available container, likely.
> // now fetch ArcLinkImpl* as ArcLink*
> intf = reinterpret_cast<ArcLink*>(elem2->obj);
> // again ok because I stuffed the ArcLinkImpl*
> // as an ArcLink*
> // what's better???
No cast at all if you can avoid it. If elem2 would be an
IntSetElem<ArcLink> and would be declared as follows:
template<class T>
struct IntSetElem {
T* obj;
private:
IntSetElem* left;
IntSetElem* right;
int key;
bool color;
};
no cast whatsoever would be required, and type-correctness would
be guaranteed by the compiler.
By the way: Why is this a struct? And are you sure the compiler
generated copy and assignment operators are what you want?
So long,
Thomas
> It can get pretty complex for me, namely I also want to store
> 2 types of pointers in a virtual hierarchy as void* and then
> retrieve it and narrow it to the base class pointer.
Both static_cast to void and reinterpret_cast have the
constraint that you must cast the pointer back to the original
type if they are to work. In your case, you should explicitly
narrow the pointer before converting it to void*, then cast back
to the narrowed type.
> class Arc;
I would have thought that that was narrowing.
> elem1->obj = reinterpret_cast<void*>(intf);
> //??? can I pass arc directly to the cast here
You don't need the cast. Note, however, that to use the void*
(other than copying it), you must first convert it to an
ArcLink* (I'd use static_cast for this), or you have undefined
behavior.
> intf = arclink; // a different widen
Again, you're using widen for the operation you described as
narrowing in the text at the top. This is derived to base.
> elem2->obj = reinterpret_cast<void*>(intf);
> //??? can I pass arclink directly to the cast here
Of course. Why would it be different than the previous case.
> // now fetch Arc* as ArcLink*
> intf = reinterpret_cast<ArcLink*>(elem1->obj);
> // I bet this is ok because I made sure to stuff it
> // as an ArcLink* interface pointer
> // what's better???
I'd use static_cast.
This is fine: you stored an ArcLink*, and you cast back to the
same type.
> // now fetch ArcLinkImpl* as ArcLink*
> intf = reinterpret_cast<ArcLink*>(elem2->obj);
> // again ok because I stuffed the ArcLinkImpl*
> // as an ArcLink*
> // what's better???
As above.
> }
I'd still be very leary of this. It's all too easy to forget
the passage through ArcLink* when storing the pointer, and then
it won't work---static_cast or reinterpret_cast when extracting
the pointer. Wrap IntSetElem in a class which takes ArcLink*
pointers, so the user cannot set the pointers without converting
his pointers to ArcLink*.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
> A mistake is indeed unlikely. So logically, we can speculate that using
> reinterpret_cast at the time this example was written - was likely not
> a mistake in C++.
A bit extreme don't you think... ? Programmers make mistakes here and there
-- but we catch them when we read back over our code.
I _know_ that static_cast is the one for the job, so my first guess would be
that Bjarne made a mistake by using reinterpret_cast. But then again,
there's
no observable difference in the final program.
--
Frederick Gotham
> > I've noticed that on pg. 342 of Stroustups "C++ Programming Language"
> > book his example of using partial specialization for a custom vector
> > template where T is a pointer to anything to avoid bloat, he uses
> > reinterpret_cast to go back and forth between void* and T* pointers.
> > Why? Why not use static_cast?
> > 1) he made a mistake (unlikely)
> A mistake is indeed unlikely. So logically, we can speculate that using
> reinterpret_cast at the time this example was written - was likely not
> a mistake in C++.
It's not really a mistake today, since it has the same effect as
static_cast in this context.
Still, Stroustrup is human (and I think he'd be the last to deny
it). Typos occur, and he's not immune to them, any more than
you or I are. He corrected it in later editions; I'd consider
that a recognition on his part that there was a mistake.
> > 2) does static_cast with complicated T* to void* cause
> > address shifts? If so this could cause problems with
> > preservation of a NULL value as well as a round-trip
> > inconsistency. My feeling is that reinterpret_cast simply
> > will not change the actual pointer value stored from the
> > assembly perspective in either direction, which is desired.
> If changing the actual pointer value is undesirable - why
> would a static_cast change the pointer value any more than
> reinterpret_cast?
Because it does. Not when casting to and from a void*, but when
casting up and down in an inheritance hierarchy.
> A void pointer can be a pointer to any type - so with each
> type just as likely as the other, would we not expect either
> cast to leave the pointer's value unchanged?
Strictly speaking: the standard does not require that the
address of a non-POD object point to the first byte of that
object; an implementation could place the vptr at the address -
4, or some such. In which case, both reinterpret_cast and
static_cast will change the physical address. (Presumable, in
the case of reinterpret_cast, because of course, all that the
standard requires is that you can cast it to void* and back to
the original type.) In practice, I've never heard of such an
implementation.
[...]
> I have no idea of the actual reason - nor do I think the the
> issue is particularly significant. But I would guess that - at
> the time - the names of the cast operators were not yet final,
> so a retroactive "mistake" was always a possibillity once the
> cast operators were given names.
The names were finalized very early, if I remember correctly.
On the other hand, our instincts as to which cast is correct
didn't really develope until after that.
> Furthermore, choosing one or the other cast won't make any
> difference to how the program runs.
But it will affect how a maintenance programmer sees the code.
Which is, in many cases, just as important.
--
James Kanze (Gabi Software) email: kanze...@neuf.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
But it is WRONG to use reinterpret_cast in this example?
In my programs, I use reinterpret_cast to cast to void * and cast back
to the original pointer type.
Are there any drawbacks using this approach? What does static_cast buy
me in this case?
Diego Martins
HP
Not wrong, just unwise. When you use reinterpret_cast you can make
mistakes that are impossible using static_cast and using static_cast,
you can make mistakes that are impossible using C-style cast. Thus, by
using the "least powerful" cast that can do the job, you minimize
errors and the likelyhood of maintenance errors. In particular,
reinterpret_cast can convert between pointers and integers - something
you wouldn't want to happen by accident.
See http://www.research.att.com/~bs/bs_faq2.html#static-cast
Also, when you use static_cast, you stay within the semantics of the
language; when you use reinterpret_cast, you play with bits (the
representation). By doing static_cast<int*>(pv) when pv is a void*, you
assume that a void* is represented in exactly the same ways as an int*.
That's by far the most common, but not guaranted.
-- Bjarne Stroustrup; http://www.research.att.com/~bs
It sends the wrong message to the reader.
When I see a reinterpret_cast in code, I immediately assume that
there is something very delicate and implementation dependant
going on. When I see a static_cast, I suppose that while you
are subverting the type system somewhat, it is within more
reasonable bounds, and is portable---I don't have to pay
particular attention to it when porting to another system, for
example.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
>All pointer casts preserve null.
True for null pointer values. It's important to distinguish those ones
from "null pointer constants", though. See core issue 463 (and/or the
current working paper).
--
Gennaro Prota
>When you use reinterpret_cast you can make
>mistakes that are impossible using static_cast
Ok :-)
>and using static_cast,
>you can make mistakes that are impossible using C-style cast.
This one perplexes me. Could you please elaborate?
--
Gennaro Prota
Is that a typo?
Otherwise, an example would be nice.
One counter-example is a C style cast that originally worked as a
const_cast but ends up being e.g. a reinterpret_cast, which is difficult
to achieve with static_cast; another counter-example is a C style cast
that unintentionally casts to an inaccessible base (breaking the
design), which again is difficult to achieve with static_cast.
Cheers,
- Alf
--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
> >All pointer casts preserve null.
> True for null pointer values. It's important to distinguish
> those ones from "null pointer constants", though. See core
> issue 463 (and/or the current working paper).
Which represents a change compared to what was originally
adopted, according to the intent indicated in the footnote.
(In this case, it seems a reasonable change, given the small
number of cases affected.)
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
A typo. Sorry. "Can not"
-- Bjarne Stroustrup; http://www.research.att.com/~bs
This statement scares me somewhat, but hopefully its just my
understanding of english that suffers. static_casting from e.g. void*
to int* can only be performed when the representation for void* is the
same as for int* - and there is nothing that guarantees that this will
work? So - by inference - malloc is not guaranteed to work?
I surely must misunderstand something - and yet I am the only one to
react (so far!).
/Peter
There is a specific scenario that I have never been clear about in all
these years w.r.t reinterpret_casts.
In MSFT's COM-land, there is an API called CoCreateInstance -- its last
parameter is of type void**. This API is used to get a pointer to the
interface (abstract class to be precise) you are interested in. So
people always do something like this:
struct IUnknown
{
virtual void QueryInterface(REFIID riid, void** ppv) = 0;
virtual void AddRef() = 0;
virtual void Release() = 0;
};
struct ISomeInterface : public IUnknown
{
virtual void DoIt() = 0;
};
ISomeInterface* isi;
CoCreateInstance(......,...., reinterpret_cast<void**>(&isi));
Internally a lot of hocus-pocus happens and the API ends up calling an
implementation of a standard IUnknown method called QueryInterface that
returns a pointer to ISomeInterface like so:
class SomeInterfaceImpl : public ISomeInterface
{
void QueryInterface(REFIID riid, void** ppv)
{
*ppv = static_cast<ISomeInterface*>(this);
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
}
// remaining implementations elided for clarity
};
Have the proper casts been used in the above code? Can you explain why
reinterpret_cast is needed in places where its been used?
The code could be rewritten using static_cast:
void* pv;
CoCreateInstance(......,...., &pv);
ISomeInterface* isi = static_cast<ISomeInterface*>(pv);
--
Andrei Polushin
This is pretty system specific, so I don't know if this will go or not.
But, I think I can explain that. A COM object can implement many
independent interfaces as well as inherited interfaces and All
interfaces inherit from IUnknown. So, they end up constructing a
fairly fancy vtbl to implement all of this. The static_cast<> above
can move the pointer up and down the vtbl to the root of the
appropriate interface. The reinterpret_cast<> is then used to be sure
that the appropriate IUnknown will be invoked without further moving of
the pointer. I will try to draw a picture. If we have 2 interfaces,
we might have a vtbl that looks like this:
1: IUnknown
2: IInterface1
3: IUnknown
4: IInterface2
if we static cast to Interface2, the pointer will correspond to
position 3. When we invoke the IUnknown addref method, we want the
IUnknown from position 3 and not the IUnknown and position 1. To
indicate that there is something tricky going on and that we are
getting a specific IUnknown, they are using a reinterpret_cast.
Hopefully that sheds some light and isn't too far off base.
To bring it back to normal C++, we can imagine an interface like:
class base
{
virtual ~Base(){}
virtual void Print() = 0;
}
class IInterface1 : public Base
{
}
class IInterface2 : public Base
{
}
class Implement : public IInterface1, public IInterface2
{
}
Now, if we wanted to get a pointer to the Base that came with
IInterface2 we might static_cast to IInterface2 the reinterpret cast to
Base. I do not believe that the reinterpret_cast is required to do
this, but it does signal that something tricky is going on.
Hopefully, I haven't spread too many lies above, I have slept at least
once since doing any COM stuff. :)
joe
> This statement scares me somewhat, but hopefully its just my
> understanding of english that suffers. static_casting from
> e.g. void* to int* can only be performed when the
> representation for void* is the same as for int* - and there
> is nothing that guarantees that this will work? So - by
> inference - malloc is not guaranteed to work? I surely must
> misunderstand something - and yet I am the only one to react
> (so far!).
I think that there was a type here as well, and that Bjarne was
in fact talking about reinterpret_cast. (Technically, the
mapping done by reinterpret_cast is implementation defined, and
an implementation could make it work here regardless. But the
statement "you play with bits" is, I think, an accurate informal
description of the intent of reinterpret_cast.)
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
I believe you need more than the Standard guarantees for this to work.
A more conformant way would be
void* out_param;
CoCreateInstance(......,...., &out_param);
if (/* co-create succeeded */)
{
ISomeInterface* isi = static_cast<ISomeInterface*>(out_param);
// code using isi here
}
It also means you can't accidentally forget the & operator.
> Internally a lot of hocus-pocus happens and the API ends up calling an
> implementation of a standard IUnknown method called QueryInterface that
> returns a pointer to ISomeInterface like so:
>
> class SomeInterfaceImpl : public ISomeInterface
> {
> void QueryInterface(REFIID riid, void** ppv)
> {
> *ppv = static_cast<ISomeInterface*>(this);
>
> reinterpret_cast<IUnknown*>(*ppv)->AddRef();
> }
>
> // remaining implementations elided for clarity
> };
>
Don't all interfaces inherit IUnknown? If so, the reinterpret_cast
isn't needed at all (or a static_cast could be used just in case AddRef
has been hidden by ISomeInterface).
What I've said only addresses the casts used in a COM-like system. If I
remember correctly, to create real COM classes in C++, the compiler
needs to lay out the objects in a particular way. I've mostly seen
C-style casts in COM code; these work because of the extra requirements
COM places on the compiler's ABI. (You can create COM classes on other
compilers, but COM features won't map to C++ features. For example,
you'd have to create your own v-tables - that's how you write COM
classes in C).
James
If the lvalue which *ppv designates does not have the type
void*, this is undefined behavior. You've lied to the compiler,
and the compiler has the right to punish you for it.
I am aware of implementations where a void* would be larger than
an ISomeInterface*. On such an implementation, Assuming that
ppv was initialized (indirectly) with the reinterpret_cast in
your call above, this line will write not just isi, but also
some bytes behind it. More generally, it will convert the
ISomeInterface* results of the cast to a void*, doing whatever
conversion is relevant for your machine, and then write the
results, as a void*, starting at the address of isi. If (as is
usually the case), void* and ISomeInterface* have the same size
and representation, everything will work fine; if they don't,
you're screwed.
About the only way to write this line in a way that is
guaranteed to work according to the C++ standard would be to
declare the parameter of CoCreateInstance to be a void* (which
eliminates the need of a cast when calling it), and then write:
*static_cast< ISomeInterface** >( ppv ) = this ;
here. Given the current interface, you need two pointers:
void * visi ;
ISomeInterface* isi ;
You then pass &visi to CoCreateInstance, and the above line is
valid. Later, when you want to use isi, you must static_cast
visi to ISomeInterface* and assign it to isi.
> reinterpret_cast<IUnknown*>(*ppv)->AddRef();
Same problem as above. Except that this will actually fail,
even on a PC, if multiple inheritance is involved. Even
assuming that all pointers have the same representation (which
the interface seems to do), the only valid reinterpret_cast of
*ppv is to the type of pointer that was actually stored there,
in this case, an ISomeInterface*.
> }
>
> // remaining implementations elided for clarity
> };
> Have the proper casts been used in the above code? Can you
> explain why reinterpret_cast is needed in places where its
> been used?
What does Microsoft say about it? There are definitly
architectures where this reinterpret_cast will NOT work, and
where it will get you into deep trouble. But what it actually
does is somewhere between implementation defined and undefined
behavior. If Microsoft says that it will work in this case,
then they are defining a legal extension to the language. If
you're using COM, you're using extensions to the language
anyway, and your code won't work on a lot of platforms.
Anything regarding the use of COM is Microsoft's call, not that
of the C++ standard. (The use of such techniques probably means
that COM cannot be implemented on a platform where void* and
SomeClass* have different representations. But presumably, that
is a limitation that Microsoft is willing to take.)
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
I suspect that under time pressure Bjarne has once again written
static_cast when he intended reinterpret_cast.
--
Francis Glassborow ACCU
Author of 'You Can Do It!' and "You Can Program in C++"
see http://www.spellen.org/youcandoit
For project ideas and contributions:
http://www.spellen.org/youcandoit/projects
Okay - that was my original thought as well. I believe we can summarize
now that for a conversion from void* to e.g. int*, you must use
static_cast in order to be portable. This means I'll have to revise
some code - as I also used reinterpret_cast to do that trick (perhaps
inspired by bjarne as I used his books to learn C++ about 12 years
ago).
/Peter
So far so good.
> > By doing static_cast<int*>(pv) when pv is a void*, you
> > assume that a void* is represented in exactly the same ways as an int*.
> > That's by far the most common, but not guaranted.
Another typo. Sorry. I must stop posting while I'm drinking my first
cup of morning coffee (though when else do I find time?):
I meant *reinterpret_cast* in that statement.
Static_casts don't mess with representations, so your are protected
from all representation-related problems (though, as with every cast
there are some potential problems, or you wouldn't need a cast) whereas
reinterpret_casts do.
The point was exactly that *reinterpret_cast* simply reinterprets the
representation so that you are making assumptions about the
representation of both the type you cast from and the type you cast to.
Most people are a bit vague about what is and what is not guaranteed by
the language.
> This statement scares me somewhat, but hopefully its just my
> understanding of english that suffers. static_casting from e.g. void*
> to int* can only be performed when the representation for void* is the
> same as for int* - and there is nothing that guarantees that this will
> work? So - by inference - malloc is not guaranteed to work?
> I surely must misunderstand something - and yet I am the only one to
> react (so far!).
>
> /Peter
>
It was meant to scare you about *reinterpret_cast*. Reinterpret_cast
really is scary.
Use static_cast for malloc and you are as safe as you can ever be with
malloc().
-- Bjarne Stroustrup; http://www.research.att.com/~bs
> Static_casts don't mess with representations, so your are
> protected from all representation-related problems (though, as
> with every cast there are some potential problems, or you
> wouldn't need a cast) whereas reinterpret_casts do.
Still on that first cup of coffee, or have I misunderstood
something? A static_cast is almost required to modify the
representation in certain cases, and it is these modifications
which protect you, and make it work. Whereas on a normal
machine, provided the implementation respects the intent of the
standard, a reinterpret_cast will never change the
representation; the bits in the pointer stay the same,
regardless.
> The point was exactly that *reinterpret_cast* simply
> reinterprets the representation so that you are making
> assumptions about the representation of both the type you cast
> from and the type you cast to. Most people are a bit vague
> about what is and what is not guaranteed by the language.
My impression is that the standard itself is a bit vague about
what is guaranteed by reinterpret_cast. Intentionally, so that
an implementation can make it do something useful (and
"unsurprising to those who know the addressing structure of the
underlying machine") regardless of how bizarre the architecture.
--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
> bjarne wrote:
>
>> Static_casts don't mess with representations, so your are
>> protected from all representation-related problems (though, as
>> with every cast there are some potential problems, or you
>> wouldn't need a cast) whereas reinterpret_casts do.
>
> Still on that first cup of coffee, or have I misunderstood
> something? A static_cast is almost required to modify the
> representation in certain cases, and it is these modifications
> which protect you, and make it work. Whereas on a normal
> machine, provided the implementation respects the intent of the
> standard, a reinterpret_cast will never change the
> representation; the bits in the pointer stay the same,
> regardless.
I read "Static_casts don't mess with representations" as "Static_casts
are not defined in terms of representation", hence on a conceptual
level they have nothing to do with representations, and so you (the
programmer) don't have to worry about them.
A static_cast converts a pointer of some type A pointing to an object
x into a pointer of some type B pointing to the same object x.
It "messes with" the type of the pointer, not with anything else. As
you say, the representation may or may not have to be changed
depending on the situation and the platform, but that is all taken
care of by the compiler, and is "under the hood" of the abstraction
provided by static_cast.
Lourens
I think it's a terminology thing; static_cast is defined
in terms of _values_, not representations, whereas in
most cases reinterpret_cast is implemented as identity
of _representations_ (though, as you said, the standard
doesn't require much of reinterpret_cast, and only alludes
indirectly to it not affecting representations).
-- James
No, not exactly true. If a class has multiple bases, a static cast can
be used to point to any of the base class objects and those will not all
share an address with the complete object.
--
Francis Glassborow ACCU
Author of 'You Can Do It!' and "You Can Program in C++"
see http://www.spellen.org/youcandoit
For project ideas and contributions:
http://www.spellen.org/youcandoit/projects
kanze wrote:
> bjarne wrote:
>
> > Static_casts don't mess with representations, so your are
> > protected from all representation-related problems (though, as
> > with every cast there are some potential problems, or you
> > wouldn't need a cast) whereas reinterpret_casts do.
>
> Still on that first cup of coffee, or have I misunderstood
> something? A static_cast is almost required to modify the
> representation in certain cases, and it is these modifications
> which protect you, and make it work. Whereas on a normal
> machine, provided the implementation respects the intent of the
> standard, a reinterpret_cast will never change the
> representation; the bits in the pointer stay the same,
> regardless.
No, not the coffee (or lack thereof) this time :-)
I guess I should have said that reinterpret_cast "messes with the
meaning of representations" (rather than just "messing with
repesentations"). Static_cast manipulates values according to the type
rules of the language. Reinterpret cast does not. Consider:
int main()
{
int i = 2;
int* pi = &i;
double* dp = reinterpret_cast<double*>(pi);
}
The value of *dp is quite unlikely to be 2.0.
> > The point was exactly that *reinterpret_cast* simply
> > reinterprets the representation so that you are making
> > assumptions about the representation of both the type you cast
> > from and the type you cast to. Most people are a bit vague
> > about what is and what is not guaranteed by the language.
>
> My impression is that the standard itself is a bit vague about
> what is guaranteed by reinterpret_cast. Intentionally, so that
> an implementation can make it do something useful (and
> "unsurprising to those who know the addressing structure of the
> underlying machine") regardless of how bizarre the architecture.
Exactly. Reinterpret_casts is what you use as a last resort, making
assumptions about the machine. You can get into trouble with
static_cast (as with all cast), but reinterpret_cast is provided for
the cases where the compiler can provide essentially no help. An
experienced programmer may till be able to do something useful based on
knowledge of the machine.
-- Bjarne Stroustrup; http://www.research.att.com/~bs
I believe that is consistent with the latter part of the
post to which you were replying, which did say
> the representation may or may not have to be changed
> depending on the situation and the platform, but that
> is all taken care of by the compiler, and is "under
> the hood" of the abstraction provided by static_cast
However, the sentence
> It "messes with" the type of the pointer, not with anything else.
was somewhat unfortunate, and more descriptive of typical
implementations of reinterpret_cast than of static_cast.
-- James
> In article <8e1a2$45027798$8259a2fa$27...@news1.tudelft.nl>, Lourens
> Veen <lou...@rainbowdesert.net> writes
>>A static_cast converts a pointer of some type A pointing to an
>>object x into a pointer of some type B pointing to the same object
>>x. It "messes with" the type of the pointer, not with anything else.
>>As you say, the representation may or may not have to be changed
>>depending on the situation and the platform, but that is all taken
>>care of by the compiler, and is "under the hood" of the abstraction
>>provided by static_cast.
>
> No, not exactly true. If a class has multiple bases, a static cast
> can be used to point to any of the base class objects and those will
> not all share an address with the complete object.
I was considering them all to be the same object, but I'm not sure
whether that is accepted terminology. I think that we need to make a
clear distinction between the value of a variable and its
representation. Consider
struct A { /* ... */ }
struct B { /* ... */ }
struct C : public A, public B { /* ... */ }
int main() {
C c;
C * cp = &c;
A * ap = static_cast<A *>(&c);
B * bp = static_cast<B *>(&c);
}
If I call a member function on c through cp that modifies some
variable in the B subobject of c, then I will be able to see that
change through bp. So, conceptually, I'd argue that all the pointers
point to the same object.
In other words, the result of static_cast<B *>(&c) is a pointer of
type B * that points to c. The only difference between &c and
static_cast<B *>(&c) is that the type of the first expression is C *,
and the type of the second expression is B *. Their _value_ is the
same, namely "pointer to c", their types are different.
Now, most (all?) machines represent pointers as memory addresses. The
exact mapping between a pointer value and its representation depends
on the type of the pointer value and the type of the object it points
to. Depending on the compiler and the machine, bp may well have to
have a different representation than cp, even though they point to
the same object c (e.g. so that it stores the address of the
subobject of c of type B). So, static_cast may have to return a
different representation to ascertain that it returns the same value,
depending on the types it's casting from and to.
The language doesn't say anything about what the representation for &c
looks like however, and it doesn't specify whether static_cast
changes the representation or not. All it guarantees is that the
value remains the same, so that you can safely access c through bp.
Mr. Stroustrup expressed that (somewhat imprecisely) as "Static_casts
don't mess with representations". Perhaps "Static_casts don't deal
with representations, only with types and values. An implementation
of static_cast may change the representation to satisfy the
constraints on the value." would be a better description.
Lourens
Sorry, IMHO that way madness lies. bp is a pointer to some B object or
subobject. The above code initialises it to a subobject of c. But we can
latter change it to point to some other B object. It does not inherently
point into a C object.
At best, after the initialisation above we can say that bp points into
c. Note that I can make the problem even more severe by using a virtual
base class.
--
Francis Glassborow ACCU
Author of 'You Can Do It!' and "You Can Program in C++"
see http://www.spellen.org/youcandoit
For project ideas and contributions:
http://www.spellen.org/youcandoit/projects