Friday, April 22, 2011

When to use reinterpret_cast?

The C++ standard guarantees the following:

static_casting a pointer to and from void* preserves the address. That is, in the following, a, b and c all point to the same address:

int* a = new int();
void* b = static_cast<void*>(a);
int* c = static_cast<int*>(b);

reinterpret_cast only guarantees that if you cast a pointer to a different type, and thenreinterpret_cast it back to the original type, you get the original value. So in the following:

int* a = new int();
void* b = reinterpret_cast<void*>(a);
int* c = reinterpret_cast<int*>(b);

a and c contain the same value, but the value of b is unspecified. (in practice it will typically contain the same address as a and c, but that's not specified in the standard, and it may not be true on machines with more complex memory systems.

For casting to and from void*, static_cast should be preferred.

One case when reinterpret_cast is necessary is when interfacing with opaque data types. This occurs frequently in vendor APIs over which the programmer has no control. Here's a contrived example where a vendor provides an API for storing and retrieving arbitrary global data:

// vendor.hpp
typedef struct _Opaque * VendorGlobalUserData;
void VendorSetUserData( VendorGlobalUserData p );
VendorGlobalUserData VendorGetUserData();

To use this API, the programmer must cast their data to VendorGlobalUserData and back again.static_cast won't work, you must use reinterpret_cast:

// main.cpp
#include "vendor.hpp"
#include
using namespace std;

struct MyUserData {
MyUserData() : m( 42 ) {}
int m;
};

int main() {
MyUserData u;

// store global data
VendorGlobalUserData d1;
// d1 = &u; // compile error
// d1 = static_cast< VendorGlobalUserData >( &u ); // compile error
d1
= reinterpret_cast< VendorGlobalUserData >( &u ); // ok
VendorSetUserData( d1 );

// do other stuff...

// retrieve global data
VendorGlobalUserData d2 = VendorGetUserData();
MyUserData * p = 0;
// p = d2; // compile error
// p = static_cast< MyUserData * >( d2 ); // compile error
p
= reinterpret_cast< MyUserData * >( d2 ); // ok

if ( p ) { cout << p->m << endl; }
return 0;
}

Below is a contrived implementation of the sample API:

// vendor.cpp
static VendorGlobalUserData g = 0;
void VendorSetUserData( VendorGlobalUserData p ) { g = p; }
VendorGlobalUserData VendorGetUserData() { return g; }

No comments: