ML Reference
|
00001 // **InsertLicense** code 00002 //------------------------------------------------------------------------- 00004 00009 //------------------------------------------------------------------------- 00010 #ifndef __mlRangeCasts_H 00011 #define __mlRangeCasts_H 00012 00013 #ifndef __mlTypeDefs_H 00014 #include "mlTypeDefs.h" 00015 #endif 00016 #ifndef __mlErrorMacros_H 00017 #include "mlErrorMacros.h" 00018 #endif 00019 #ifndef __mlErrorOutput_H 00020 #include "mlErrorOutput.h" 00021 #endif 00022 00023 #include "mlSystemWarningsDisable.h" 00024 #include <limits.h> 00025 #include "mlSystemWarningsRestore.h" 00026 00028 //#define _ML_COMPILE_RANGE_CASTS_ONLY_AS_CASTS 00029 00031 #define _ML_COMPILE_RANGE_CASTS_WITH_CHECKS 00032 00034 #define _ML_RANGE_ERROR_TYPE ML_PRINT_FATAL_ERROR 00035 00037 #define _ML_OUTPUT_RANGE_ERROR \ 00038 _ML_RANGE_ERROR_TYPE("_MLRangeCheck::checked_cast", ML_OUT_OF_RANGE, "Invalid numeric cast (range check failed)."); 00039 00040 //= _ml_numeric_limits ========================================================================== 00041 00043 // The mininums are always returned with the equivalent signed type, while the 00044 // maximums are returned with the equivalent unsigned type. This is needed so 00045 // that comparisons of mins or maxs of types with the same size but different 00046 // signed-ness don't fail (the compiler would cast to one of the types). 00047 template <typename T> 00048 struct _ml_numeric_limits; 00049 00050 template<> 00051 struct _ml_numeric_limits<unsigned char> 00052 { 00053 static const signed char MinValue = 0; 00054 static const unsigned char MaxValue = UCHAR_MAX; 00055 static const bool isFloat = false; 00056 }; 00057 00058 template<> 00059 struct _ml_numeric_limits<signed char> 00060 { 00061 static const signed char MinValue = SCHAR_MIN; 00062 static const unsigned char MaxValue = SCHAR_MAX; 00063 static const bool isFloat = false; 00064 }; 00065 00066 template<> 00067 struct _ml_numeric_limits<char> 00068 { 00069 static const signed char MinValue = CHAR_MIN; 00070 static const unsigned char MaxValue = CHAR_MAX; 00071 static const bool isFloat = false; 00072 }; 00073 00074 template<> 00075 struct _ml_numeric_limits<unsigned short> 00076 { 00077 static const signed short MinValue = 0; 00078 static const unsigned short MaxValue = USHRT_MAX; 00079 static const bool isFloat = false; 00080 }; 00081 00082 template<> 00083 struct _ml_numeric_limits<short> 00084 { 00085 static const short MinValue = SHRT_MIN; 00086 static const unsigned short MaxValue = SHRT_MAX; 00087 static const bool isFloat = false; 00088 }; 00089 00090 template<> 00091 struct _ml_numeric_limits<unsigned int> 00092 { 00093 static const int MinValue = 0; 00094 static const unsigned int MaxValue = UINT_MAX; 00095 static const bool isFloat = false; 00096 }; 00097 00098 template<> 00099 struct _ml_numeric_limits<int> 00100 { 00101 static const int MinValue = INT_MIN; 00102 static const unsigned int MaxValue = INT_MAX; 00103 static const bool isFloat = false; 00104 }; 00105 00106 template<> 00107 struct _ml_numeric_limits<unsigned long> 00108 { 00109 static const long MinValue = 0; 00110 static const unsigned long MaxValue = ULONG_MAX; 00111 static const bool isFloat = false; 00112 }; 00113 00114 template<> 00115 struct _ml_numeric_limits<long> 00116 { 00117 static const long MinValue = LONG_MIN; 00118 static const unsigned long MaxValue = LONG_MAX; 00119 static const bool isFloat = false; 00120 }; 00121 00122 template<> 00123 struct _ml_numeric_limits<unsigned long long> 00124 { 00125 static const long long MinValue = 0; 00126 static const unsigned long long MaxValue = ULLONG_MAX; 00127 static const bool isFloat = false; 00128 }; 00129 00130 template<> 00131 struct _ml_numeric_limits<long long> 00132 { 00133 static const long long MinValue = LLONG_MIN; 00134 static const unsigned long long MaxValue = LLONG_MAX; 00135 static const bool isFloat = false; 00136 }; 00137 00138 template<> 00139 struct _ml_numeric_limits<float> 00140 { 00141 // float values can't be compile time constants: 00142 // static const double MinValue = -FLT_MAX; 00143 // static const double MaxValue = FLT_MAX; 00144 static const bool isFloat = true; 00145 }; 00146 00147 template<> 00148 struct _ml_numeric_limits<double> 00149 { 00150 // float values can't be compile time constants: 00151 // static const double MinValue = -DBL_MAX; 00152 // static const double MaxValue = DBL_MAX; 00153 static const bool isFloat = true; 00154 }; 00155 00156 00158 #if defined(_ML_COMPILE_RANGE_CASTS_WITH_CHECKS) 00159 00160 //= _MLIntegerRangeCheck ======================================================================== 00161 00164 template <bool CheckLowerBounds, bool CheckUpperBounds, typename Target, typename Source> 00165 struct _MLIntegerRangeCheck; 00166 00167 template <typename Target, typename Source> 00168 struct _MLIntegerRangeCheck<true, true, Target, Source> 00169 { 00170 static inline Target checked_cast(Source srcVal) 00171 { 00172 if ((srcVal < static_cast<Source>(_ml_numeric_limits<Target>::MinValue)) || 00173 (srcVal > static_cast<Source>(_ml_numeric_limits<Target>::MaxValue))) 00174 { 00175 _ML_OUTPUT_RANGE_ERROR 00176 } 00177 return static_cast<Target>(srcVal); 00178 } 00179 }; 00180 00181 template <typename Target, typename Source> 00182 struct _MLIntegerRangeCheck<false, true, Target, Source> 00183 { 00184 static inline Target checked_cast(Source srcVal) 00185 { 00186 if (srcVal > static_cast<Source>(_ml_numeric_limits<Target>::MaxValue)) 00187 { 00188 _ML_OUTPUT_RANGE_ERROR 00189 } 00190 return static_cast<Target>(srcVal); 00191 } 00192 }; 00193 00194 template <typename Target, typename Source> 00195 struct _MLIntegerRangeCheck<true, false, Target, Source> 00196 { 00197 static inline Target checked_cast(Source srcVal) 00198 { 00199 if (srcVal < static_cast<Source>(_ml_numeric_limits<Target>::MinValue)) 00200 { 00201 _ML_OUTPUT_RANGE_ERROR 00202 } 00203 return static_cast<Target>(srcVal); 00204 } 00205 }; 00206 00207 template <typename Target, typename Source> 00208 struct _MLIntegerRangeCheck<false, false, Target, Source> 00209 { 00210 static inline Target checked_cast(Source srcVal) 00211 { 00212 return static_cast<Target>(srcVal); 00213 } 00214 }; 00215 00216 //= _MLFloatRangeCheck ========================================================================== 00217 00218 // The default float range check does nothing 00219 template <typename Target, typename Source> 00220 struct _MLFloatRangeCheck 00221 { 00222 static inline Target checked_cast(Source arg) 00223 { 00224 return static_cast<Target>(arg); 00225 } 00226 }; 00227 00228 // Only when casting from double to float a range check is applied 00229 template<> 00230 struct _MLFloatRangeCheck<float, double> 00231 { 00232 static inline float checked_cast(double arg) 00233 { 00234 if ((arg < static_cast<double>(-FLT_MAX)) || 00235 (arg > static_cast<double>(FLT_MAX))) 00236 { 00237 _ML_OUTPUT_RANGE_ERROR 00238 } 00239 return static_cast<float>(arg); 00240 } 00241 }; 00242 00243 //= _MLRangeCheck =============================================================================== 00244 00247 template <bool isTargetFloat, bool isSourceFloat, typename Target, typename Source> 00248 struct _MLRangeCheck; 00249 00250 // casting integer to integer does (only the necessary) range checks 00251 template <typename Target, typename Source> 00252 struct _MLRangeCheck<false, false, Target, Source> 00253 { 00254 static inline Target checked_cast(Source arg) 00255 { 00256 return _MLIntegerRangeCheck<(_ml_numeric_limits<Target>::MinValue > _ml_numeric_limits<Source>::MinValue), 00257 (_ml_numeric_limits<Target>::MaxValue < _ml_numeric_limits<Source>::MaxValue), 00258 Target, Source> 00259 ::checked_cast(arg); 00260 } 00261 }; 00262 00263 // casting integer to float does no range checks 00264 template <typename Target, typename Source> 00265 struct _MLRangeCheck<true, false, Target, Source> 00266 { 00267 static inline Target checked_cast(Source arg) 00268 { 00269 return static_cast<Target>(arg); 00270 } 00271 }; 00272 00273 // casting float to integer does range checks 00274 template <typename Target, typename Source> 00275 struct _MLRangeCheck<false, true, Target, Source> 00276 { 00277 static inline Target checked_cast(Source arg) 00278 { 00279 arg = floor(arg + 0.5f); 00280 if ((arg < static_cast<Source>(_ml_numeric_limits<Target>::MinValue)) || 00281 (arg > static_cast<Source>(_ml_numeric_limits<Target>::MaxValue))) 00282 { 00283 _ML_OUTPUT_RANGE_ERROR 00284 } 00285 return static_cast<Target>(arg); 00286 } 00287 }; 00288 00289 template <typename Target, typename Source> 00290 struct _MLRangeCheck<true, true, Target, Source> 00291 { 00292 static inline Target checked_cast(Source arg) 00293 { 00294 return _MLFloatRangeCheck<Target, Source>::checked_cast(arg); 00295 } 00296 }; 00297 00298 #endif 00299 00300 //=============================================================================================== 00301 00302 //---------------------------------------------- 00304 //---------------------------------------------- 00305 #if defined(_ML_COMPILE_RANGE_CASTS_ONLY_AS_CASTS) && defined(_ML_COMPILE_RANGE_CASTS_WITH_CHECKS) 00306 #error "_ML_COMPILE_RANGE_CASTS_ONLY_AS_CASTS and _ML_COMPILE_RANGE_CAST_FUNCTIONS_WITH_CHECKS must not be set both." 00307 #else 00308 #if !defined(_ML_COMPILE_RANGE_CASTS_ONLY_AS_CASTS) && !defined(_ML_COMPILE_RANGE_CASTS_WITH_CHECKS) 00309 // Compilation is disabled - so just insert original statements instead of function calls. 00310 00311 template<Target, Source> 00312 inline Target mlrange_cast(Source arg) { return arg; } 00313 00314 #else 00315 00316 #if defined(_ML_COMPILE_RANGE_CASTS_ONLY_AS_CASTS) 00317 // Compilation is enabled, but checks are disabled for maximum performance. Just insert casts then. 00318 00319 template<Target, Source> 00320 inline Target mlrange_cast(Source arg) { return static_cast<Target>(arg); } 00321 00322 #else 00323 // Compilation is enabled with checks. Insert check function calls. 00324 00332 template<typename Target, typename Source> 00333 inline Target mlrange_cast(Source arg) 00334 { 00335 return _MLRangeCheck<_ml_numeric_limits<Target>::isFloat, 00336 _ml_numeric_limits<Source>::isFloat, 00337 Target, Source> 00338 ::checked_cast(arg); 00339 } 00340 00341 #endif 00342 #endif 00343 #endif 00344 #endif 00345