Thursday, May 3, 2012

Useful C reinterpret_cast-like #define's

While starting to write a very large chunk of code with lots of casting I created the following set of #define's which I think anyone trying to perform C++ reinterpret_cast-style functionality will find useful.

//These just set up the regular types with useful mnemonics:

typedef char int8;
typedef char* int8p;
typedef unsigned char uint8;
typedef unsigned char * uint8p;
typedef short int16;
typedef short* int16p;
typedef unsigned short uint16;
typedef unsigned short * uint16p;
typedef int int32;
typedef int* int32p;
typedef unsigned int uint32;
typedef unsigned int * uint32p;
typedef long long int64;
typedef long long* int64p;
typedef unsigned long long uint64;
typedef unsigned long long * uint64p;
typedef float f32;
typedef float* f32p;
typedef double f64;
typedef double* f64p;

//These set up easy casting:

#define cast_uint8 *(uint8p)(void*)&
#define cast_int8 *(int8p)(void*)&
#define cast_uint16 *(uint16p)(void*)&
#define cast_int16 *(int16p)(void*)&
#define cast_uint32 *(uint32p)(void*)&
#define cast_int32 *(int32p)(void*)&
#define cast_uint64 *(uint64p)(void*)&
#define cast_int64 *(int64p)(void*)&
#define cast_f32 *(f32p)(void*)&
#define cast_f64 *(f64p)(void*)&

The above code lets you do things like this (note that 0x4a66666 is hexadecimal of the floating point value "5.2" in IEE 754 format) :

long long a = 0x40a66666;
long long b = 0x40a66666;
long long result;
cast_f64 result = cast_f32 a + cast_f32 b;
printf("result %f\n",result);

The result of the above code is the output "10.4".  We can see that the one line of "cast_f64 result = cast_f32 a + cast_f32 b;" does a lot quite concisely.  We converted from 32-bit integer to single precision floating point using 64-bit integer registers for data storage - and upconvert the result to double precision floating point (This is strangely different from the SystemCrafter synthesizer  which upconverts first using the destination type... probably in an attempt to be helpful). Wikipedia notes that the above code works thanks to x86 using little endian for both integer and floating point, but this is not the case on all architectures so the above code doesn't work everywhere. As with any kind of reinterpret casts, jury rigged C or otherwise, you have to know your hardware.

Note that you have be careful with #define's like this, because if you accidentally type something like:

cast_f64 result = 5.2 cast_f32 b;

you will get a confusing error:
'*' : illegal, right operand has type 'f32p'

This is because the compiler interprets the "*" in our #define as multiplication instead of the unary dereference operator.  These types of errors are why C++ uses templates etc... but one of the greatest programmers I have ever had the pleasure of knowing, Brandyn Webb, helped me appreciate the simplicity of C over the syntactic sugar of C++.  I appreciate C++ and historically have used Python and C++. (It is interesting to note that analysis of Cognitive hardware designs indicate it will do exceedingly well on C++, possibly even better than on C).  Compatibility has become more important in my current role and C is one of the most compatible (i.e. available on arbitrary hardware) programming languages of all time - hence my current attention to it.

Happy programming!  Like riding without training wheels, reinterpret casting is fun but dangerous!

1 comment:

  1. Probably you would be interested about some things in this wiki and this section of it: