Skip to content

Instantly share code, notes, and snippets.

@hostilefork
Last active August 29, 2015 14:24
Show Gist options
  • Save hostilefork/8be86896be36be0d0711 to your computer and use it in GitHub Desktop.
Save hostilefork/8be86896be36be0d0711 to your computer and use it in GitHub Desktop.
C Casting Macros for Subversive C++ Programmers
#if !defined(__cplusplus)
/* These macros are easier-to-spot variants of the traditional C cast.
* The 'w_cast' is for rare moments you need [W]rite access on a const
* type :-/ while plain 'cast' is intended for all other purposes:
*/
#define cast(t,v) ((t)(v))
#define w_cast(t,v) ((t)(v))
/*
* Q: Why divide roles? A: Frequently, input to cast is const but you
* "just forget" to include const in the result type, gaining mutable
* access. Stray writes to that can cause even time-traveling bugs, with
* effects *before* that write is made...due to "undefined behavior".
*/
#elif __cplusplus < 201103L
/* Well-intentioned macros aside, C has no way to enforce that you can't
* cast away a const without w_cast. Yet C++ builds can check just that:
*/
#define cast(t,v) reinterpret_cast<t>(v)
#define w_cast(t,v) const_cast<t>(v)
#else
/* __cplusplus >= 201103L has C++11's type_traits. We can catch if you
* try to remove const from a non-const expression, or make No-Op casts.
* (When cleaning those out, look closely for misunderstandings/bugs):
*/
#include <type_traits>
#define cast(t,v) \
([]{static_assert(!std::is_same<t, decltype(v)>::value, \
"useless cast() - source expression already has target type" \
);}, reinterpret_cast<t>(v))
#define w_cast(t, v) \
([]{static_assert(std::is_const<decltype(v)>::value, \
"suspicious w_cast() - can't get more write access on a mutable" \
); static_assert(!std::is_const<t>::value, \
"invalid w_cast() - requested a const type for output result" \
)}, const_cast<t>(v))
#endif
#ifdef NDEBUG
/* These [S]tring and [B]inary casts are for "flips" between a 'char *'
* and 'unsigned char *' (or 'const char *' and 'const unsigned char *').
* Being single-arity with no type passed in, they are succinct to use:
*/
#define s_cast(b) ((char *), (b))
#define cs_cast(b) ((const char *), (b))
#define b_cast(s) ((unsigned char *), (s))
#define cb_cast(s) ((const unsigned char *), (s))
/*
* Assuming '-Wpointer-sign' enabled, this is very useful. 'char *' can
* be used with string functions like strlen(). Then 'unsigned char *'
* can be saved for things you shouldn't _accidentally_ pass to functions
* like strlen(). (One GREAT example: encoded UTF-8 byte strings.)
*/
#else
/* We want to ensure the input type is what we thought we were flipping,
* and not a 'float*' or otherwise. Instead of C++ type_traits, 4 simple
* functions check in *both* C and C++ (here only during Debug builds):
*/
char *s_cast(unsigned char *b);
/* { return cast(char *, b); } */
const char *cs_cast(const unsigned char *b);
/* { return cast(const char *, b); } */
unsigned char *b_cast(char *s);
/* { return cast(unsigned char *, s); } */
const unsigned char *cb_cast(const char *s);
/* { return cast(const char *, s); } */
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment