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