Friday, April 22, 2011

Complicated declarations & definitions

As an aside, once you figure out how the C and C++ declaration syntax works you can create much more complicated items. For instance:

//: C03:ComplicatedDefinitions.cpp  
/* 1. */     void * (*(*fp1)(int))[10];  
/* 2. */     float (*(*fp2)(int,int,float))(int);  
/* 3. */     typedef double (*(*(*fp3)())[10])();              
fp3 a;  
/* 4. */     int (*(*f4())[10])();  int main() {} ///:~

Walk through each one and use the right-left guideline to figure it out. Number 1 says “fp1 is a pointer to a function that takes an integer argument and returns a pointer to an array of 10 void pointers.”

Number 2 says “fp2 is a pointer to a function that takes three arguments (int, int, and float) and returns a pointer to a function that takes an integer argument and returns a float.”

If you are creating a lot of complicated definitions, you might want to use a typedef. Number 3 shows how a typedef saves typing the complicated description every time. It says “An fp3 is a pointer to a function that takes no arguments and returns a pointer to an array of 10 pointers to functions that take no arguments and return doubles.” Then it says “a is one of these fp3 types.” typedef is generally useful for building complicated descriptions from simple ones.

Number 4 is a function declaration instead of a variable definition. It says “f4 is a function that returns a pointer to an array of 10 pointers to functions that return integers.”

You will rarely if ever need such co

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; }