Some people (myself included) dislike the syntax of the C++ const_cast<>
operator, because;
- It seems misnamed, because it removes
const
.
- It seems to violate DRY, because it requires a redundant type arg.
But I am wrong: it is not misnamed, since it can also add const
and/or volatile
"cv" qualifiers, and it only partially violates DRY, since the compiler will catch any errors. So I dislike it slightly less and use it: it is safer than the C-style cast.
Using gcc's typeof
, you can have almost the same type safety in C.
The following C code sample gives a CONST_CAST(T, x)
macro, and illustrates its use:
#define REMOVE_QUALIFIER(cv, T, x) /* this macro evaluates its args only once */ \
__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), cv T), ((T)(x)), \
(void)0)
#define ADD_QUALIFIER(cv, T, x) /* this macro evaluates its args only once */ \
__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), T), ((cv T)(x)), \
(void)0)
#ifdef __GNUC__
#define CONST_CAST(T, x) REMOVE_QUALIFIER(const, T, x) // "misnamed"
#else
#define CONST_CAST(T, x) ((T)(x)) // fallback to standard C cast
#endif
void foo(void);
void foo(void) {
const int *a = 0;
const float *x = 0;
int *b = a; // warning
int *c = (int *)a; // no warning, unsafe standard cast
int *d = (int *)x; // no warning, and likely wrong
int *e = CONST_CAST(int *, a); // ok
int *f = CONST_CAST(int *, x); // error
unsigned *g = CONST_CAST(unsigned *, a); // error
const int **h = &b; // warning
const int **i = ADD_QUALIFIER(const, int **, &b); // ok
const int **j = ADD_QUALIFIER(const, int **, &x); // error
}
This technique can also be used to change the signedness of a type, reminiscent of C++'s std::make_signed
and std::make_unsigned
, or Boost traits. For example:
#define MAKE_UNSIGNED(T, x) ADD_QUALIFIER(unsigned, T, x) // T usually char*