Peeter Joot's (OLD) Blog.

Math, physics, perl, and programming obscurity.

casting away volatile with const_cast: a subtlety.

Posted by peeterjoot on October 27, 2011

I broke our windows platform build yesterday, with a change to a macro that I didn’t realize was only used in our windows code. The macro was of the following form:

void goo(const void *) ;
#define foo(p) goo((const void *)p)

Do you ask why there’s a cast here? Shouldn’t that be redundant since types can be converted without cast to void *? That’s true most of the cases, but this particular macro is used with types that may also have a volatile attribute. I realize now that this must have been the point of the cast in the macro. I’d changed the macro to force a bit of stricter type checking:

#define foo(p) goo( const_cast< T*>(p) )

The const_cast can be a handy beastie, and will force a compilation error if the type is not convertible to T* once const or volatile attributes are stripped off. Example:

volatile int x ;
const int y = 3 ;
short z ;
goo( const_cast<int *>(&x) ) ; // compiles.
goo( const_cast<int *>(&y) ) ; // compiles.
goo( const_cast<int *>(&z) ) ; // compilation error

The last line will produce a nice error message of the form:

error: a const_cast can only adjust type qualifiers; it cannot change the underlying type

From what I’d read I’d thought that const_cast stripped off any const and volatile attribute, but my build break shows that this is not quite true. Here’s an example that demonstrates:

#include <stdio.h>

void foo( void * y )
   printf( "%d\n", *(int *)y ) ;

typedef volatile int T ;

int main()
   T x ;

   foo( const_cast< int* >(&x) ) ; // compiles.
   foo( const_cast< T* >(&x) ) ;   // compilation error

   return 0 ;

I end up with an error message like:

t.C(15): error: argument of type "T={volatile int} *" is incompatible with parameter of type "void *"
     foo( const_cast(&x) ) ;   // compilation error

If your type cast argument in the const_cast also includes a volatile attribute, then the const_cast appears to leave the type unchanged, retaining the volatile attribute. This makes a certain sense. It also appears to be a cross platform behavior. I’d changed a number of macros in this way, and found some coding errors by doing so in my other platform builds. The macro in question that I broke the build with was unluckily only called in windows code, and also the only one where the type happened to have volatile in it (all the rest of the types were structures, and in those structures were members that had volatile on specific parts of them).

In retrospect I wish that I’d coded this one type as a struct with a volatile member, then pointers to this struct type could be passed around, cast to void * in the tracing and problem determination code if desired. That would leave the volatile detail to the innards of the hardware oriented code that required it.

4 Responses to “casting away volatile with const_cast: a subtlety.”

  1. ip said has the following statement: “If a pointer’s type is void *, the pointer can point to any variable that is not declared with the const or volatile keyword.”. Probably based on the standard … Line 15 in the example converts to a type, which contains “volatile”. In the end, foo is called with “volatile void *”, which fails because of the mentioned statement.

    int aa = 6;
    foo( const_cast< const int * >( &aa ) );

    Fails the same way, “void *” is not compatible with modifiers.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: