-
-
Save hostilefork/8be86896be36be0d0711 to your computer and use it in GitHub Desktop.
C Casting Macros for Subversive C++ Programmers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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