views:

98

answers:

5

I need to write code for a callback function (it will be called from within ATL, but that's not really important):

HRESULT callback( void* myObjectVoid )
{
    if( myObjectVoid == 0 ) {
       return E_POINTER;
    }
    CMyClass* myObject = static_cast<CMyClass*>( myObjectVoid );
    return myObject->CallMethod();
}

here the void* is guaranteed to be a pointer to CMyClass, so static_cast is legal. My concern is the code must be as portable (to newer versions of Visual C++ at least) as possible. So to be super-paranoic I'm inclined to check the CMyClass* pointer as well - I mean what if it turns out to be null?

    if( myObjectVoid == 0 ) {
       return E_POINTER;
    }
    CMyClass* myObject = static_cast<CMyClass*>( myObjectVoid );
    if( myObject == 0 ) {
       return E_POINTER;
    }

Is the second check reasonable? Is it possible for static_cast to turn a non-null pointer into a null pointer?

A: 

The only change static_cast should make to a pointer is for word-alignment. So, in theory, of myObjectVoid pointed to the last byte in memory, it's possible that it might be 'aligned-up" to 0, but I don't see that as a realistic concern.

James Curran
If this was possible, wouldn't it be a huge problem ? I always thought that 0 was the only value that a valid pointer couldn't have.
ereOn
The real question is would the address be rounded up or down or is it implementation defined?
Martin York
+1  A: 

No. If the pointer refers to a valid object, and the conversion is valid, then the result will also refer to a valid object, so it won't be null. If either is invalid, then the code is incorrect and the result is undefined. So the only way for valid usage to give a null result is to start with null.

In the specific case of converting between object pointers and void pointers, the standard has this to say (5.2.9/10):

A value of type "pointer to object" converted to "pointer to void" and back to the original pointer type will have its original value.

and this (4.10/3)

The result of converting a "pointer to T" to a "pointer to void" points to the start of the storage location where the object of type T resides

so the original and final object pointers will be the same, and the void pointer will be null if and only if the object pointers are.

Mike Seymour
+2  A: 

static_cast can change the pointer value, if you cast between object parts on different offsets:

class A{ int x; }; class B{ int y; };
class C : A,B {};

C *c=new C(); 

B *b=c; 
// The B part comes after the A part in C. Pointer adjusted

C *c2=static_cast<C*>(b); 
// Pointer gets adjusted back, points to the beginning of the C part

However, "The null pointer value (4.10) is converted to the null pointer value of the destination type." (5.2.9-8), i.e. if c is NULL, then b is also NULL (and not adjusted) and thus c2 is set to NULL. The whole thing means: if static casting a non-NULL myObjectVoid yields NULL, then the value of myObjectVoid was obtained by circumventing the type system somehow. And it means, that the compiler might throw your second check away because "it can't happen anyway".

Luther Blissett
A: 

No, the second check is not reasonable. It is not possible for static_cast to turn a non-null pointer into a null pointer. The only thing static_cast may change about the supplied value in your case (being a pointer) is to adjust the address. Otherwise it's strictly concerned with advising the rest of the compiler's type analysis that the result of the expression should be treated as the target type. For a pointer, that means that dereferencing finds the right address within the target object, and that increment and decrement strides are appropriate to the type's size.

seh
Well there you go. In non real-world scenarios, passing in a carefully crafted pointer to static_cast could cause it to be adjusted by an offset that makes it NULL. Of course, that's never going to happen in real life.
Kennet Belenky
+2  A: 

I suspect you're asking if static_cast can return NULL like dynamic_cast does when the cast is invalid. In that case the answer is no.
However ...

class A { int i; };
class B { int j; };
class C : public A, public B { int k; };

int main()
{
   C* c1 = NULL;
   B* b = static_cast<B*>(c1);
   C* c2 = static_cast<C*>(b);
}

c1 is obviously NULL.
b is not NULL, it is 0x00000004.
c2 is NULL. That is what the reader would expect since it started out as c1 which is NULL. However, if the reader didn't know where b came from it might be surprising that a non-NULL pointer (0x4) resulted in a NULL pointer after a static_cast.

caspin
This is not correct. The C++ standard says, that static_cast transforms NULL pointer of the source type into NULL pointer of the destination type. A conforming compiler would set b to NULL. This is important, because it means we don't have to bother with "almost-NULL" pointers.
Luther Blissett
@Luther Blissett: Yes, in VC++ the emitted assembly contains checks for whether the source pointer is null - specifically for this.
sharptooth