]>
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; | |
92f5a8d4 | 97 | #ifndef BOOST_HANA_WORKAROUND_MSVC_MULTIPLECTOR_106654 |
7c673cae | 98 | trap_construct(trap_construct&) = default; |
92f5a8d4 | 99 | #endif |
7c673cae FG |
100 | trap_construct(trap_construct&&) = default; |
101 | ||
102 | template <typename X> | |
103 | trap_construct(X&&) { | |
104 | static_assert(detail::wrong<X>{}, | |
105 | "this constructor must not be instantiated"); | |
106 | } | |
107 | }; | |
108 | ||
109 | // A move-only type. Useful for testing containers. | |
110 | struct move_only { | |
111 | move_only() = default; | |
112 | move_only(move_only const&) = delete; | |
113 | move_only(move_only&&) = default; | |
114 | }; | |
115 | ||
116 | ////////////////////////////////////////////////////////////////////// | |
117 | // InjectionResult | |
118 | ////////////////////////////////////////////////////////////////////// | |
119 | struct InjectionResult { }; | |
120 | ||
121 | template <int i, typename ...X> | |
122 | struct injection_result { | |
123 | using hana_tag = InjectionResult; | |
124 | static constexpr int injection_id = i; | |
125 | hana::tuple<X...> args; | |
126 | Tracked tracker; | |
127 | ||
128 | template <typename ...Y, typename = decltype(tuple<X...>{std::declval<Y>()...})> | |
129 | constexpr explicit injection_result(Y&& ...y) | |
130 | : args{static_cast<Y&&>(y)...}, tracker{i} | |
131 | { } | |
132 | }; | |
133 | ||
134 | //! A monotonic injective function. | |
135 | //! | |
136 | //! This is used in the unit tests, where we often just need a function | |
137 | //! which preserves equality and order, but which also satisfies the | |
138 | //! following law for all `Injection`s `f` and `g`: | |
139 | //! @code | |
140 | //! f(x) == g(x) if and only if f === g | |
141 | //! @endcode | |
142 | //! where `===` means _was created by the same call to `injection`_. | |
143 | //! This allows creating several such functions in the unit tests while | |
144 | //! conserving precious information such as the fact that | |
145 | //! `f(g(x)) != g(f(x))`. | |
146 | template <int i> | |
147 | struct _injection { | |
148 | template <typename ...X> | |
149 | constexpr auto operator()(X&& ...x) const { | |
150 | return injection_result<i, | |
151 | typename std::decay<X>::type... | |
152 | >{static_cast<X&&>(x)...}; | |
153 | } | |
154 | }; | |
155 | } // end namespace test | |
156 | ||
157 | template <> | |
158 | struct equal_impl<test::InjectionResult, test::InjectionResult> { | |
159 | template <typename X, typename Y> | |
160 | static constexpr auto apply(X x, Y y) { | |
161 | return hana::and_( | |
162 | hana::bool_c<X::injection_id == Y::injection_id>, | |
163 | hana::equal(x.args, y.args) | |
164 | ); | |
165 | } | |
166 | }; | |
167 | ||
168 | template <> | |
169 | struct less_impl<test::InjectionResult, test::InjectionResult> { | |
170 | template <typename X, typename Y> | |
171 | static constexpr auto apply(X x, Y y) { | |
172 | static_assert(X::injection_id == Y::injection_id, | |
173 | "can't order the result of two different injections"); | |
174 | return hana::less(x.args, y.args); | |
175 | } | |
176 | }; | |
177 | ||
178 | ||
179 | ////////////////////////////////////////////////////////////////////////// | |
180 | // Integer | |
181 | ////////////////////////////////////////////////////////////////////////// | |
182 | namespace test { | |
183 | enum class Policy : int { | |
184 | // One of those is mandatory | |
185 | Constant = 1 << 0 | |
186 | , Constexpr = 1 << 1 | |
187 | , Runtime = 1 << 2 | |
188 | ||
189 | // Those are optional | |
190 | , Tracked = 1 << 3 | |
191 | , Comparable = 1 << 4 | |
192 | , Orderable = 1 << 5 | |
193 | }; | |
194 | ||
195 | constexpr bool operator&&(Policy a, Policy b) { | |
196 | return static_cast<int>(a) && static_cast<int>(b); | |
197 | } | |
198 | ||
199 | constexpr bool operator&&(Policy a, bool b) { | |
200 | return static_cast<int>(a) && b; | |
201 | } | |
202 | ||
203 | constexpr bool operator&&(bool a, Policy b) { | |
204 | return a && static_cast<int>(b); | |
205 | } | |
206 | ||
207 | constexpr bool operator||(Policy a, Policy b) { | |
208 | return static_cast<int>(a) || static_cast<int>(b); | |
209 | } | |
210 | ||
211 | constexpr bool operator||(Policy a, bool b) { | |
212 | return static_cast<int>(a) || b; | |
213 | } | |
214 | ||
215 | constexpr bool operator||(bool a, Policy b) { | |
216 | return a || static_cast<int>(b); | |
217 | } | |
218 | ||
219 | constexpr bool operator!(Policy a) { | |
220 | return !static_cast<int>(a); | |
221 | } | |
222 | ||
223 | constexpr Policy operator|(Policy a, Policy b) { | |
224 | return static_cast<Policy>(static_cast<int>(a) | static_cast<int>(b)); | |
225 | } | |
226 | ||
227 | constexpr Policy operator&(Policy a, Policy b) { | |
228 | return static_cast<Policy>(static_cast<int>(a) & static_cast<int>(b)); | |
229 | } | |
230 | ||
231 | template <Policy policy, typename = void> | |
232 | struct Integer { }; | |
233 | ||
234 | template <Policy policy> | |
235 | struct Integer<policy, std::enable_if_t<!!(policy & Policy::Constant)>> { | |
236 | using value_type = int; | |
237 | }; | |
238 | ||
239 | template <int i, Policy policy, typename = void> | |
240 | struct integer { | |
241 | static_assert( | |
242 | !!(policy & (Policy::Constant | Policy::Constexpr | Policy::Runtime)) | |
243 | , "You must choose at least one of Constant, Constexpr and Runtime."); | |
244 | ||
245 | static constexpr int value = i; | |
246 | constexpr operator int() const { return value; } | |
247 | using hana_tag = Integer<policy>; | |
248 | Tracked tracker{i}; | |
249 | }; | |
250 | ||
251 | template <int i, Policy policy> | |
252 | struct integer <i, policy, std::enable_if_t<!!(policy & Policy::Constexpr)>> { | |
253 | static constexpr int value = i; | |
254 | constexpr operator int() const { return value; } | |
255 | using hana_tag = Integer<policy>; | |
256 | }; | |
257 | ||
258 | template <int i> | |
259 | struct eq : integer<i, Policy::Comparable | Policy::Runtime> { }; | |
260 | ||
261 | template <int i> | |
262 | struct ct_eq : integer<i, Policy::Comparable | Policy::Constant> { }; | |
263 | ||
264 | template <int i> | |
265 | struct cx_eq : integer<i, Policy::Comparable | Policy::Constexpr> { }; | |
266 | ||
267 | template <int i> | |
268 | struct ord : integer<i, Policy::Orderable | Policy::Runtime> { }; | |
269 | ||
270 | template <int i> | |
271 | struct ct_ord : integer<i, Policy::Orderable | Policy::Constant> { }; | |
272 | ||
273 | template <int i> | |
274 | struct cx_ord : integer<i, Policy::Orderable | Policy::Constexpr> { }; | |
275 | ||
276 | template <int i> | |
277 | struct _constant | |
278 | : integer<i, Policy::Constant | Policy::Comparable | Policy::Orderable> | |
279 | { }; | |
280 | } | |
281 | ||
282 | ////////////////////////////////////////////////////////////////////////// | |
283 | // Model of Constant/IntegralConstant | |
284 | ////////////////////////////////////////////////////////////////////////// | |
285 | template <test::Policy policy> | |
286 | struct IntegralConstant<test::Integer<policy>> { | |
287 | static constexpr bool value = static_cast<bool>(policy & test::Policy::Constant); | |
288 | }; | |
289 | ||
290 | template <test::Policy policy, typename C> | |
291 | struct to_impl<test::Integer<policy>, C, when< | |
292 | (policy & test::Policy::Constant) && | |
293 | hana::IntegralConstant<C>::value | |
294 | >> | |
295 | : embedding<is_embedded<typename C::value_type, int>::value> | |
296 | { | |
297 | template <typename N> | |
298 | static constexpr auto apply(N const&) { | |
299 | return test::integer<N::value, policy>{}; | |
300 | } | |
301 | }; | |
302 | ||
303 | ////////////////////////////////////////////////////////////////////////// | |
304 | // Model of Comparable | |
305 | ////////////////////////////////////////////////////////////////////////// | |
306 | template <test::Policy p1, test::Policy p2> | |
307 | struct equal_impl<test::Integer<p1>, test::Integer<p2>, when< | |
308 | // both Comparable or Orderable | |
309 | (p1 & (test::Policy::Comparable | test::Policy::Orderable)) && | |
310 | (p2 & (test::Policy::Comparable | test::Policy::Orderable)) && | |
311 | ||
312 | // one Constexpr and the other Constant, or both Constexpr | |
313 | (((p1 & test::Policy::Constant) && (p2 & test::Policy::Constexpr)) || | |
314 | ((p1 & test::Policy::Constexpr) && (p2 & test::Policy::Constant)) || | |
315 | ((p1 & test::Policy::Constexpr) && (p2 & test::Policy::Constexpr))) | |
316 | >> { | |
317 | template <typename X, typename Y> | |
318 | static constexpr bool apply(X const&, Y const&) | |
319 | { return X::value == Y::value; } | |
320 | }; | |
321 | ||
322 | template <test::Policy p1, test::Policy p2> | |
323 | struct equal_impl<test::Integer<p1>, test::Integer<p2>, when< | |
324 | // both Comparable or Orderable | |
325 | (p1 & (test::Policy::Comparable | test::Policy::Orderable)) && | |
326 | (p2 & (test::Policy::Comparable | test::Policy::Orderable)) && | |
327 | ||
328 | // either one is Runtime | |
329 | ((p1 & test::Policy::Runtime) || (p2 & test::Policy::Runtime)) | |
330 | >> { | |
331 | template <typename X, typename Y> | |
332 | static bool apply(X const&, Y const&) | |
333 | { return X::value == Y::value; } | |
334 | }; | |
335 | ||
336 | ||
337 | ////////////////////////////////////////////////////////////////////////// | |
338 | // Model of Orderable | |
339 | ////////////////////////////////////////////////////////////////////////// | |
340 | template <test::Policy p1, test::Policy p2> | |
341 | struct less_impl<test::Integer<p1>, test::Integer<p2>, when< | |
342 | // both Orderable | |
343 | (p1 & test::Policy::Orderable) && (p2 & test::Policy::Orderable) && | |
344 | ||
345 | // one Constexpr and the other Constant, or both Constexpr | |
346 | (((p1 & test::Policy::Constant) && (p2 & test::Policy::Constexpr)) || | |
347 | ((p1 & test::Policy::Constexpr) && (p2 & test::Policy::Constant)) || | |
348 | ((p1 & test::Policy::Constexpr) && (p2 & test::Policy::Constexpr))) | |
349 | >> { | |
350 | template <typename X, typename Y> | |
351 | static constexpr bool apply(X const&, Y const&) | |
352 | { return X::value < Y::value; } | |
353 | }; | |
354 | ||
355 | template <test::Policy p1, test::Policy p2> | |
356 | struct less_impl<test::Integer<p1>, test::Integer<p2>, when< | |
357 | // both Orderable | |
358 | (p1 & test::Policy::Orderable) && (p2 & test::Policy::Orderable) && | |
359 | ||
360 | // either one is Runtime | |
361 | ((p1 & test::Policy::Runtime) || (p2 & test::Policy::Runtime)) | |
362 | >> { | |
363 | template <typename X, typename Y> | |
364 | static bool apply(X const&, Y const&) | |
365 | { return X::value < Y::value; } | |
366 | }; | |
367 | }} // end namespace boost::hana | |
368 | ||
369 | #endif // !BOOST_HANA_TEST_LAWS_BASE_HPP |