]>
Commit | Line | Data |
---|---|---|
b32b8144 | 1 | // Copyright Louis Dionne 2013-2017 |
7c673cae FG |
2 | // Distributed under the Boost Software License, Version 1.0. |
3 | // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) | |
4 | ||
5 | #ifndef BOOST_HANA_TEST_LAWS_BASE_HPP | |
6 | #define BOOST_HANA_TEST_LAWS_BASE_HPP | |
7 | ||
8 | #include <boost/hana/and.hpp> | |
9 | #include <boost/hana/bool.hpp> | |
10 | #include <boost/hana/core/when.hpp> | |
11 | #include <boost/hana/detail/wrong.hpp> | |
12 | #include <boost/hana/equal.hpp> | |
13 | #include <boost/hana/eval_if.hpp> | |
14 | #include <boost/hana/for_each.hpp> | |
15 | #include <boost/hana/functional/compose.hpp> | |
16 | #include <boost/hana/functional/infix.hpp> | |
17 | #include <boost/hana/functional/partial.hpp> | |
18 | #include <boost/hana/fwd/concept/integral_constant.hpp> | |
19 | #include <boost/hana/fwd/core/to.hpp> | |
20 | #include <boost/hana/fwd/less.hpp> | |
21 | #include <boost/hana/not.hpp> | |
22 | #include <boost/hana/or.hpp> | |
23 | #include <boost/hana/tuple.hpp> | |
24 | ||
25 | #include <support/tracked.hpp> | |
26 | ||
27 | #include <type_traits> | |
28 | #include <utility> | |
29 | ||
30 | ||
31 | namespace boost { namespace hana { | |
32 | ////////////////////////////////////////////////////////////////////////// | |
33 | // Misc | |
34 | ////////////////////////////////////////////////////////////////////////// | |
35 | namespace test { | |
36 | struct laws; | |
37 | ||
38 | template <int i> | |
39 | struct for_each_n_t { | |
40 | static_assert(i > 0, "can't use for_each_n with i < 0"); | |
41 | ||
42 | template <typename Xs, typename F> | |
43 | constexpr auto operator()(Xs const& xs, F const& f) const { | |
44 | hana::for_each(xs, | |
45 | hana::compose( | |
46 | hana::partial(for_each_n_t<i - 1>{}, xs), | |
47 | hana::partial(hana::partial, f) | |
48 | ) | |
49 | ); | |
50 | } | |
51 | }; | |
52 | ||
53 | template <> | |
54 | struct for_each_n_t<1> { | |
55 | template <typename Xs, typename F> | |
56 | constexpr auto operator()(Xs const& xs, F const& f) const { | |
57 | hana::for_each(xs, f); | |
58 | } | |
59 | }; | |
60 | ||
61 | template <int i> | |
62 | constexpr for_each_n_t<i> for_each_n{}; | |
63 | ||
64 | auto foreach = hana::for_each; | |
65 | constexpr auto foreach3 = for_each_n<3>; | |
66 | constexpr auto foreach2 = for_each_n<2>; | |
67 | ||
68 | struct implies_t { | |
69 | template <typename P, typename Q> | |
70 | constexpr decltype(auto) operator()(P&& p, Q&& q) const { | |
71 | return hana::or_(hana::not_(static_cast<P&&>(p)), | |
72 | static_cast<Q&&>(q)); | |
73 | } | |
74 | }; | |
75 | constexpr auto implies = hana::infix(implies_t{}); | |
76 | ||
77 | struct iff_t { | |
78 | template <typename P, typename Q> | |
79 | constexpr decltype(auto) operator()(P&& p, Q&& q) const { | |
80 | return hana::and_(implies(p, q), implies(q, p)); | |
81 | } | |
82 | }; | |
83 | constexpr auto iff = hana::infix(iff_t{}); | |
84 | ||
85 | template <typename Cond, typename F> | |
86 | constexpr decltype(auto) only_when_(Cond cond, F f) { | |
87 | return hana::eval_if(cond, f, [](auto){ }); | |
88 | } | |
89 | ||
90 | // A type with a constructor that must not be instantiated. | |
91 | // This is to make sure we don't instantiate something else than | |
92 | // the copy-constructor of the elements inside a container when we | |
93 | // copy the container. | |
94 | struct trap_construct { | |
95 | trap_construct() = default; | |
96 | trap_construct(trap_construct const&) = default; | |
97 | trap_construct(trap_construct&) = default; | |
98 | trap_construct(trap_construct&&) = default; | |
99 | ||
100 | template <typename X> | |
101 | trap_construct(X&&) { | |
102 | static_assert(detail::wrong<X>{}, | |
103 | "this constructor must not be instantiated"); | |
104 | } | |
105 | }; | |
106 | ||
107 | // A move-only type. Useful for testing containers. | |
108 | struct move_only { | |
109 | move_only() = default; | |
110 | move_only(move_only const&) = delete; | |
111 | move_only(move_only&&) = default; | |
112 | }; | |
113 | ||
114 | ////////////////////////////////////////////////////////////////////// | |
115 | // InjectionResult | |
116 | ////////////////////////////////////////////////////////////////////// | |
117 | struct InjectionResult { }; | |
118 | ||
119 | template <int i, typename ...X> | |
120 | struct injection_result { | |
121 | using hana_tag = InjectionResult; | |
122 | static constexpr int injection_id = i; | |
123 | hana::tuple<X...> args; | |
124 | Tracked tracker; | |
125 | ||
126 | template <typename ...Y, typename = decltype(tuple<X...>{std::declval<Y>()...})> | |
127 | constexpr explicit injection_result(Y&& ...y) | |
128 | : args{static_cast<Y&&>(y)...}, tracker{i} | |
129 | { } | |
130 | }; | |
131 | ||
132 | //! A monotonic injective function. | |
133 | //! | |
134 | //! This is used in the unit tests, where we often just need a function | |
135 | //! which preserves equality and order, but which also satisfies the | |
136 | //! following law for all `Injection`s `f` and `g`: | |
137 | //! @code | |
138 | //! f(x) == g(x) if and only if f === g | |
139 | //! @endcode | |
140 | //! where `===` means _was created by the same call to `injection`_. | |
141 | //! This allows creating several such functions in the unit tests while | |
142 | //! conserving precious information such as the fact that | |
143 | //! `f(g(x)) != g(f(x))`. | |
144 | template <int i> | |
145 | struct _injection { | |
146 | template <typename ...X> | |
147 | constexpr auto operator()(X&& ...x) const { | |
148 | return injection_result<i, | |
149 | typename std::decay<X>::type... | |
150 | >{static_cast<X&&>(x)...}; | |
151 | } | |
152 | }; | |
153 | } // end namespace test | |
154 | ||
155 | template <> | |
156 | struct equal_impl<test::InjectionResult, test::InjectionResult> { | |
157 | template <typename X, typename Y> | |
158 | static constexpr auto apply(X x, Y y) { | |
159 | return hana::and_( | |
160 | hana::bool_c<X::injection_id == Y::injection_id>, | |
161 | hana::equal(x.args, y.args) | |
162 | ); | |
163 | } | |
164 | }; | |
165 | ||
166 | template <> | |
167 | struct less_impl<test::InjectionResult, test::InjectionResult> { | |
168 | template <typename X, typename Y> | |
169 | static constexpr auto apply(X x, Y y) { | |
170 | static_assert(X::injection_id == Y::injection_id, | |
171 | "can't order the result of two different injections"); | |
172 | return hana::less(x.args, y.args); | |
173 | } | |
174 | }; | |
175 | ||
176 | ||
177 | ////////////////////////////////////////////////////////////////////////// | |
178 | // Integer | |
179 | ////////////////////////////////////////////////////////////////////////// | |
180 | namespace test { | |
181 | enum class Policy : int { | |
182 | // One of those is mandatory | |
183 | Constant = 1 << 0 | |
184 | , Constexpr = 1 << 1 | |
185 | , Runtime = 1 << 2 | |
186 | ||
187 | // Those are optional | |
188 | , Tracked = 1 << 3 | |
189 | , Comparable = 1 << 4 | |
190 | , Orderable = 1 << 5 | |
191 | }; | |
192 | ||
193 | constexpr bool operator&&(Policy a, Policy b) { | |
194 | return static_cast<int>(a) && static_cast<int>(b); | |
195 | } | |
196 | ||
197 | constexpr bool operator&&(Policy a, bool b) { | |
198 | return static_cast<int>(a) && b; | |
199 | } | |
200 | ||
201 | constexpr bool operator&&(bool a, Policy b) { | |
202 | return a && static_cast<int>(b); | |
203 | } | |
204 | ||
205 | constexpr bool operator||(Policy a, Policy b) { | |
206 | return static_cast<int>(a) || static_cast<int>(b); | |
207 | } | |
208 | ||
209 | constexpr bool operator||(Policy a, bool b) { | |
210 | return static_cast<int>(a) || b; | |
211 | } | |
212 | ||
213 | constexpr bool operator||(bool a, Policy b) { | |
214 | return a || static_cast<int>(b); | |
215 | } | |
216 | ||
217 | constexpr bool operator!(Policy a) { | |
218 | return !static_cast<int>(a); | |
219 | } | |
220 | ||
221 | constexpr Policy operator|(Policy a, Policy b) { | |
222 | return static_cast<Policy>(static_cast<int>(a) | static_cast<int>(b)); | |
223 | } | |
224 | ||
225 | constexpr Policy operator&(Policy a, Policy b) { | |
226 | return static_cast<Policy>(static_cast<int>(a) & static_cast<int>(b)); | |
227 | } | |
228 | ||
229 | template <Policy policy, typename = void> | |
230 | struct Integer { }; | |
231 | ||
232 | template <Policy policy> | |
233 | struct Integer<policy, std::enable_if_t<!!(policy & Policy::Constant)>> { | |
234 | using value_type = int; | |
235 | }; | |
236 | ||
237 | template <int i, Policy policy, typename = void> | |
238 | struct integer { | |
239 | static_assert( | |
240 | !!(policy & (Policy::Constant | Policy::Constexpr | Policy::Runtime)) | |
241 | , "You must choose at least one of Constant, Constexpr and Runtime."); | |
242 | ||
243 | static constexpr int value = i; | |
244 | constexpr operator int() const { return value; } | |
245 | using hana_tag = Integer<policy>; | |
246 | Tracked tracker{i}; | |
247 | }; | |
248 | ||
249 | template <int i, Policy policy> | |
250 | struct integer <i, policy, std::enable_if_t<!!(policy & Policy::Constexpr)>> { | |
251 | static constexpr int value = i; | |
252 | constexpr operator int() const { return value; } | |
253 | using hana_tag = Integer<policy>; | |
254 | }; | |
255 | ||
256 | template <int i> | |
257 | struct eq : integer<i, Policy::Comparable | Policy::Runtime> { }; | |
258 | ||
259 | template <int i> | |
260 | struct ct_eq : integer<i, Policy::Comparable | Policy::Constant> { }; | |
261 | ||
262 | template <int i> | |
263 | struct cx_eq : integer<i, Policy::Comparable | Policy::Constexpr> { }; | |
264 | ||
265 | template <int i> | |
266 | struct ord : integer<i, Policy::Orderable | Policy::Runtime> { }; | |
267 | ||
268 | template <int i> | |
269 | struct ct_ord : integer<i, Policy::Orderable | Policy::Constant> { }; | |
270 | ||
271 | template <int i> | |
272 | struct cx_ord : integer<i, Policy::Orderable | Policy::Constexpr> { }; | |
273 | ||
274 | template <int i> | |
275 | struct _constant | |
276 | : integer<i, Policy::Constant | Policy::Comparable | Policy::Orderable> | |
277 | { }; | |
278 | } | |
279 | ||
280 | ////////////////////////////////////////////////////////////////////////// | |
281 | // Model of Constant/IntegralConstant | |
282 | ////////////////////////////////////////////////////////////////////////// | |
283 | template <test::Policy policy> | |
284 | struct IntegralConstant<test::Integer<policy>> { | |
285 | static constexpr bool value = static_cast<bool>(policy & test::Policy::Constant); | |
286 | }; | |
287 | ||
288 | template <test::Policy policy, typename C> | |
289 | struct to_impl<test::Integer<policy>, C, when< | |
290 | (policy & test::Policy::Constant) && | |
291 | hana::IntegralConstant<C>::value | |
292 | >> | |
293 | : embedding<is_embedded<typename C::value_type, int>::value> | |
294 | { | |
295 | template <typename N> | |
296 | static constexpr auto apply(N const&) { | |
297 | return test::integer<N::value, policy>{}; | |
298 | } | |
299 | }; | |
300 | ||
301 | ////////////////////////////////////////////////////////////////////////// | |
302 | // Model of Comparable | |
303 | ////////////////////////////////////////////////////////////////////////// | |
304 | template <test::Policy p1, test::Policy p2> | |
305 | struct equal_impl<test::Integer<p1>, test::Integer<p2>, when< | |
306 | // both Comparable or Orderable | |
307 | (p1 & (test::Policy::Comparable | test::Policy::Orderable)) && | |
308 | (p2 & (test::Policy::Comparable | test::Policy::Orderable)) && | |
309 | ||
310 | // one Constexpr and the other Constant, or both Constexpr | |
311 | (((p1 & test::Policy::Constant) && (p2 & test::Policy::Constexpr)) || | |
312 | ((p1 & test::Policy::Constexpr) && (p2 & test::Policy::Constant)) || | |
313 | ((p1 & test::Policy::Constexpr) && (p2 & test::Policy::Constexpr))) | |
314 | >> { | |
315 | template <typename X, typename Y> | |
316 | static constexpr bool apply(X const&, Y const&) | |
317 | { return X::value == Y::value; } | |
318 | }; | |
319 | ||
320 | template <test::Policy p1, test::Policy p2> | |
321 | struct equal_impl<test::Integer<p1>, test::Integer<p2>, when< | |
322 | // both Comparable or Orderable | |
323 | (p1 & (test::Policy::Comparable | test::Policy::Orderable)) && | |
324 | (p2 & (test::Policy::Comparable | test::Policy::Orderable)) && | |
325 | ||
326 | // either one is Runtime | |
327 | ((p1 & test::Policy::Runtime) || (p2 & test::Policy::Runtime)) | |
328 | >> { | |
329 | template <typename X, typename Y> | |
330 | static bool apply(X const&, Y const&) | |
331 | { return X::value == Y::value; } | |
332 | }; | |
333 | ||
334 | ||
335 | ////////////////////////////////////////////////////////////////////////// | |
336 | // Model of Orderable | |
337 | ////////////////////////////////////////////////////////////////////////// | |
338 | template <test::Policy p1, test::Policy p2> | |
339 | struct less_impl<test::Integer<p1>, test::Integer<p2>, when< | |
340 | // both Orderable | |
341 | (p1 & test::Policy::Orderable) && (p2 & test::Policy::Orderable) && | |
342 | ||
343 | // one Constexpr and the other Constant, or both Constexpr | |
344 | (((p1 & test::Policy::Constant) && (p2 & test::Policy::Constexpr)) || | |
345 | ((p1 & test::Policy::Constexpr) && (p2 & test::Policy::Constant)) || | |
346 | ((p1 & test::Policy::Constexpr) && (p2 & test::Policy::Constexpr))) | |
347 | >> { | |
348 | template <typename X, typename Y> | |
349 | static constexpr bool apply(X const&, Y const&) | |
350 | { return X::value < Y::value; } | |
351 | }; | |
352 | ||
353 | template <test::Policy p1, test::Policy p2> | |
354 | struct less_impl<test::Integer<p1>, test::Integer<p2>, when< | |
355 | // both Orderable | |
356 | (p1 & test::Policy::Orderable) && (p2 & test::Policy::Orderable) && | |
357 | ||
358 | // either one is Runtime | |
359 | ((p1 & test::Policy::Runtime) || (p2 & test::Policy::Runtime)) | |
360 | >> { | |
361 | template <typename X, typename Y> | |
362 | static bool apply(X const&, Y const&) | |
363 | { return X::value < Y::value; } | |
364 | }; | |
365 | }} // end namespace boost::hana | |
366 | ||
367 | #endif // !BOOST_HANA_TEST_LAWS_BASE_HPP |