1//
2// immer: immutable data structures for C++
3// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
4//
5// This software is distributed under the Boost Software License, Version 1.0.
6// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
7//
8
9#pragma once
10
11#include <algorithm>
12#include <iterator>
13#include <memory>
14#include <type_traits>
15#include <utility>
16
17namespace immer {
18namespace detail {
19
20template <typename... Ts>
21struct make_void { using type = void; };
22
23template <typename... Ts>
24using void_t = typename make_void<Ts...>::type;
25
26template <typename T, typename = void>
27struct is_dereferenceable : std::false_type {};
28
29template <typename T>
30struct is_dereferenceable<T, void_t<decltype(*(std::declval<T&>()))>> :
31 std::true_type {};
32
33template <typename T>
34constexpr bool is_dereferenceable_v = is_dereferenceable<T>::value;
35
36template<typename T, typename U=T, typename = void>
37struct is_equality_comparable : std::false_type {};
38
39template<typename T, typename U>
40struct is_equality_comparable
41<T, U, std::enable_if_t
42 <std::is_same
43 <bool, decltype(std::declval<T&>() == std::declval<U&>())>::value>> :
44 std::true_type {};
45
46template<typename T, typename U=T>
47constexpr bool is_equality_comparable_v = is_equality_comparable<T, U>::value;
48
49template<typename T, typename U=T, typename = void>
50struct is_inequality_comparable : std::false_type {};
51
52template<typename T, typename U>
53struct is_inequality_comparable
54<T, U, std::enable_if_t
55 <std::is_same
56 <bool, decltype(std::declval<T&>() != std::declval<U&>())>::value>> :
57 std::true_type {};
58
59template<typename T, typename U=T>
60constexpr bool is_inequality_comparable_v =
61 is_inequality_comparable<T, U>::value;
62
63template <typename T, typename = void>
64struct is_preincrementable : std::false_type {};
65
66template <typename T>
67struct is_preincrementable
68<T, std::enable_if_t
69 <std::is_same<T&, decltype(++(std::declval<T&>()))>::value>> :
70 std::true_type {};
71
72template <typename T>
73constexpr bool is_preincrementable_v = is_preincrementable<T>::value;
74
75template <typename T, typename U=T, typename = void>
76struct is_subtractable : std::false_type {};
77
78template <typename T, typename U>
79struct is_subtractable
80<T, U, void_t<decltype(std::declval<T&>() - std::declval<U&>())>> :
81 std::true_type {};
82
83template <typename T, typename U = T>
84constexpr bool is_subtractable_v = is_subtractable<T, U>::value;
85
86namespace swappable {
87
88using std::swap;
89
90template <typename T, typename U, typename = void>
91struct with : std::false_type {};
92
93// Does not account for non-referenceable types
94template <typename T, typename U>
95struct with
96<T, U, void_t<decltype(swap(std::declval<T&>(), std::declval<U&>())),
97 decltype(swap(std::declval<U&>(), std::declval<T&>()))>> :
98 std::true_type {};
99
100}
101
102template<typename T, typename U>
103using is_swappable_with = swappable::with<T, U>;
104
105template<typename T>
106using is_swappable = is_swappable_with<T, T>;
107
108template <typename T>
109constexpr bool is_swappable_v = is_swappable_with<T&, T&>::value;
110
111template <typename T, typename = void>
112struct is_iterator : std::false_type {};
113
114// See http://en.cppreference.com/w/cpp/concept/Iterator
115template <typename T>
116struct is_iterator
117<T, void_t
118 <std::enable_if_t
119 <is_preincrementable_v<T>
120 && is_dereferenceable_v<T>
121 // accounts for non-referenceable types
122 && std::is_copy_constructible<T>::value
123 && std::is_copy_assignable<T>::value
124 && std::is_destructible<T>::value
125 && is_swappable_v<T>>,
126 typename std::iterator_traits<T>::value_type,
127 typename std::iterator_traits<T>::difference_type,
128 typename std::iterator_traits<T>::reference,
129 typename std::iterator_traits<T>::pointer,
130 typename std::iterator_traits<T>::iterator_category>> :
131 std::true_type {};
132
133template<typename T>
134constexpr bool is_iterator_v = is_iterator<T>::value;
135
136template<typename T, typename U, typename = void>
137struct compatible_sentinel : std::false_type {};
138
139template<typename T, typename U>
140struct compatible_sentinel
141<T, U, std::enable_if_t
142 <is_iterator_v<T>
143 && is_equality_comparable_v<T, U>
144 && is_inequality_comparable_v<T, U>>> :
145 std::true_type {};
146
147template<typename T, typename U>
148constexpr bool compatible_sentinel_v = compatible_sentinel<T,U>::value;
149
150template<typename T, typename = void>
151struct is_forward_iterator : std::false_type {};
152
153template<typename T>
154struct is_forward_iterator
155<T, std::enable_if_t
156 <is_iterator_v<T> &&
157 std::is_base_of
158 <std::forward_iterator_tag,
159 typename std::iterator_traits<T>::iterator_category>::value>> :
160 std::true_type {};
161
162template<typename T>
163constexpr bool is_forward_iterator_v = is_forward_iterator<T>::value;
164
165template<typename T, typename U, typename = void>
166struct std_distance_supports : std::false_type {};
167
168template<typename T, typename U>
169struct std_distance_supports
170<T, U, void_t<decltype(std::distance(std::declval<T>(), std::declval<U>()))>> :
171 std::true_type {};
172
173template<typename T, typename U>
174constexpr bool std_distance_supports_v = std_distance_supports<T, U>::value;
175
176template<typename T, typename U, typename V, typename = void>
177struct std_uninitialized_copy_supports : std::false_type {};
178
179template<typename T, typename U, typename V>
180struct std_uninitialized_copy_supports
181<T, U, V, void_t<decltype(std::uninitialized_copy(std::declval<T>(),
182 std::declval<U>(),
183 std::declval<V>()))>> :
184 std::true_type {};
185
186template<typename T, typename U, typename V>
187constexpr bool std_uninitialized_copy_supports_v =
188 std_uninitialized_copy_supports<T, U, V>::value;
189
190}
191}
192