Casting to void*, result unexpected.

H

He Shiming

Hi,

I'm having a little bit of trouble regarding pointer casting in my program.
I don't understand why the following two cases produce different results.

Case 1:
IInterface *pInterface = new CImplementation();
pInterface->Method();

Case 2:
void* pInterface = static_cast<void*>(new CImplementation());
static_cast<IInterface*>(pInterface)->Method();

In these two cases, "IInterface" is an interface definition class, it
contains no member variables, only public pure virtual methods are defined.
"CImplementation" is derived from IInterface, and it implements those pure
virtual methods.

Now, if CImplementation is derived only from IInterface, the above two cases
will work the exact same way. However, when CImplementation derives from
other classes, which also contain virtual or pure virtual methods, Case 2
will fail. It'll cause a crash mainly because the ->Method() call isn't
actually calling IInterface::Method(), depending on the actual
inheritance, ->Method() may be pointed at a pure virtual method of another
class. Case 1 still works. Why is that? And more importantly, why is the
above two cases work differently?

Thanks,
 
A

Alf P. Steinbach

* He Shiming:
[unexpected result from void* casting]
...

Why is that?

Disregarding compiler-specific behavior there are only two things
you can do with a void* pointer casted from a type T: compare it wrt.
equality to another such void* pointer, and cast it back to T.

You cast to void* from a type X and then cast back to Y, and that's
Undefined Behavior even if the types X and Y are related.

Perhaps what surprises you is that static_cast can change the pointer
bitpattern (while not changing the conceptual pointer value). But if
you think about it that's how it must be. Consider:

struct A { int x; };
struct B { int y; }
struct X: A, B {};

X* pX = new X;
A* pA = static_cast<A*>( pX );
B* pB = static_cast<B*>( pX );

'pA' points to the beginning of the A subobject, and 'pB' points to
the beginning of the B subobject; these two pointers cannot be equal.
 
H

He Shiming

Disregarding compiler-specific behavior there are only two things
you can do with a void* pointer casted from a type T: compare it wrt.
equality to another such void* pointer, and cast it back to T.
You cast to void* from a type X and then cast back to Y, and that's
Undefined Behavior even if the types X and Y are related.

I'm sorry but what does this mean? I mentioned that it only fails when
multiple inheritance is used. Then if it's an undefined behavior, why does
it work flawlessly, when single inheritance is used?
Consider:

struct A { int x; };
struct B { int y; }
struct X: A, B {};

X* pX = new X;
A* pA = static_cast<A*>( pX );
B* pB = static_cast<B*>( pX );

'pA' points to the beginning of the A subobject, and 'pB' points to
the beginning of the B subobject; these two pointers cannot be equal.

I got your point but this isn't what exactly I wanted.

Consider case 1 in my first post, it worked with the "IInterface" type
definition on the pointer, case 2 fails because something bad happened
during pointer re-casting. The reason why I'm trying to use void* is because
I can't store the type definition in this particular case. In the first
line, the class allocator has no idea about what type should it use.

Regarding the pointer itself. Isn't the pointer just an address? Isn't void*
capable of storing any kind of pointers? If so, why won't casting work?

Thanks,
He Shiming
 
A

Alf P. Steinbach

* "He Shiming said:
I'm sorry but what does this mean? I mentioned that it only fails when
multiple inheritance is used. Then if it's an undefined behavior, why does
it work flawlessly, when single inheritance is used?

Undefined Behavior means the effect can be anything, including whatever you
think it "should" be.

Don't rely on Undefined Behavior.

I got your point but this isn't what exactly I wanted.

Consider case 1 in my first post, it worked with the "IInterface" type
definition on the pointer,

Case 1:
IInterface *pInterface = new CImplementation();
pInterface->Method();


Case 2:
void* pInterface = static_cast<void*>(new CImplementation());
fails because something bad happened during pointer re-casting.

Nope, it fails because it can. It _can_ fail because it's UB. However,
if the type you cast from is the as same the one you cast to it's no longer
UB and won't fail:

void* pInterface = static_cast<IInterface*>( new CImplementation() );
static_cast<IInterface*>( pInterface )->Method();

Not that I recommend such things.

Don't use void*.

The reason why I'm trying to use void* is because
I can't store the type definition in this particular case.

If you can cast the pointer back from void*, then you know the type and
don't have to use void* in the first place.

Simple.

In the first
line, the class allocator has no idea about what type should it use.
?


Regarding the pointer itself. Isn't the pointer just an address?

In general no, but for most pointers, yes.

Isn't void* capable of storing any kind of pointers?

No, but it's capable of storing the kind you're using here.

If so, why won't casting work?

Because you're casting from a type X and back to a different type Y.
 
H

He Shiming

I'm not sure if you are aware of this. But in Microsoft COM APIs, there is
this CoCreateInstance function that does use void* pointer casting. The
function definition went like this:

STDAPI CoCreateInstance(
REFCLSID rclsid,
LPUNKNOWN pUnkOuter,
DWORD dwClsContext,
REFIID riid,
LPVOID * ppv
);

When we use this function, we write something like this:
CComPtr<Interface> spInterface;
CoCreateInstance(blahblahblah...., (void**) &spInteface);

CComPtr is a smart pointer, there is no void* operators defined in it. And
as you can see here, we do cast such pointers. It's pretty much what I'm
trying to do. You can see it initiates the class object to void*, and in the
smart pointer, in order to use it, it'll re-cast it back. If
CoCreateInstance produces a void* pointer, as you said should have undefined
behavior, and may or may not work. How are we going to use COM interfaces
correctly?

Sorry for being Microsoft-specific, but it certainly explains that it should
work.
 
A

Alf P. Steinbach

* He Shiming:
[top-posting]

Please don't top-post in this group. See the FAQ. Corrected.


* He Shiming:
* Alf P. Steinbach:

I'm not sure if you are aware of this. But in Microsoft COM APIs, there is
this CoCreateInstance function that does use void* pointer casting. The
function definition went like this:

STDAPI CoCreateInstance(
REFCLSID rclsid,
LPUNKNOWN pUnkOuter,
DWORD dwClsContext,
REFIID riid,
LPVOID * ppv
);

So? Ask for a pointer to the correct interface type. When the pointer
originates from type IWhatever* you can safely cast it to IWhatever*
(if you're unsure about how to specify types vis-a-vis the Microsoft
COM API, please ask about that in a Microsoft-specific group, not here).

But better, don't use void* pointers yourself.

Have I mentioned that?

...
Sorry for being Microsoft-specific, but it certainly explains that it should
work.

The example bears no relation to your "non-working" example. In that example
you casted from a type X to void*, and then back to a different type Y.
That's not the case in the example you're now giving.
 
H

He Shiming

* He Shiming:
[top-posting]

Please don't top-post in this group. See the FAQ. Corrected.

I don't even know how to "top-post". And how can you correct that? Just
because your Forte hates my Outlook Express doesn't justify my top-posting.
So? Ask for a pointer to the correct interface type. When the pointer
originates from type IWhatever* you can safely cast it to IWhatever*
(if you're unsure about how to specify types vis-a-vis the Microsoft
COM API, please ask about that in a Microsoft-specific group, not here).

I said sorry for being Microsoft-specific already. But if you have no idea
about how CoCreateInstance works, you have no idea about what I'm doing
either. FYI, CoCreateInstance works pretty much (although not exactly) the
same way as my Case 2 example.

"When the pointer originates from type IWhatever* you can safely cast it to
IWhatever*" <-- Well, how did you come up with that? Previously, you said
that as type B is different, one can't cast from type A to B, and then cast
from B back to A. And now you are saying that we can?

Well, given the fact the Case 2 doesn't work as expected, and I do need to
cast the specialized pointer to something general, then later cast it back.
How would you recommend a solution, or your are saying that there's no
solution at all?

Thanks,
He Shiming
 
H

He Shiming

You know, I got it worked.

It's as simple as this:

void* pInterface = static_cast<void*>(static_cast<IInterface*>(new
CImplementation());

That's right, casting twice. And it also proved my point, that void* should
work, the pointer is only an address.
 
A

Alf P. Steinbach

* "He Shiming said:
* He Shiming:
[top-posting]

Please don't top-post in this group. See the FAQ. Corrected.

I don't even know how to "top-post". And how can you correct that? Just
because your Forte hates my Outlook Express doesn't justify my top-posting.

See the FAQ.

I said sorry for being Microsoft-specific already. But if you have no idea
about how CoCreateInstance works, you have no idea about what I'm doing
either. FYI, CoCreateInstance works pretty much (although not exactly) the
same way as my Case 2 example.

Case 2:
void* pInterface = static_cast<void*>(new CImplementation());
static_cast<IInterface*>(pInterface)->Method();

That's _very_ different from the OS API function: this case 2 code casts from
a type X = CImplementation* to void*, and then from void* to a different type
Y = IInterface*.

Is that unclear somehow?

Or do you believe that the OS API mentioned above does exactly that?

It doesn't: it gives you a pointer casted from the type you specify, to void*,
so simply specify the type you'll cast it back to.

"When the pointer originates from type IWhatever* you can safely cast it to
IWhatever*" <-- Well, how did you come up with that? Previously, you said
that as type B is different, one can't cast from type A to B, and then cast
from B back to A.

No, I didn't write that: I told you you get UB if you cast from A* to void*
and from that void* to B*, when A and B are different types.

And now you are saying that we can?

No, not that either.

I suggest you read the earlier postings again, with attention to detail.

Well, given the fact the Case 2 doesn't work as expected, and I do need to
cast the specialized pointer to something general, then later cast it back.
How would you recommend a solution, or your are saying that there's no
solution at all?

See above.

And the earlier replies in this thread.

The implication of those replies: figure out how to use that smart pointer
class you're using so as to avoid having void* pointers around... ;-)
 
H

He Shiming

Okay my bad.

--
He Shiming
Alf P. Steinbach said:
* "He Shiming said:
* He Shiming:
[top-posting]

Please don't top-post in this group. See the FAQ. Corrected.

I don't even know how to "top-post". And how can you correct that? Just
because your Forte hates my Outlook Express doesn't justify my
top-posting.

See the FAQ.

I said sorry for being Microsoft-specific already. But if you have no
idea
about how CoCreateInstance works, you have no idea about what I'm doing
either. FYI, CoCreateInstance works pretty much (although not exactly)
the
same way as my Case 2 example.

Case 2:
void* pInterface = static_cast<void*>(new CImplementation());
static_cast<IInterface*>(pInterface)->Method();

That's _very_ different from the OS API function: this case 2 code casts
from
a type X = CImplementation* to void*, and then from void* to a different
type
Y = IInterface*.

Is that unclear somehow?

Or do you believe that the OS API mentioned above does exactly that?

It doesn't: it gives you a pointer casted from the type you specify, to
void*,
so simply specify the type you'll cast it back to.

"When the pointer originates from type IWhatever* you can safely cast it
to
IWhatever*" <-- Well, how did you come up with that? Previously, you said
that as type B is different, one can't cast from type A to B, and then
cast
from B back to A.

No, I didn't write that: I told you you get UB if you cast from A* to
void*
and from that void* to B*, when A and B are different types.

And now you are saying that we can?

No, not that either.

I suggest you read the earlier postings again, with attention to detail.

Well, given the fact the Case 2 doesn't work as expected, and I do need
to
cast the specialized pointer to something general, then later cast it
back.
How would you recommend a solution, or your are saying that there's no
solution at all?

See above.

And the earlier replies in this thread.

The implication of those replies: figure out how to use that smart pointer
class you're using so as to avoid having void* pointers around... ;-)

--
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?
 
A

Alf P. Steinbach

* "He Shiming said:
You know, I got it worked.

It's as simple as this:

void* pInterface = static_cast<void*>(static_cast<IInterface*>(new
CImplementation());

That's good; except for the unnecessary cast (figure it out ;-)) that's
the code I gave you in my second posting.

That's right, casting twice. And it also proved my point, that void* should
work, the pointer is only an address.

Unfortunately, that's not-so-good: it seems you're misunderstanding why
it works.

Here's why it works: you're now casting from a type T to void*, and back
to the same type T (where T = IInterface*).
 
B

ben

He Shiming said:
I'm not sure if you are aware of this. But in Microsoft COM APIs, there is
this CoCreateInstance function that does use void* pointer casting. The
function definition went like this:

STDAPI CoCreateInstance(
REFCLSID rclsid,
LPUNKNOWN pUnkOuter,
DWORD dwClsContext,
REFIID riid,
LPVOID * ppv
);

This relies on the fact that ALL COM interface must inherit from the
IUnknown. So no matter which interface is pointed to by whatever returned
from CoCreateInstance, it starts with IUnknow memory layout. It is from
IUnknown::QueryInterface that you start to do more specific interface casts.
That is, the three methods in IUnknown is guaranteed to work correctly;
calls to other methods are still undefined.

ben
 
B

ben

If you are maintaining a single inheritance tree, the code pretty much works
because all subobjects begins with the same memory address in most cases.

But if you start to get into multiple inheritance things are a little
tricky, because subobjects no longer can share the same beginning address,
so the cast operator must do work to correctly offset the addresses from
type to type. This can only be done with some type information available to
the compiler. The void* doesn't have any type information for the compiler
to correctly offset the memory addresses.

Stan Lipman's "Inside the C++ Object Model" has an in depth explanation
throughout the book.

ben
 
R

Ron Natalie

ben said:
This relies on the fact that ALL COM interface must inherit from the
IUnknown. So no matter which interface is pointed to by whatever returned
from CoCreateInstance, it starts with IUnknow memory layout. It is from
IUnknown::QueryInterface that you start to do more specific interface casts.
That is, the three methods in IUnknown is guaranteed to work correctly;
calls to other methods are still undefined.
This is really no different than the preivous example where he made a
cast to the base class before stuffing it in void. Frankly, if
Microsoft had a clue these things would use IUnknown* rather than
void*.

-Ron

IUnkonwn is the all-day sucker of COM interfaces.
 
R

Rolf Magnus

He said:
* He Shiming:
[top-posting]

Please don't top-post in this group. See the FAQ. Corrected.

I don't even know how to "top-post".

Funny, considering that you're doing it all the time (except in this
posting) :)
And how can you correct that?

See the FAQ.
Just because your Forte hates my Outlook Express doesn't justify my
top-posting.

Yup, right. Whatever other people's newsreaders think about yours, you are
not supposed to be top-posting, so don't do it.
 
B

ben

This is really no different than the preivous example where he made a
cast to the base class before stuffing it in void. Frankly, if
Microsoft had a clue these things would use IUnknown* rather than
void*.

Using IUnknown* to return a generic interface pointer is sweet, but because
QueryInterface mandates that every call that requests the same interface
must return the same address, we might get one IUnknown* from
CoCreateInterace and get another IUnknow* from QueryInterface and we might
accidentally assume that these are two distinct objects because they have
two different IUnknown*....

So Microsoft has a reason of preferring void** over IUnknown**

ben
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,297
Messages
2,571,536
Members
48,282
Latest member
Xyprime

Latest Threads

Top