ML Reference
MeVis/Foundation/Sources/MLUtilities/mlRangeCasts.h
Go to the documentation of this file.
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