00001 /*!@file Util/Promotions.H basic definitions for template type promotions */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2003 // 00005 // by the University of Southern California (USC) and the iLab at USC. // 00006 // See http://iLab.usc.edu for information about this project. // 00007 // //////////////////////////////////////////////////////////////////// // 00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00010 // in Visual Environments, and Applications'' by Christof Koch and // 00011 // Laurent Itti, California Institute of Technology, 2001 (patent // 00012 // pending; application number 09/912,225 filed July 23, 2001; see // 00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00014 // //////////////////////////////////////////////////////////////////// // 00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00016 // // 00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00018 // redistribute it and/or modify it under the terms of the GNU General // 00019 // Public License as published by the Free Software Foundation; either // 00020 // version 2 of the License, or (at your option) any later version. // 00021 // // 00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00025 // PURPOSE. See the GNU General Public License for more details. // 00026 // // 00027 // You should have received a copy of the GNU General Public License // 00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00030 // Boston, MA 02111-1307 USA. // 00031 // //////////////////////////////////////////////////////////////////// // 00032 // 00033 // Primary maintainer for this file: Laurent Itti <itti@usc.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Util/Promotions.H $ 00035 // $Id: Promotions.H 4731 2005-06-29 19:33:51Z rjpeters $ 00036 // 00037 00038 #ifndef PROMOTIONS_H_DEFINED 00039 #define PROMOTIONS_H_DEFINED 00040 00041 #include "Util/Types.H" 00042 #include "Util/log.H" 00043 #include <limits> 00044 00045 00046 /*! The idea here is to create a mechanism by which a given template 00047 type can be promoted to a more capable type (e.g., byte to float, or 00048 PixRGB<byte> to PixRGB<float>), typically to hold the result of some 00049 operation that may exceed the capacity of the original type (e.g., the 00050 result of adding two bytes may not fit within the range of possible 00051 values for byte). See http://www.oonumerics.org/blitz/traits.html for 00052 further details. The way to use this is as follows: if you have a 00053 template type T (which may be scalar or not) that you wish to promote 00054 to a more capable type that would have "similar characteristics" to 00055 another type TT, then use the type promote_trait<T,TT>::TP. For 00056 example: promote_trait<T,float>::TP is a float if T was a byte, and is 00057 a PixRGB<float> if T was a PixRGB<byte> */ 00058 00059 00060 //! Promote from T1 and T2 to a type than can hold T1 * T2 00061 /*! Basic promotion mechanism: given T1 and T2, TP provides the 00062 appropriate type to hold the result of an operation involving T1 and 00063 T2. Default is to promote to type T1, that is, no change (this is 00064 used when T2 is "weaker" than T1; when T2 is "stronger" that T1, the 00065 explicit specialized rules below are used). */ 00066 template <class T1, class T2> 00067 struct promote_trait { typedef T1 TP; enum { willPromote = 0 }; }; 00068 00069 // Here go the various specializations: 00070 00071 // NOTE: we follow the basic C++ integral promotions, promoting to int 00072 // from byte, int16 and int32 00073 template <> struct promote_trait<byte, byte> { typedef int TP; enum { willPromote = 1 }; }; 00074 template <> struct promote_trait<byte, int16> { typedef int TP; enum { willPromote = 1 }; }; 00075 template <> struct promote_trait<byte, int32> { typedef int TP; enum { willPromote = 1 }; }; 00076 template <> struct promote_trait<byte, float> { typedef float TP; enum { willPromote = 1 }; }; 00077 template <> struct promote_trait<byte, double> { typedef double TP; enum { willPromote = 1 }; }; 00078 template <> struct promote_trait<byte, long double> { typedef long double TP; enum { willPromote = 1 }; }; 00079 00080 template <> struct promote_trait<int16, byte> { typedef int TP; enum { willPromote = 1 }; }; 00081 template <> struct promote_trait<int16, int16> { typedef int TP; enum { willPromote = 1 }; }; 00082 template <> struct promote_trait<int16, int32> { typedef int TP; enum { willPromote = 1 }; }; 00083 template <> struct promote_trait<int16, float> { typedef float TP; enum { willPromote = 1 }; }; 00084 template <> struct promote_trait<int16, double> { typedef double TP; enum { willPromote = 1 }; }; 00085 template <> struct promote_trait<int16, long double> { typedef long double TP; enum { willPromote = 1 }; }; 00086 00087 template <> struct promote_trait<int32, float> { typedef float TP; enum { willPromote = 1 }; }; 00088 template <> struct promote_trait<int32, double> { typedef double TP; enum { willPromote = 1 }; }; 00089 template <> struct promote_trait<int32, long double> { typedef long double TP; enum { willPromote = 1 }; }; 00090 00091 template <> struct promote_trait<float, double> { typedef double TP; enum { willPromote = 1 }; }; 00092 template <> struct promote_trait<float, long double> { typedef long double TP; enum { willPromote = 1 }; }; 00093 00094 template <> struct promote_trait<double, long double> { typedef long double TP; enum { willPromote = 1 }; }; 00095 00096 //! A macro for specializing promote_trait for template types. 00097 /*! See e.g. usage in PixelsTypes.H. */ 00098 #define SPECIALIZE_PROMOTE_TRAIT(T_TYPE, T_PARAMS, T_ARGS) \ 00099 template <T_PARAMS> class T_TYPE; \ 00100 template <> struct promote_trait<T_TYPE<byte T_ARGS> , byte> { typedef T_TYPE<int T_ARGS> TP; enum { willPromote = 1 }; }; \ 00101 template <> struct promote_trait<T_TYPE<byte T_ARGS> , int16> { typedef T_TYPE<int T_ARGS> TP; enum { willPromote = 1 }; }; \ 00102 template <> struct promote_trait<T_TYPE<byte T_ARGS> , int32> { typedef T_TYPE<int T_ARGS> TP; enum { willPromote = 1 }; }; \ 00103 template <> struct promote_trait<T_TYPE<byte T_ARGS> , float> { typedef T_TYPE<float T_ARGS> TP; enum { willPromote = 1 }; }; \ 00104 template <> struct promote_trait<T_TYPE<byte T_ARGS> , double> { typedef T_TYPE<double T_ARGS> TP; enum { willPromote = 1 }; }; \ 00105 template <> struct promote_trait<T_TYPE<byte T_ARGS> , long double> { typedef T_TYPE<long double T_ARGS> TP; enum { willPromote = 1 }; }; \ 00106 \ 00107 template <> struct promote_trait<T_TYPE<int16 T_ARGS> , byte> { typedef T_TYPE<int T_ARGS> TP; enum { willPromote = 1 }; }; \ 00108 template <> struct promote_trait<T_TYPE<int16 T_ARGS> , int16> { typedef T_TYPE<int T_ARGS> TP; enum { willPromote = 1 }; }; \ 00109 template <> struct promote_trait<T_TYPE<int16 T_ARGS> , int32> { typedef T_TYPE<int T_ARGS> TP; enum { willPromote = 1 }; }; \ 00110 template <> struct promote_trait<T_TYPE<int16 T_ARGS> , float> { typedef T_TYPE<float T_ARGS> TP; enum { willPromote = 1 }; }; \ 00111 template <> struct promote_trait<T_TYPE<int16 T_ARGS> , double> { typedef T_TYPE<double T_ARGS> TP; enum { willPromote = 1 }; }; \ 00112 template <> struct promote_trait<T_TYPE<int16 T_ARGS> , long double> { typedef T_TYPE<long double T_ARGS> TP; enum { willPromote = 1 }; }; \ 00113 \ 00114 template <> struct promote_trait<T_TYPE<int32 T_ARGS> , float> { typedef T_TYPE<float T_ARGS> TP; enum { willPromote = 1 }; }; \ 00115 template <> struct promote_trait<T_TYPE<int32 T_ARGS> , double> { typedef T_TYPE<double T_ARGS> TP; enum { willPromote = 1 }; }; \ 00116 template <> struct promote_trait<T_TYPE<int32 T_ARGS> , long double> { typedef T_TYPE<long double T_ARGS> TP; enum { willPromote = 1 }; }; \ 00117 \ 00118 template <> struct promote_trait<T_TYPE<float T_ARGS> , double> { typedef T_TYPE<double T_ARGS> TP; enum { willPromote = 1 }; }; \ 00119 template <> struct promote_trait<T_TYPE<float T_ARGS> , long double> { typedef T_TYPE<long double T_ARGS> TP; enum { willPromote = 1 }; }; \ 00120 \ 00121 template <> struct promote_trait<T_TYPE<double T_ARGS> , long double> { typedef T_TYPE<long double T_ARGS> TP; enum { willPromote = 1 }; }; \ 00122 \ 00123 template <> struct promote_trait<T_TYPE<byte T_ARGS> , T_TYPE<byte T_ARGS> > { typedef T_TYPE<int T_ARGS> TP; enum { willPromote = 1 }; }; \ 00124 template <> struct promote_trait<T_TYPE<byte T_ARGS> , T_TYPE<int16 T_ARGS> > { typedef T_TYPE<int T_ARGS> TP; enum { willPromote = 1 }; }; \ 00125 template <> struct promote_trait<T_TYPE<byte T_ARGS> , T_TYPE<int32 T_ARGS> > { typedef T_TYPE<int T_ARGS> TP; enum { willPromote = 1 }; }; \ 00126 template <> struct promote_trait<T_TYPE<byte T_ARGS> , T_TYPE<float T_ARGS> > { typedef T_TYPE<float T_ARGS> TP; enum { willPromote = 1 }; }; \ 00127 template <> struct promote_trait<T_TYPE<byte T_ARGS> , T_TYPE<double T_ARGS> > { typedef T_TYPE<double T_ARGS> TP; enum { willPromote = 1 }; }; \ 00128 template <> struct promote_trait<T_TYPE<byte T_ARGS> , T_TYPE<long double T_ARGS> > { typedef T_TYPE<long double T_ARGS> TP; enum { willPromote = 1 }; }; \ 00129 \ 00130 template <> struct promote_trait<T_TYPE<int16 T_ARGS> , T_TYPE<byte T_ARGS> > { typedef T_TYPE<int T_ARGS> TP; enum { willPromote = 1 }; }; \ 00131 template <> struct promote_trait<T_TYPE<int16 T_ARGS> , T_TYPE<int16 T_ARGS> > { typedef T_TYPE<int T_ARGS> TP; enum { willPromote = 1 }; }; \ 00132 template <> struct promote_trait<T_TYPE<int16 T_ARGS> , T_TYPE<int32 T_ARGS> > { typedef T_TYPE<int T_ARGS> TP; enum { willPromote = 1 }; }; \ 00133 template <> struct promote_trait<T_TYPE<int16 T_ARGS> , T_TYPE<float T_ARGS> > { typedef T_TYPE<float T_ARGS> TP; enum { willPromote = 1 }; }; \ 00134 template <> struct promote_trait<T_TYPE<int16 T_ARGS> , T_TYPE<double T_ARGS> > { typedef T_TYPE<double T_ARGS> TP; enum { willPromote = 1 }; }; \ 00135 template <> struct promote_trait<T_TYPE<int16 T_ARGS> , T_TYPE<long double T_ARGS> > { typedef T_TYPE<long double T_ARGS> TP; enum { willPromote = 1 }; }; \ 00136 \ 00137 template <> struct promote_trait<T_TYPE<int32 T_ARGS> , T_TYPE<float T_ARGS> > { typedef T_TYPE<float T_ARGS> TP; enum { willPromote = 1 }; }; \ 00138 template <> struct promote_trait<T_TYPE<int32 T_ARGS> , T_TYPE<double T_ARGS> > { typedef T_TYPE<double T_ARGS> TP; enum { willPromote = 1 }; }; \ 00139 template <> struct promote_trait<T_TYPE<int32 T_ARGS> , T_TYPE<long double T_ARGS> > { typedef T_TYPE<long double T_ARGS> TP; enum { willPromote = 1 }; }; \ 00140 \ 00141 template <> struct promote_trait<T_TYPE<float T_ARGS> , T_TYPE<double T_ARGS> > { typedef T_TYPE<double T_ARGS> TP; enum { willPromote = 1 }; }; \ 00142 template <> struct promote_trait<T_TYPE<float T_ARGS> , T_TYPE<long double T_ARGS> > { typedef T_TYPE<long double T_ARGS> TP; enum { willPromote = 1 }; }; \ 00143 \ 00144 template <> struct promote_trait<T_TYPE<double T_ARGS> , T_TYPE<long double T_ARGS> > { typedef T_TYPE<long double T_ARGS> TP; enum { willPromote = 1 }; }; 00145 00146 // ###################################################################### 00147 00148 // The following defines rules to safely convert from type TT to type 00149 // T, clamping the values to the destination type's range if 00150 // necessary. The function to use is clamped_convert<T>(xx), which 00151 // will convert xx from type TT to type T, clamping its value to the 00152 // acceptable range for T if necessary. 00153 00154 // Basically there's a promoteFromTo<T1,T2> struct that exposes an 00155 // enum is_preserving which is set to 1 or 0 depending on whether T1 00156 // --> T2 is value-preserving or not. Then defined a convert_helper 00157 // struct that has a convert() function that does the real work, 00158 // except the struct can be specialized on whether the conversion is 00159 // preserving or not. Then the actual clamped_convert function just 00160 // forwards to convert_helper<T1,T2>::convert(), which picks the right 00161 // implementation. This way all of the important information is 00162 // specified by how we define promoteFromTo<T1,T2>::is_preserving, and 00163 // this specification is cleanly separated from the implementation 00164 // that uses it. 00165 00166 // by default, promotion is considered range-preserving (will not 00167 // require clamping by us in clamped_convert). The rationale for this 00168 // weirdness is that for unknown types we will leave it to the 00169 // converting operator=() of that type to deal with the clamping. 00170 template <class T1, class T2> 00171 struct promoteFromTo { enum { is_preserving = 1 }; }; 00172 00173 // promote from T to itself is a range-preserving no-op: 00174 template <class T> 00175 struct promoteFromTo<T, T> { enum { is_preserving = 1 }; }; 00176 00177 // these rules declare which conversions are not range-preserving and 00178 // will require clamping within clamped_convert; they are used to 00179 // enforce clamping of builtin types: 00180 00181 template <> struct promoteFromTo<int16 , byte> { enum { is_preserving = 0 }; }; 00182 template <> struct promoteFromTo<int32 , byte> { enum { is_preserving = 0 }; }; 00183 template <> struct promoteFromTo<float , byte> { enum { is_preserving = 0 }; }; 00184 template <> struct promoteFromTo<double , byte> { enum { is_preserving = 0 }; }; 00185 template <> struct promoteFromTo<long double, byte> { enum { is_preserving = 0 }; }; 00186 00187 template <> struct promoteFromTo<int32 , int16> { enum { is_preserving = 0 }; }; 00188 template <> struct promoteFromTo<float , int16> { enum { is_preserving = 0 }; }; 00189 template <> struct promoteFromTo<double , int16> { enum { is_preserving = 0 }; }; 00190 template <> struct promoteFromTo<long double, int16> { enum { is_preserving = 0 }; }; 00191 00192 template <> struct promoteFromTo<float , int32> { enum { is_preserving = 0 }; }; 00193 template <> struct promoteFromTo<double , int32> { enum { is_preserving = 0 }; }; 00194 template <> struct promoteFromTo<long double, int32> { enum { is_preserving = 0 }; }; 00195 00196 template <> struct promoteFromTo<double , float> { enum { is_preserving = 0 }; }; 00197 template <> struct promoteFromTo<long double, float> { enum { is_preserving = 0 }; }; 00198 00199 template <> struct promoteFromTo<long double, double> { enum { is_preserving = 0 }; }; 00200 00201 //! Helper struct to handle rounding between different types 00202 /*! Note that we aren't worried about clamping here, that is handled 00203 separately by convert_helper (see below). We have different 00204 specializations of round_helper depending on whether the src and 00205 dest types are integral or floating-point. The only case where we 00206 have to do any actual rounding is when we're going from 00207 floating->integral. */ 00208 template <class dst_type, class src_type, 00209 bool need_rounding = 00210 (std::numeric_limits<dst_type>::is_integer 00211 && !std::numeric_limits<src_type>::is_integer)> 00212 struct round_helper; 00213 00214 template <class dst_type, class src_type> 00215 struct round_helper<dst_type, src_type, false> // no rounding needed 00216 { 00217 inline static dst_type round(src_type x) 00218 { return dst_type(x); } 00219 }; 00220 00221 template <class dst_type, class src_type> 00222 struct round_helper<dst_type, src_type, true> // rounding needed 00223 { 00224 inline static dst_type round(src_type x) 00225 { 00226 if (x > 0) return dst_type(x+0.5); // round toward +Inf 00227 else if (x < 0) return dst_type(x-0.5); // round toward -Inf 00228 else return dst_type(0); 00229 } 00230 }; 00231 00232 //! Helper struct to handle type conversions at compile time. 00233 /*! Helps decide whether to clamp or not at compile time so that we 00234 don't waste time in a test at run time; because 00235 std::numeric_limits is a pile of trash, we also need to 00236 parameterize the helper by a boolean indicating whether the type 00237 we convert to is integer or not. This will determine which numeric 00238 limits we use to do the clamping: */ 00239 template <class dst_type, class src_type, 00240 bool is_preserving = 00241 promoteFromTo<src_type, dst_type>::is_preserving, 00242 bool dst_type_is_integer = 00243 std::numeric_limits<dst_type>::is_integer> 00244 struct convert_helper; 00245 00246 // clamp and convert if non-preserving; dest type is integral: 00247 template <class dst_type, class src_type> 00248 struct convert_helper<dst_type, src_type, 00249 /* is_preserving = */ false, 00250 /* dst_type_is_integer = */ true> 00251 { 00252 inline static dst_type convert(src_type x) 00253 { 00254 if (x < std::numeric_limits<dst_type>::min()) 00255 return std::numeric_limits<dst_type>::min(); 00256 if (x > std::numeric_limits<dst_type>::max()) 00257 return std::numeric_limits<dst_type>::max(); 00258 return dst_type(x); 00259 } 00260 00261 inline static dst_type rounded_convert(src_type x) 00262 { 00263 if (x < std::numeric_limits<dst_type>::min()) 00264 return std::numeric_limits<dst_type>::min(); 00265 if (x > std::numeric_limits<dst_type>::max()) 00266 return std::numeric_limits<dst_type>::max(); 00267 00268 return round_helper<dst_type, src_type>::round(x); 00269 } 00270 }; 00271 00272 // clamp and convert if non-preserving; dest type is non-integral: 00273 template <class dst_type, class src_type> 00274 struct convert_helper<dst_type, src_type, 00275 /* is_preserving = */ false, 00276 /* dst_type_is_integer = */ false> 00277 { 00278 inline static dst_type convert(src_type x) 00279 { 00280 if (x < -std::numeric_limits<dst_type>::max()) 00281 return -std::numeric_limits<dst_type>::max(); 00282 if (x > std::numeric_limits<dst_type>::max()) 00283 return std::numeric_limits<dst_type>::max(); 00284 return dst_type(x); 00285 } 00286 00287 inline static dst_type rounded_convert(src_type x) 00288 { 00289 if (x < -std::numeric_limits<dst_type>::max()) 00290 return -std::numeric_limits<dst_type>::max(); 00291 if (x > std::numeric_limits<dst_type>::max()) 00292 return std::numeric_limits<dst_type>::max(); 00293 00294 return round_helper<dst_type, src_type>::round(x); 00295 } 00296 }; 00297 00298 // cast without checking range if preserving; for builtin types, this 00299 // means that we know that we can safely cast (the destination types 00300 // can represent all values of the source type); for other types, we 00301 // here assume that the conversion constructor will take care of the 00302 // clamping: 00303 template <class dst_type, class src_type> 00304 struct convert_helper<dst_type, src_type, 00305 /* is_preserving = */ true, 00306 /* dst_type_is_integer = */ true> 00307 { 00308 inline static dst_type convert(src_type x) 00309 { 00310 return dst_type(x); 00311 } 00312 00313 inline static dst_type rounded_convert(src_type x) 00314 { 00315 return round_helper<dst_type, src_type>::round(x); 00316 } 00317 }; 00318 00319 template <class dst_type, class src_type> 00320 struct convert_helper<dst_type, src_type, 00321 /* is_preserving = */ true, 00322 /* dst_type_is_integer = */ false> 00323 { 00324 inline static dst_type convert(src_type x) 00325 { 00326 return dst_type(x); 00327 } 00328 00329 inline static dst_type rounded_convert(src_type x) 00330 { 00331 return round_helper<dst_type, src_type>::round(x); 00332 } 00333 }; 00334 00335 //! Convert safely from src_type to type dst_type, clamping if necessary. 00336 template <class dst_type, class src_type> 00337 inline 00338 dst_type clamped_convert(src_type x) 00339 { 00340 return convert_helper<dst_type, src_type>::convert(x); 00341 } 00342 00343 //! Convert safely from src_type to type dst_type, clamping and rounding as needed. 00344 template <class dst_type, class src_type> 00345 inline 00346 dst_type clamped_rounded_convert(src_type x) 00347 { 00348 return convert_helper<dst_type, src_type>::rounded_convert(x); 00349 } 00350 00351 #endif 00352 00353 // ###################################################################### 00354 /* So things look consistent in everyone's emacs... */ 00355 /* Local Variables: */ 00356 /* indent-tabs-mode: nil */ 00357 /* End: */