piranha  0.10
type_traits.hpp
Go to the documentation of this file.
1 /* Copyright 2009-2017 Francesco Biscani (bluescarni@gmail.com)
2 
3 This file is part of the Piranha library.
4 
5 The Piranha library is free software; you can redistribute it and/or modify
6 it under the terms of either:
7 
8  * the GNU Lesser General Public License as published by the Free
9  Software Foundation; either version 3 of the License, or (at your
10  option) any later version.
11 
12 or
13 
14  * the GNU General Public License as published by the Free Software
15  Foundation; either version 3 of the License, or (at your option) any
16  later version.
17 
18 or both in parallel, as here.
19 
20 The Piranha library is distributed in the hope that it will be useful, but
21 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
22 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23 for more details.
24 
25 You should have received copies of the GNU General Public License and the
26 GNU Lesser General Public License along with the Piranha library. If not,
27 see https://www.gnu.org/licenses/. */
28 
29 #ifndef PIRANHA_TYPE_TRAITS_HPP
30 #define PIRANHA_TYPE_TRAITS_HPP
31 
38 #include <cstdarg>
39 #include <cstddef>
40 #include <functional>
41 #include <initializer_list>
42 #include <iterator>
43 #include <limits>
44 #include <ostream>
45 #include <tuple>
46 #include <type_traits>
47 #include <utility>
48 
49 #include <piranha/config.hpp>
50 
51 namespace piranha
52 {
53 
54 inline namespace impl
55 {
56 
57 // http://en.cppreference.com/w/cpp/types/void_t
58 template <typename... Ts>
59 struct make_void {
60  typedef void type;
61 };
62 
63 template <typename... Ts>
64 using void_t = typename make_void<Ts...>::type;
65 
66 // http://en.cppreference.com/w/cpp/experimental/is_detected
67 template <class Default, class AlwaysVoid, template <class...> class Op, class... Args>
68 struct detector {
69  using value_t = std::false_type;
70  using type = Default;
71 };
72 
73 template <class Default, template <class...> class Op, class... Args>
74 struct detector<Default, void_t<Op<Args...>>, Op, Args...> {
75  using value_t = std::true_type;
76  using type = Op<Args...>;
77 };
78 
79 // http://en.cppreference.com/w/cpp/experimental/nonesuch
80 struct nonesuch {
81  nonesuch() = delete;
82  ~nonesuch() = delete;
83  nonesuch(nonesuch const &) = delete;
84  void operator=(nonesuch const &) = delete;
85 };
86 
87 template <template <class...> class Op, class... Args>
88 using is_detected = typename detector<nonesuch, void, Op, Args...>::value_t;
89 
90 template <template <class...> class Op, class... Args>
91 using detected_t = typename detector<nonesuch, void, Op, Args...>::type;
92 
93 // http://en.cppreference.com/w/cpp/types/conjunction
94 template <class...>
95 struct conjunction : std::true_type {
96 };
97 
98 template <class B1>
99 struct conjunction<B1> : B1 {
100 };
101 
102 template <class B1, class... Bn>
103 struct conjunction<B1, Bn...> : std::conditional<B1::value != false, conjunction<Bn...>, B1>::type {
104 };
105 
106 // http://en.cppreference.com/w/cpp/types/disjunction
107 template <class...>
108 struct disjunction : std::false_type {
109 };
110 
111 template <class B1>
112 struct disjunction<B1> : B1 {
113 };
114 
115 template <class B1, class... Bn>
116 struct disjunction<B1, Bn...> : std::conditional<B1::value != false, B1, disjunction<Bn...>>::type {
117 };
118 
119 // http://en.cppreference.com/w/cpp/types/negation
120 template <class B>
121 struct negation : std::integral_constant<bool, !B::value> {
122 };
123 
124 // This is like disjunction, but instead of providing true/false it provides
125 // the index of the first boolean class which evaluates to true. If no class
126 // evaluates to true, then it will return the number of boolean classes
127 // used in the instantiation.
128 template <std::size_t CurIdx, class...>
129 struct disjunction_idx_impl : std::integral_constant<std::size_t, 0u> {
130 };
131 
132 template <std::size_t CurIdx, class B1>
133 struct disjunction_idx_impl<CurIdx, B1>
134  : std::integral_constant<std::size_t, (B1::value != false) ? CurIdx : CurIdx + 1u> {
135 };
136 
137 template <std::size_t CurIdx, class B1, class... Bn>
138 struct disjunction_idx_impl<CurIdx, B1, Bn...>
139  : std::conditional<B1::value != false, std::integral_constant<std::size_t, CurIdx>,
140  disjunction_idx_impl<CurIdx + 1u, Bn...>>::type {
141 };
142 
143 template <class... Bs>
144 struct disjunction_idx : disjunction_idx_impl<0u, Bs...> {
145 };
146 
147 // std::index_sequence and std::make_index_sequence implementation for C++11. These are available
148 // in the std library in C++14. Implementation taken from:
149 // http://stackoverflow.com/questions/17424477/implementation-c14-make-integer-sequence
150 template <std::size_t... Ints>
151 struct index_sequence {
152  using type = index_sequence;
153  using value_type = std::size_t;
154  static constexpr std::size_t size() noexcept
155  {
156  return sizeof...(Ints);
157  }
158 };
159 
160 template <class Sequence1, class Sequence2>
161 struct merge_and_renumber;
162 
163 template <std::size_t... I1, std::size_t... I2>
164 struct merge_and_renumber<index_sequence<I1...>, index_sequence<I2...>>
165  : index_sequence<I1..., (sizeof...(I1) + I2)...> {
166 };
167 
168 template <std::size_t N>
169 struct make_index_sequence
170  : merge_and_renumber<typename make_index_sequence<N / 2>::type, typename make_index_sequence<N - N / 2>::type> {
171 };
172 
173 template <>
174 struct make_index_sequence<0> : index_sequence<> {
175 };
176 
177 template <>
178 struct make_index_sequence<1> : index_sequence<0> {
179 };
180 
181 template <typename T, typename F, std::size_t... Is>
182 void apply_to_each_item(T &&t, const F &f, index_sequence<Is...>)
183 {
184  (void)std::initializer_list<int>{0, (void(f(std::get<Is>(std::forward<T>(t)))), 0)...};
185 }
186 
187 // Tuple for_each(). Execute the functor f on each element of the input Tuple.
188 // https://isocpp.org/blog/2015/01/for-each-arg-eric-niebler
189 // https://www.reddit.com/r/cpp/comments/2tffv3/for_each_argumentsean_parent/
190 // https://www.reddit.com/r/cpp/comments/33b06v/for_each_in_tuple/
191 template <class Tuple, class F>
192 void tuple_for_each(Tuple &&t, const F &f)
193 {
194  apply_to_each_item(std::forward<Tuple>(t), f,
195  make_index_sequence<std::tuple_size<typename std::decay<Tuple>::type>::value>{});
196 }
197 
198 // Some handy aliases.
199 template <typename T>
200 using uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
201 
202 template <typename T>
203 using decay_t = typename std::decay<T>::type;
204 
205 template <typename T>
206 using unref_t = typename std::remove_reference<T>::type;
207 
208 template <typename T>
209 using addlref_t = typename std::add_lvalue_reference<T>::type;
210 
211 template <bool B, typename T = void>
212 using enable_if_t = typename std::enable_if<B, T>::type;
213 
214 template <typename T>
215 using is_nonconst_rvalue_ref
216  = std::integral_constant<bool,
217  conjunction<std::is_rvalue_reference<T>, negation<std::is_const<unref_t<T>>>>::value>;
218 
219 // The type resulting from the addition of T and U.
220 template <typename T, typename U>
221 using add_t = decltype(std::declval<const T &>() + std::declval<const U &>());
222 }
223 
225 
232 template <typename T, typename U = T>
234 {
235  static const bool implementation_defined = is_detected<add_t, T, U>::value;
236 
237 public:
239  static const bool value = implementation_defined;
240 };
241 
242 template <typename T, typename U>
243 const bool is_addable<T, U>::value;
244 
246 
253 template <typename T, typename U = T>
255 {
256  template <typename T1, typename U1>
257  using ip_add_t = decltype(std::declval<T1 &>() += std::declval<const U1 &>());
258  static const bool implementation_defined = is_detected<ip_add_t, T, U>::value;
259 
260 public:
262  static const bool value = implementation_defined;
263 };
264 
265 template <typename T, typename U>
267 
269 
276 template <typename T, typename U = T>
278 {
279  template <typename T1, typename U1>
280  using sub_t = decltype(std::declval<const T1 &>() - std::declval<const U1 &>());
281  static const bool implementation_defined = is_detected<sub_t, T, U>::value;
282 
283 public:
285  static const bool value = implementation_defined;
286 };
287 
288 template <typename T, typename U>
290 
292 
299 template <typename T, typename U = T>
301 {
302  template <typename T1, typename U1>
303  using ip_sub_t = decltype(std::declval<T1 &>() -= std::declval<const U1 &>());
304  static const bool implementation_defined = is_detected<ip_sub_t, T, U>::value;
305 
306 public:
308  static const bool value = implementation_defined;
309 };
310 
311 template <typename T, typename U>
313 
314 inline namespace impl
315 {
316 
317 // Type resulting from the multiplication of T and U.
318 template <typename T, typename U>
319 using mul_t = decltype(std::declval<const T &>() * std::declval<const U &>());
320 }
321 
323 
330 template <typename T, typename U = T>
332 {
333  static const bool implementation_defined = is_detected<mul_t, T, U>::value;
334 
335 public:
337  static const bool value = implementation_defined;
338 };
339 
340 template <typename T, typename U>
342 
344 
351 template <typename T, typename U = T>
353 {
354  template <typename T1, typename U1>
355  using ip_mul_t = decltype(std::declval<T1 &>() *= std::declval<const U1 &>());
356  static const bool implementation_defined = is_detected<ip_mul_t, T, U>::value;
357 
358 public:
360  static const bool value = implementation_defined;
361 };
362 
363 template <typename T, typename U>
365 
367 
374 template <typename T, typename U = T>
376 {
377  template <typename T1, typename U1>
378  using div_t = decltype(std::declval<const T1 &>() / std::declval<const U1 &>());
379  static const bool implementation_defined = is_detected<div_t, T, U>::value;
380 
381 public:
383  static const bool value = implementation_defined;
384 };
385 
386 template <typename T, typename U>
387 const bool is_divisible<T, U>::value;
388 
390 
397 template <typename T, typename U = T>
399 {
400  template <typename T1, typename U1>
401  using ip_div_t = decltype(std::declval<T1 &>() /= std::declval<const U1 &>());
402  static const bool implementation_defined = is_detected<ip_div_t, T, U>::value;
403 
404 public:
406  static const bool value = implementation_defined;
407 };
408 
409 template <typename T, typename U>
411 
413 
420 template <typename T, typename U = T>
422 {
423  template <typename T1, typename U1>
424  using ls_t = decltype(std::declval<const T1 &>() << std::declval<const U1 &>());
425  static const bool implementation_defined = is_detected<ls_t, T, U>::value;
426 
427 public:
429  static const bool value = implementation_defined;
430 };
431 
432 template <typename T, typename U>
433 const bool has_left_shift<T, U>::value;
434 
436 
443 template <typename T, typename U = T>
445 {
446  template <typename T1, typename U1>
447  using rs_t = decltype(std::declval<const T1 &>() >> std::declval<const U1 &>());
448  static const bool implementation_defined = is_detected<rs_t, T, U>::value;
449 
450 public:
452  static const bool value = implementation_defined;
453 };
454 
455 template <typename T, typename U>
457 
459 
466 template <typename T, typename U = T>
468 {
469  template <typename T1, typename U1>
470  using ls_t = decltype(std::declval<T1 &>() <<= std::declval<const U1 &>());
471  static const bool implementation_defined = is_detected<ls_t, T, U>::value;
472 
473 public:
475  static const bool value = implementation_defined;
476 };
477 
478 template <typename T, typename U>
480 
482 
489 template <typename T, typename U = T>
491 {
492  template <typename T1, typename U1>
493  using rs_t = decltype(std::declval<T1 &>() >>= std::declval<const U1 &>());
494  static const bool implementation_defined = is_detected<rs_t, T, U>::value;
495 
496 public:
498  static const bool value = implementation_defined;
499 };
500 
501 template <typename T, typename U>
503 
505 
510 template <typename T, typename U = T>
512 {
513  template <typename T1, typename U1>
514  using eq_t = decltype(std::declval<const T1 &>() == std::declval<const U1 &>());
515  template <typename T1, typename U1>
516  using ineq_t = decltype(std::declval<const T1 &>() != std::declval<const U1 &>());
517  static const bool implementation_defined = conjunction<std::is_convertible<detected_t<eq_t, T, U>, bool>,
518  std::is_convertible<detected_t<ineq_t, T, U>, bool>>::value;
519 
520 public:
522  static const bool value = implementation_defined;
523 };
524 
525 // Static init.
526 template <typename T, typename U>
528 
530 
535 template <typename T, typename U = T>
537 {
538  template <typename T1, typename U1>
539  using lt_t = decltype(std::declval<const T1 &>() < std::declval<const U1 &>());
540  static const bool implementation_defined = std::is_convertible<detected_t<lt_t, T, U>, bool>::value;
541 
542 public:
544  static const bool value = implementation_defined;
545 };
546 
547 // Static init.
548 template <typename T, typename U>
550 
552 
558 template <typename T, typename U = T>
560 {
561  template <typename T1, typename U1>
562  using gt_t = decltype(std::declval<const T1 &>() > std::declval<const U1 &>());
563  static const bool implementation_defined = std::is_convertible<detected_t<gt_t, T, U>, bool>::value;
564 
565 public:
567  static const bool value = implementation_defined;
568 };
569 
570 // Static init.
571 template <typename T, typename U>
573 
575 
582 template <typename T, typename = void>
585  static const bool value = true;
586 };
587 
588 // Static init.
589 template <typename T, typename Enable>
591 
593 
603 template <typename T>
605 private:
606  // NOTE: here we do not require copy assignability as in our containers we always implement
607  // copy-assign as copy-construct + move for exception safety reasons.
608  static const bool implementation_defined
609  = conjunction<std::is_default_constructible<T>, std::is_copy_constructible<T>,
610  disjunction<negation<enable_noexcept_checks<T>>,
611  conjunction<std::is_nothrow_destructible<T>,
612 #if defined(PIRANHA_COMPILER_IS_INTEL)
613  // The Intel compiler has troubles with the noexcept versions of these two
614  // type traits.
615  std::is_move_constructible<T>, std::is_move_assignable<T>
616 #else
617 
618  std::is_nothrow_move_constructible<T>, std::is_nothrow_move_assignable<T>
619 #endif
620  >>>::value;
621 
622 public:
624  static const bool value = implementation_defined;
625 };
626 
627 template <typename T>
629 
631 
639 template <typename T>
641 {
642  template <typename T1>
643  using ostreamable_t = decltype(std::declval<std::ostream &>() << std::declval<const T1 &>());
644  static const bool implementation_defined = std::is_same<detected_t<ostreamable_t, T>, std::ostream &>::value;
645 
646 public:
648  static const bool value = implementation_defined;
649 };
650 
651 template <typename T>
652 const bool is_ostreamable<T>::value;
653 
655 
664 // NOTE: the standard includes among function objects also function pointers. It would be nice in the future
665 // to exactly map the standard definition of callable into this type trait. See, e.g.:
666 // http://stackoverflow.com/questions/19278424/what-is-a-callable-object-in-c
667 template <typename T, typename ReturnType, typename... Args>
669 {
670  template <typename T1, typename... Args1>
671  using ret_t = decltype(std::declval<T1 &>()(std::declval<Args1>()...));
672  static const bool implementation_defined
673  = conjunction<std::is_class<T>, std::is_same<detected_t<ret_t, T, Args...>, ReturnType>>::value;
674 
675 public:
677  static const bool value = implementation_defined;
678 };
679 
680 template <typename T, typename ReturnType, typename... Args>
681 const bool is_function_object<T, ReturnType, Args...>::value;
682 
684 
689 template <typename T, typename U>
691 {
692  // NOTE: use addlref_t to avoid forming a ref to void.
693  static const bool implementation_defined
694  = conjunction<is_function_object<const T, std::size_t, const addlref_t<U>>, is_container_element<T>>::value;
695 
696 public:
698  static const bool value = implementation_defined;
699 };
700 
701 template <typename T, typename U>
703 
705 
710 template <typename T, typename U>
712 {
713  static const bool implementation_defined
714  = conjunction<is_function_object<const T, bool, const addlref_t<U>, const addlref_t<U>>,
716 
717 public:
719  static const bool value = implementation_defined;
720 };
721 
722 template <typename T, typename U>
724 
726 
736 // NOTE: when we remove the is_container_element check we might need to make sure that std::hash is def ctible,
737 // depending on how we use it. Check.
738 template <typename T>
740 {
741  static const bool implementation_defined = is_hash_function_object<std::hash<uncvref_t<T>>, T>::value;
742 
743 public:
745  static const bool value = implementation_defined;
746 };
747 
748 template <typename T>
749 const bool is_hashable<T>::value;
750 }
751 
753 
769 #define PIRANHA_DECLARE_HAS_TYPEDEF(type_name) \
770  template <typename PIRANHA_DECLARE_HAS_TYPEDEF_ARGUMENT> \
771  class has_typedef_##type_name \
772  { \
773  using Td_ = piranha::uncvref_t<PIRANHA_DECLARE_HAS_TYPEDEF_ARGUMENT>; \
774  template <typename U> \
775  using type_t = typename U::type_name; \
776  \
777  public: \
778  static const bool value = piranha::is_detected<type_t, Td_>::value; \
779  }
780 
782 
786 #define PIRANHA_TT_CHECK(tt, ...) \
787  static_assert(tt<__VA_ARGS__>::value, "type trait check failure -> " #tt "<" #__VA_ARGS__ ">")
788 
789 namespace piranha
790 {
791 
792 namespace detail
793 {
794 
795 template <typename T, typename... Args>
796 struct min_int_impl {
797  using next = typename min_int_impl<Args...>::type;
798  static_assert((std::is_unsigned<T>::value && std::is_unsigned<next>::value && std::is_integral<next>::value)
799  || (std::is_signed<T>::value && std::is_signed<next>::value && std::is_integral<next>::value),
800  "The type trait's arguments must all be (un)signed integers.");
801  using type = typename std::conditional<(std::numeric_limits<T>::max() < std::numeric_limits<next>::max()
802  && (std::is_unsigned<T>::value
803  || std::numeric_limits<T>::min() > std::numeric_limits<next>::min())),
804  T, next>::type;
805 };
806 
807 template <typename T>
808 struct min_int_impl<T> {
809  static_assert(std::is_integral<T>::value, "The type trait's arguments must all be (un)signed integers.");
810  using type = T;
811 };
812 
813 template <typename T, typename... Args>
814 struct max_int_impl {
815  using next = typename max_int_impl<Args...>::type;
816  static_assert((std::is_unsigned<T>::value && std::is_unsigned<next>::value && std::is_integral<next>::value)
817  || (std::is_signed<T>::value && std::is_signed<next>::value && std::is_integral<next>::value),
818  "The type trait's arguments must all be (un)signed integers.");
819  using type = typename std::conditional<(std::numeric_limits<T>::max() > std::numeric_limits<next>::max()
820  && (std::is_unsigned<T>::value
821  || std::numeric_limits<T>::min() < std::numeric_limits<next>::min())),
822  T, next>::type;
823 };
824 
825 template <typename T>
826 struct max_int_impl<T> {
827  static_assert(std::is_integral<T>::value, "The type trait's arguments must all be (un)signed integers.");
828  using type = T;
829 };
830 }
831 
833 
837 template <typename T, typename... Args>
838 using min_int = typename detail::min_int_impl<T, Args...>::type;
839 
841 
845 template <typename T, typename... Args>
846 using max_int = typename detail::max_int_impl<T, Args...>::type;
847 
848 inline namespace impl
849 {
850 
851 #if !defined(PIRANHA_DOXYGEN_INVOKED)
852 
853 // Detect the availability of std::iterator_traits on type It, plus a couple more requisites from the
854 // iterator concept.
855 // NOTE: this needs also the is_swappable type trait, but this seems to be difficult to implement in C++11. Mostly
856 // because it seems that:
857 // - we cannot detect a specialised std::swap (so if it is not specialised, it will pick the default implementation
858 // which could fail in the implementation without giving hints in the prototype),
859 // - it's tricky to fulfill the requirement that swap has to be called unqualified (cannot use 'using std::swap' within
860 // a decltype() SFINAE, might be doable with automatic return type deduction for regular functions in C++14?).
861 template <typename It>
862 struct has_iterator_traits {
863  using it_tags = std::tuple<std::input_iterator_tag, std::output_iterator_tag, std::forward_iterator_tag,
864  std::bidirectional_iterator_tag, std::random_access_iterator_tag>;
865  PIRANHA_DECLARE_HAS_TYPEDEF(difference_type);
866  PIRANHA_DECLARE_HAS_TYPEDEF(value_type);
868  PIRANHA_DECLARE_HAS_TYPEDEF(reference);
869  PIRANHA_DECLARE_HAS_TYPEDEF(iterator_category);
870  using i_traits = std::iterator_traits<It>;
871  static const bool value
872  = conjunction<has_typedef_reference<i_traits>, has_typedef_value_type<i_traits>, has_typedef_pointer<i_traits>,
873  has_typedef_difference_type<i_traits>, has_typedef_iterator_category<i_traits>,
874  std::is_copy_constructible<It>, std::is_copy_assignable<It>, std::is_destructible<It>>::value;
875 };
876 
877 // TMP to check if a type is convertible to a type in the tuple.
878 template <typename, typename>
879 struct convertible_type_in_tuple {
880 };
881 
882 template <typename T, typename... Args>
883 struct convertible_type_in_tuple<T, std::tuple<Args...>> {
884  static const bool value = disjunction<std::is_convertible<T, Args>...>::value;
885 };
886 
887 #endif
888 }
889 
890 // NOTE: this and the other iterator type traits seem to work ok in practice, but probably
891 // there are some corner cases which are not handled fully according to the standard. After spending
892 // some time on these, it seems like a nontrivial amount of work would be needed to refine them
893 // further, so let's just leave them like this for now.
894 
896 
900 template <typename T>
902 {
903  template <typename U>
904  using deref_t = decltype(*std::declval<U &>());
905  template <typename U>
906  using inc_t = decltype(++std::declval<U &>());
907  template <typename U>
908  using it_cat = typename std::iterator_traits<U>::iterator_category;
909  using uT = uncvref_t<T>;
910  static const bool implementation_defined = conjunction<
911  // NOTE: here the correct condition is the commented one, as opposed to the first one appearing. However, it
912  // seems like there are inconsistencies between the commented condition and the definition of many output
913  // iterators in the standard library:
914  //
915  // http://stackoverflow.com/questions/23567244/apparent-inconsistency-in-iterator-requirements
916  //
917  // Until this is clarified, it is probably better to keep this workaround.
918  /* std::is_same<typename std::iterator_traits<T>::reference,decltype(*std::declval<T &>())>::value */
919  is_detected<deref_t, uT>, std::is_same<detected_t<inc_t, uT>, addlref_t<uT>>, has_iterator_traits<uT>,
920  // NOTE: here we used to have type_in_tuple, but it turns out Boost.iterator defines its own set of tags derived
921  // from the standard ones. Hence, check that the category can be converted to one of the standard categories.
922  // This should not change anything for std iterators, and just enable support for Boost ones.
923  convertible_type_in_tuple<detected_t<it_cat, uT>, typename has_iterator_traits<uT>::it_tags>>::value;
924 
925 public:
927  static const bool value = implementation_defined;
928 };
929 
930 template <typename T>
931 const bool is_iterator<T>::value;
932 
933 inline namespace impl
934 {
935 
936 #if !defined(PIRANHA_DOXYGEN_INVOKED)
937 
938 // The purpose of these bits is to check whether U correctly implements the arrow operator.
939 // A correct implementation will return a pointer, after potentially calling
940 // the operator recursively as many times as needed. See:
941 // http://stackoverflow.com/questions/10677804/how-arrow-operator-overloading-works-internally-in-c
942 template <typename U, typename = void>
943 struct arrow_operator_type {
944 };
945 
946 // This represents the terminator of the recursion.
947 template <typename U>
948 struct arrow_operator_type<U *> {
949  using type = U *;
950 };
951 
952 // Handy alias.
953 template <typename U>
954 using arrow_operator_t = typename arrow_operator_type<U>::type;
955 
956 // These bits invoke arrow_operator_type recursively, until a pointer is found.
957 template <typename U>
958 using rec_arrow_op_t = arrow_operator_t<decltype(std::declval<U &>().operator->())>;
959 
960 template <typename U>
961 struct arrow_operator_type<U, enable_if_t<is_detected<rec_arrow_op_t, U>::value>> {
962  using type = rec_arrow_op_t<U>;
963 };
964 
965 // NOTE: need the SFINAE wrapper here and below because we need to soft-error
966 // out in case the types in std::iterator_traits are not defined.
967 template <typename T, typename = void>
968 struct is_input_iterator_impl : std::false_type {
969 };
970 
971 template <typename T>
972 struct is_input_iterator_impl<
973  T,
974  enable_if_t<conjunction<
975  is_iterator<T>, is_equality_comparable<T>,
976  std::is_convertible<decltype(*std::declval<T &>()), typename std::iterator_traits<T>::value_type>,
977  std::is_same<decltype(++std::declval<T &>()), T &>,
978  std::is_same<decltype((void)std::declval<T &>()++), decltype((void)++std::declval<T &>())>,
979  std::is_convertible<decltype(*std::declval<T &>()++), typename std::iterator_traits<T>::value_type>,
980  // NOTE: here we know that the arrow op has to return a pointer, if
981  // implemented correctly, and that the syntax it->m must be
982  // equivalent to (*it).m. This means that, barring differences in
983  // reference qualifications, it-> and *it must return the same thing.
984  std::is_same<unref_t<decltype(*std::declval<arrow_operator_t<T>>())>, unref_t<decltype(*std::declval<T &>())>>,
985  // NOTE: here the usage of is_convertible guarantees we catch both
986  // iterators higher in the type hierarchy and the Boost versions of
987  // standard iterators as well.
988  std::is_convertible<typename std::iterator_traits<T>::iterator_category, std::input_iterator_tag>>::value>>
989  : std::true_type {
990 };
991 
992 #endif
993 }
994 
996 
1000 template <typename T>
1002 {
1003  static const bool implementation_defined = is_input_iterator_impl<uncvref_t<T>>::value;
1004 
1005 public:
1007  static const bool value = implementation_defined;
1008 };
1009 
1010 template <typename T>
1011 const bool is_input_iterator<T>::value;
1012 
1013 inline namespace impl
1014 {
1015 
1016 template <typename T, typename = void>
1017 struct is_forward_iterator_impl : std::false_type {
1018 };
1019 
1020 template <typename T>
1021 struct is_forward_iterator_impl<
1022  T, enable_if_t<conjunction<
1023  is_input_iterator<T>, std::is_default_constructible<T>,
1024  disjunction<std::is_same<typename std::iterator_traits<T>::value_type &,
1025  typename std::iterator_traits<T>::reference>,
1026  std::is_same<typename std::iterator_traits<T>::value_type const &,
1027  typename std::iterator_traits<T>::reference>>,
1028  std::is_convertible<decltype(std::declval<T &>()++), const T &>,
1029  std::is_same<decltype(*std::declval<T &>()++), typename std::iterator_traits<T>::reference>,
1030  std::is_convertible<typename std::iterator_traits<T>::iterator_category, std::forward_iterator_tag>>::value>>
1031  : std::true_type {
1032 };
1033 }
1034 
1036 
1040 template <typename T>
1042 {
1043  static const bool implementation_defined = is_forward_iterator_impl<uncvref_t<T>>::value;
1044 
1045 public:
1047  static const bool value = implementation_defined;
1048 };
1049 
1050 template <typename T>
1052 
1053 namespace detail
1054 {
1055 
1056 #if !defined(PIRANHA_DOXYGEN_INVOKED)
1057 template <typename T>
1058 constexpr T safe_abs_sint_impl(T cur_p = T(1), T cur_n = T(-1))
1059 {
1060  return (cur_p > std::numeric_limits<T>::max() / T(2) || cur_n < std::numeric_limits<T>::min() / T(2))
1061  ? cur_p
1062  : safe_abs_sint_impl(static_cast<T>(cur_p * 2), static_cast<T>(cur_n * 2));
1063 }
1064 
1065 // Determine, for the signed integer T, a value n, power of 2, such that it is safe to take -n.
1066 template <typename T>
1067 struct safe_abs_sint {
1068  static_assert(std::is_integral<T>::value && std::is_signed<T>::value, "T must be a signed integral type.");
1069  static const T value = safe_abs_sint_impl<T>();
1070 };
1071 
1072 template <typename T>
1073 const T safe_abs_sint<T>::value;
1074 #endif
1075 
1076 // A simple true type-trait that can be used inside enable_if with T a decltype() expression
1077 // subject to SFINAE. It is similar to the proposed void_t type (in C++14, maybe?).
1078 template <typename T>
1079 struct true_tt {
1080  static const bool value = true;
1081 };
1082 
1083 template <typename T>
1084 const bool true_tt<T>::value;
1085 }
1086 
1088 
1094 template <typename T>
1096 {
1097  template <typename U>
1098  using begin_t = decltype(std::begin(std::declval<U &>()));
1099  template <typename U>
1100  using end_t = decltype(std::end(std::declval<U &>()));
1101  static const bool implementation_defined
1102  = conjunction<is_input_iterator<detected_t<begin_t, T>>, is_input_iterator<detected_t<end_t, T>>,
1103  std::is_same<detected_t<begin_t, T>, detected_t<end_t, T>>>::value;
1104 
1105 public:
1107  static const bool value = implementation_defined;
1108 };
1109 
1110 template <typename T>
1112 
1114 
1118 template <typename T>
1120 {
1121  static const bool implementation_defined
1122  = disjunction<std::is_same<T, void>,
1123  conjunction<std::is_destructible<T>,
1124  disjunction<std::is_copy_constructible<T>, std::is_move_constructible<T>>>>::value;
1125 
1126 public:
1128  static const bool value = implementation_defined;
1129 };
1130 
1131 template <typename T>
1132 const bool is_returnable<T>::value;
1133 
1135 
1146 template <typename T, typename = void>
1148 {
1149  PIRANHA_TT_CHECK(is_multipliable, uncvref_t<T>);
1150 
1151 public:
1153  static const bool value = true;
1154 };
1155 
1156 template <typename T, typename Enable>
1158 
1159 inline namespace impl
1160 {
1161 
1162 // NOTE: no conj/disj as we are not using ::value members from numeric_limits.
1163 template <typename T>
1164 using fp_zero_is_absorbing_enabler = enable_if_t<std::is_floating_point<uncvref_t<T>>::value
1165  && (std::numeric_limits<uncvref_t<T>>::has_quiet_NaN
1166  || std::numeric_limits<uncvref_t<T>>::has_signaling_NaN)>;
1167 }
1168 
1170 
1178 template <typename T>
1179 class zero_is_absorbing<T, fp_zero_is_absorbing_enabler<T>>
1180 {
1181  PIRANHA_TT_CHECK(is_multipliable, uncvref_t<T>);
1182 
1183 public:
1185  static const bool value = false;
1186 };
1187 
1188 template <typename T>
1190 }
1191 
1192 #endif
Greater-than-comparable type trait.
Function object type trait.
Subtractable type trait.
Equality-comparable type trait.
static const bool value
Value of the type trait.
In-place divisible type trait.
static const bool value
Value of the type trait.
Hashable type trait.
static const bool value
Value of the type trait.
Left-shift type trait.
In-place addable type trait.
static const bool value
Value of the type trait.
static const bool value
Value of the type trait.
#define PIRANHA_DECLARE_HAS_TYPEDEF(type_name)
Macro to test if class has type definition.
static const bool value
Value of the type trait.
STL namespace.
static const bool value
Value of the type trait.
static const bool value
Value of the type trait.
static const bool value
Value of the type trait.
Right-shift type trait.
static const bool value
Value of the type trait.
Forward iterator type trait.
static const bool value
Value of the type trait.
Input iterator type trait.
typename detail::min_int_impl< T, Args... >::type min_int
Detect narrowest integer type.
In-place subtractable type trait.
static const bool value
Value of the type trait.
static const bool value
Value of the type trait.
Multipliable type trait.
static const bool value
Default value of the type trait.
Iterator type trait.
Detect if zero is a multiplicative absorber.
static const bool value
Value of the type trait.
Detect if type can be returned from a function.
In-place right-shift type trait.
static const bool value
Value of the type trait.
In-place left-shift type trait.
Enable noexcept checks.
static const bool value
Value of the type trait.
Divisible type trait.
static const bool value
Value of the type trait.
static const bool value
Value of the type trait.
In-place multipliable type trait.
Root piranha namespace.
Definition: array_key.hpp:52
static const bool value
Value of the type trait.
Less-than-comparable type trait.
static const bool value
Value of the type trait.
static const bool value
Value of the type trait.
Detect the availability of std::begin() and std::end().
typename detail::max_int_impl< T, Args... >::type max_int
Detect widest integer type.
static const bool value
Value of the type trait.
Type trait for classes that can be output-streamed.
Type trait to detect equality function objects.
static const bool value
Value of the type trait.
static const bool value
Value of the type trait.
Type trait to detect hash function objects.
Type trait for well-behaved container elements.
static const bool value
Value of the type trait.
static const bool value
Value of the type trait.
static const bool value
Value of the type trait.
Addable type trait.