1 /* traits.h -- Traits used to support array/automatic differentiation expressions 2 3 Copyright (C) 2012-2014 University of Reading 4 Copyright (C) 2015-2017 European Centre for Medium-Range Weather Forecasts 5 6 Author: Robin Hogan <r.j.hogan@ecmwf.int> 7 8 This file is part of the Adept library. 9 10 */ 11 12 #ifndef AdeptTraits_H 13 #define AdeptTraits_H 1 14 15 #include <complex> 16 #include <limits> 17 #include <iostream> 18 19 #include <adept/base.h> 20 21 #ifdef ADEPT_CXX11_FEATURES 22 #include <initializer_list> 23 #endif 24 25 namespace adept { 26 27 // Forward declaration of "Active" 28 template <typename T> class Active; 29 30 31 // All traits are in the adept::internal namespace. Note that many 32 // of these are part of the STL in C++11 but are needed so that 33 // Adept can be used with C++98 compilers. 34 namespace internal { 35 36 // ----- CONTENTS ----- 37 // 1. ADEPT_STATIC_ASSERT 38 // 2. enable_if 39 // 3. if_then_else 40 // 4. is_not_expression 41 // 5. is_complex 42 // 6. is_active 43 // 7. is_array 44 // 8. is_scalar_int 45 // 9. all_scalar_ints 46 // 10. underlying_real 47 // 11. underlying_passive 48 // 12. promote 49 // 13. rank_compatible 50 // 14. is_same 51 // 15. remove_reference 52 // 16. initializer_list_rank 53 // 17. matrix_op_defined 54 // 18. is_floating_point 55 // -------------------- 56 57 // --------------------------------------------------------------------- 58 // 1. ADEPT_STATIC_ASSERT 59 // --------------------------------------------------------------------- 60 61 // Heavily templated C++ code as in the Adept library can produce 62 // very long and cryptic compiler error messages. This macro is 63 // useful to check for conditions that should not happen. It check 64 // a bool known at compile time is true, otherwise fail to compile 65 // with a message that is hopefully understandable. 66 // E.g. ADEPT_STATIC_ASSERT(0 > 1, ZERO_IS_NOT_GREATER_THAN_ONE) 67 // would fail at compile time with a message containing 68 // ERROR_ZERO_IS_NOT_GREATER_THAN_ONE, which should hopefully 69 // stand out even in a long error message. 70 71 // Helper class 72 template<bool> struct compile_time_check 73 { typedef int STATIC_ASSERTION_HAS_FAILED; }; 74 template<> struct compile_time_check<false> { }; 75 76 // Define the macro in which a struct is defined that inherits 77 // from compile_time_check 78 #if defined(__GNUC__) && !defined(__INTEL_COMPILER) 79 #pragma GCC diagnostic ignored "-Wpragmas" 80 #pragma GCC diagnostic ignored "-Wunused-local-typedefs" 81 #pragma GCC diagnostic warning "-Wpragmas" 82 #endif 83 #define ADEPT_STATIC_ASSERT(condition, msg) \ 84 do { struct ERROR_##msg : public ::adept::internal::compile_time_check<(condition)> { }; \ 85 typedef typename ERROR_##msg ::STATIC_ASSERTION_HAS_FAILED type; \ 86 } while (0) 87 88 // --------------------------------------------------------------------- 89 // 2. enable_if 90 // --------------------------------------------------------------------- 91 92 // To enable a function "Type function()" only if CONDITION is 93 // true, replace "Type" in the function declaration with "typename 94 // enable_if<CONDITIONAL,Type>::type" 95 template <bool, typename T = void> struct enable_if { }; 96 // Partial specialization for true. 97 template <typename T> struct enable_if<true, T> { typedef T type; }; 98 99 100 // --------------------------------------------------------------------- 101 // 3. if_then_else 102 // --------------------------------------------------------------------- 103 104 // "if_then_else<CONDITION, YES, NO>::type" resolves to YES if 105 // CONDITION is "true", NO otherwise. A limitation is that both Y 106 // and N must be valid types 107 template <bool, typename Y, typename N> 108 struct if_then_else { typedef Y type; }; 109 110 template <typename Y, typename N> 111 struct if_then_else<false, Y, N> { typedef N type; }; 112 113 114 // --------------------------------------------------------------------- 115 // 4. is_not_expression 116 // --------------------------------------------------------------------- 117 118 // The following enables us to provide functions that work only on 119 // types *not* derived from the Expression struct: 120 // "is_not_expression<E>::value" is "false" if E is not an 121 // expression and "true" otherwise 122 template <typename T> 123 struct is_not_expression 124 { 125 private: 126 typedef char yes; 127 typedef struct { char array[2]; } no; 128 template <typename C> static yes test(typename C::_adept_expression_flag*); 129 template <typename C> static no test(...); 130 public: 131 static const bool value = sizeof(test<T>(0)) != sizeof(yes); 132 }; 133 134 135 // --------------------------------------------------------------------- 136 // 5. is_complex 137 // --------------------------------------------------------------------- 138 139 // Test for complex numbers: "is_complex<S>::value" is "true" if S 140 // is complex, "false" otherwise 141 template <typename> struct is_complex 142 { static const bool value = false; }; 143 template <> struct is_complex<std::complex<float> > 144 { static const bool value = true; }; 145 template <> struct is_complex<std::complex<double> > 146 { static const bool value = true; }; 147 template <> struct is_complex<std::complex<long double> > 148 { static const bool value = true; }; 149 150 151 // --------------------------------------------------------------------- 152 // 6. is_active 153 // --------------------------------------------------------------------- 154 155 // Test for active numbers: "is_active<S>::value" is "true" if S 156 // is active, "false" otherwise. 157 // Then the default case for non-expressions returns false 158 159 template <typename T> struct expr_cast; // Forward declaration 160 161 template <typename T, class Enable = void> 162 struct is_active { }; 163 164 template <typename T> 165 struct is_active<T, typename enable_if<is_not_expression<T>::value>::type> 166 { static const bool value = false; }; 167 168 // Expressions define a static const bool called "is_active" 169 template <typename T> 170 struct is_active<T, typename enable_if<!is_not_expression<T>::value>::type> 171 { static const bool value = expr_cast<T>::is_active; }; 172 173 174 // --------------------------------------------------------------------- 175 // 7. is_array 176 // --------------------------------------------------------------------- 177 178 /* 179 // "is_array<E>::value" is "true" if E is an array expression and 180 // "false" otherwise. The default case for non-expressions 181 // returns false 182 template <typename T, class Enable = void> 183 struct is_array { }; 184 template <typename T> 185 struct is_array<T, typename enable_if<is_not_expression<T>::value>::type> 186 { static const bool value = false; }; 187 // Expressions define a static const bool called "is_array" 188 template <typename T> 189 struct is_array<T, typename enable_if<!is_not_expression<T>::value>::type> 190 { static const bool value = T::is_array; }; 191 */ 192 193 // --------------------------------------------------------------------- 194 // 8. is_scalar_int 195 // --------------------------------------------------------------------- 196 197 // Return whether template argument is of integer type, or is a 198 // 0-dimensional expression of integer type 199 template <typename T, class Enable = void> 200 struct is_scalar_int { }; 201 202 template <typename T> 203 struct is_scalar_int<T, 204 typename enable_if<is_not_expression<T>::value>::type> { 205 static const bool value = std::numeric_limits<T>::is_integer; 206 static const int count = value; 207 }; 208 209 template <typename T> 210 struct is_scalar_int<T, 211 typename enable_if<!is_not_expression<T>::value>::type> 212 { 213 static const bool value 214 = std::numeric_limits<typename T::type>::is_integer 215 && expr_cast<T>::rank == 0; 216 static const int count = value; 217 }; 218 219 220 // --------------------------------------------------------------------- 221 // 9. all_scalar_ints 222 // --------------------------------------------------------------------- 223 224 // all_scalar_ints<Rank,I0,I1...>::value returns true if I[0] to 225 // I[Rank-1] are all scalar integers 226 227 // First define a "null" type 228 struct null_type { }; 229 template <typename T> struct is_null_type { 230 static const bool value = false; 231 static const int count = 0; 232 }; 233 template <> struct is_null_type<null_type>{ 234 static const bool value = true; 235 static const int count = 1; 236 }; 237 238 template <int Rank, typename I0, typename I1 = null_type, 239 typename I2 = null_type, typename I3 = null_type, 240 typename I4 = null_type, typename I5 = null_type, 241 typename I6 = null_type> 242 struct all_scalar_ints { 243 static const bool value = (Rank == (is_scalar_int<I0>::count 244 +is_scalar_int<I1>::count 245 +is_scalar_int<I2>::count 246 +is_scalar_int<I3>::count 247 +is_scalar_int<I4>::count 248 +is_scalar_int<I5>::count 249 +is_scalar_int<I6>::count)); 250 }; 251 252 253 254 // --------------------------------------------------------------------- 255 // 10. underlying_real 256 // --------------------------------------------------------------------- 257 258 // Return the underlying real type for a complex argument: 259 // "underlying_real<S>::type returns T if S is of type 260 // std::complex<T>, or returns S if it is not complex 261 /* 262 template <typename T> 263 struct underlying_real 264 { 265 private: 266 template <bool, typename S> 267 struct _underlying_real 268 { typedef S type; }; 269 template <typename S> 270 struct _underlying_real<true, S> 271 { typedef typename S::type type; }; 272 public: 273 typedef typename _underlying_real<is_complex<T>::value, 274 T>::type type; 275 }; 276 */ 277 template <typename T> 278 struct underlying_real { 279 typedef T type; 280 }; 281 template <typename T> 282 struct underlying_real<std::complex<T> > { 283 typedef T type; 284 }; 285 286 // --------------------------------------------------------------------- 287 // 11. underlying_passive 288 // --------------------------------------------------------------------- 289 290 // Return the underlying passive type for an active argument: 291 // "underlying_passive<S>::type returns T if S is of type 292 // adept::Active<T>, or returns S if it is not active. 293 template <typename T> 294 struct underlying_passive 295 { 296 private: 297 template <bool, typename S> 298 struct _underlying_passive 299 { typedef S type; }; 300 template <typename S> 301 struct _underlying_passive<true, S> 302 { typedef typename S::type type; }; 303 public: 304 typedef typename _underlying_passive<is_active<T>::value, 305 T>::type type; 306 }; 307 308 309 // --------------------------------------------------------------------- 310 // 12. promote 311 // --------------------------------------------------------------------- 312 313 // "promote<L,R>::type" returns the type that a binary operation 314 // (e.g. multiplication) between types L and R should result in. 315 // Note that "complexity" and "precision" are promoted separately, 316 // so double + std::complex<float> will result in an object of 317 // type std::complex<double> >. 318 template <typename L, typename R> 319 struct promote { 320 private: 321 template <typename A, typename B> 322 struct promote_primitive { 323 static const bool A_bigger_than_B = (sizeof(A) > sizeof(B)); 324 static const bool A_float_B_int = (!std::numeric_limits<A>::is_integer) 325 && std::numeric_limits<B>::is_integer; 326 static const bool A_int_B_float = std::numeric_limits<A>::is_integer 327 && (!std::numeric_limits<B>::is_integer); 328 static const bool prefer_float = A_float_B_int || A_int_B_float; 329 typedef typename if_then_else<A_float_B_int, A, B>::type float_type; 330 typedef typename if_then_else<A_bigger_than_B, A, B>::type biggest_type; 331 typedef typename if_then_else<prefer_float, float_type, biggest_type>::type type; 332 }; 333 334 typedef typename promote_primitive< 335 typename underlying_real<typename underlying_passive<L>::type>::type, 336 typename underlying_real<typename underlying_passive<R>::type>::type>::type real; 337 typedef typename if_then_else<is_complex<L>::value 338 || is_complex<R>::value, 339 std::complex<real>, 340 real>::type complex_type; 341 public: 342 typedef typename if_then_else<is_active<L>::value || is_active<R>::value, 343 adept::Active<complex_type>, 344 complex_type>::type type; 345 }; 346 347 // If ever the template arguments are the same 348 // (e.g. Packet<double>), we simply return this type 349 template <typename T> 350 struct promote<T,T> { 351 typedef T type; 352 }; 353 354 355 // --------------------------------------------------------------------- 356 // 13. rank_compatible 357 // --------------------------------------------------------------------- 358 359 // Check that an array of rank LRank could enter an operation 360 // (e.g. addition) with an array of rank RRank: the two ranks must 361 // either be the same, or either can be zero 362 template <int LRank, int RRank> 363 struct rank_compatible { 364 static const bool value = (LRank == RRank || LRank == 0 || RRank == 0); 365 }; 366 367 368 // --------------------------------------------------------------------- 369 // 14. is_same 370 // --------------------------------------------------------------------- 371 372 // Compare two types to see if they're the same 373 template<typename T, typename U> 374 struct is_same { static const bool value = false; }; 375 376 template<typename T> 377 struct is_same<T,T> { static const bool value = true; }; 378 379 380 // --------------------------------------------------------------------- 381 // 15. remove_reference 382 // --------------------------------------------------------------------- 383 384 // Remove reference from a type if present 385 template<typename T> struct remove_reference { typedef T type; }; 386 template<typename T> struct remove_reference<T&> { typedef T type; }; 387 388 389 // --------------------------------------------------------------------- 390 // 16. initializer_list_rank 391 // --------------------------------------------------------------------- 392 #ifdef ADEPT_CXX11_FEATURES 393 394 // initializer_link_rank<T>::value returns 0 if T is not a 395 // std:initializer_list, otherwise it returns the number of nested 396 // std::initializer_list's 397 template <typename T> struct is_initializer_list 398 { static const bool value = false; }; 399 template <typename T> struct is_initializer_list<std::initializer_list<T> > 400 { static const bool value = true; }; 401 402 template <typename T, class Enable = void> 403 struct initializer_list_rank { }; 404 405 template <typename T> 406 struct initializer_list_rank<T, 407 typename enable_if<!is_initializer_list<T>::value>::type> 408 { typedef T type; 409 static const int value = 0; }; 410 411 template <typename T> 412 struct initializer_list_rank<std::initializer_list<T>, 413 typename enable_if<!is_initializer_list<T>::value>::type> 414 { typedef T type; 415 static const int value = 1; }; 416 417 template <typename T> 418 struct initializer_list_rank<std::initializer_list<T>, 419 typename enable_if<is_initializer_list<T>::value>::type> 420 { typedef typename initializer_list_rank<T>::type type; 421 static const int value = 1 + initializer_list_rank<T>::value; }; 422 423 #endif 424 425 // --------------------------------------------------------------------- 426 // 17. matrix_op_defined 427 // --------------------------------------------------------------------- 428 429 // Return true if a type is float or double, false otherwise 430 template <typename T> 431 struct matrix_op_defined { static const bool value = false; }; 432 433 template <> 434 struct matrix_op_defined<float> { static const bool value = true; }; 435 436 template <> 437 struct matrix_op_defined<double> { static const bool value = true; }; 438 439 // --------------------------------------------------------------------- 440 // 18. is_floating_point 441 // --------------------------------------------------------------------- 442 443 template <typename T> 444 struct is_floating_point { static const bool value = false; }; 445 446 template <> 447 struct is_floating_point<float> { static const bool value = true; }; 448 template <> 449 struct is_floating_point<double> { static const bool value = true; }; 450 template <> 451 struct is_floating_point<long double> { static const bool value = true; }; 452 453 } // End namespace internal 454 455 } // End namespace adept 456 457 458 459 #endif 460