From 82f51bc3d45f4c0aa047446eebdcd43d3e213590 Mon Sep 17 00:00:00 2001 From: Glenn Maynard Date: Sat, 7 Oct 2006 08:17:15 +0000 Subject: [PATCH] more generic type-punning helper --- stepmania/src/EnumHelper.h | 38 ------------------ stepmania/src/RageDisplay_OGL_Helpers.cpp | 2 +- stepmania/src/RageUtil.h | 49 +++++++++++++++++++++++ 3 files changed, 50 insertions(+), 39 deletions(-) diff --git a/stepmania/src/EnumHelper.h b/stepmania/src/EnumHelper.h index 8a3af3fd8c..c091975476 100644 --- a/stepmania/src/EnumHelper.h +++ b/stepmania/src/EnumHelper.h @@ -9,44 +9,6 @@ extern "C" #include "lua-5.1/src/lua.h" } -/* - * Safely add an integer to an enum. - * - * This is illegal: - * - * ((int&)val) += iAmt; - * - * It breaks aliasing rules; the compiler is allowed to assume that "val" doesn't - * change (unless it's declared volatile), and in some cases, you'll end up getting - * old values for "val" following the add. (What's probably really happening is - * that the memory location is being added to, but the value is stored in a register, - * and breaking aliasing rules means the compiler doesn't know that the register - * value is invalid.) - * - * Always do these conversions through a union. - */ -template -static inline void enum_add( T &val, int iAmt ) -{ - union conv - { - T value; - int i; - }; - - conv c; - c.value = val; - c.i += iAmt; - val = c.value; -} - -template -static inline T enum_add2( T val, int iAmt ) -{ - enum_add( val, iAmt ); - return val; -} - #define FOREACH_ENUM_N( e, max, var ) for( e var=(e)0; var( var, +1 ) ) #define FOREACH_ENUM( e, var ) for( e var=(e)0; var( var, +1 ) ) #define FOREACH_ENUM2 FOREACH_ENUM diff --git a/stepmania/src/RageDisplay_OGL_Helpers.cpp b/stepmania/src/RageDisplay_OGL_Helpers.cpp index 18a14bac2a..bd67f26c3b 100644 --- a/stepmania/src/RageDisplay_OGL_Helpers.cpp +++ b/stepmania/src/RageDisplay_OGL_Helpers.cpp @@ -55,7 +55,7 @@ bool GLExt_t::HasExtension( const RString &sExt ) const return g_glExts.find(sExt) != g_glExts.end(); } -#define F(n) { (void **) &GLExt.n , #n } +#define F(n) { &CastSimilarTypes(GLExt.n), #n } struct func_t { diff --git a/stepmania/src/RageUtil.h b/stepmania/src/RageUtil.h index e9272e5f40..2ef3391d30 100644 --- a/stepmania/src/RageUtil.h +++ b/stepmania/src/RageUtil.h @@ -115,6 +115,55 @@ inline void RemoveIf( Container& c, Predicate p ) c.erase( remove_if(c.begin(), c.end(), p), c.end() ); } +/* Cast between types through a union, to avoid type-punning problems in gcc. */ +template +TO &CastSimilarTypes( FROM &val ) +{ + union conv_union + { + TO to; + FROM from; + }; + + conv_union &u = (conv_union &) val; + return u.to; +} + +template +int &UnionCast( T &e ) +{ + return CastSimilarTypes(e); +} + +/* + * Safely add an integer to an enum. + * + * This is illegal: + * + * ((int&)val) += iAmt; + * + * It breaks aliasing rules; the compiler is allowed to assume that "val" doesn't + * change (unless it's declared volatile), and in some cases, you'll end up getting + * old values for "val" following the add. (What's probably really happening is + * that the memory location is being added to, but the value is stored in a register, + * and breaking aliasing rules means the compiler doesn't know that the register + * value is invalid.) + * + * Always do these conversions through a union. + */ +template +static inline void enum_add( T &val, int iAmt ) +{ + UnionCast( val ) += iAmt; +} + +template +static inline T enum_add2( T val, int iAmt ) +{ + enum_add( val, iAmt ); + return val; +} + /* * We only have unsigned swaps; byte swapping a signed value doesn't make sense.