1 // Copyright Louis Dionne 2013-2017
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>
operator ()boost::hana::test::for_each_n_t43             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>
operator ()boost::hana::test::for_each_n_t56             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>
operator ()boost::hana::test::implies_t70             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>
operator ()boost::hana::test::iff_t79             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>
only_when_(Cond cond,F 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 #ifndef BOOST_HANA_WORKAROUND_MSVC_MULTIPLECTOR_106654
98             trap_construct(trap_construct&) = default;
99 #endif
100             trap_construct(trap_construct&&) = default;
101 
102             template <typename X>
trap_constructboost::hana::test::trap_construct103             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>()...})>
injection_resultboost::hana::test::injection_result129             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>
operator ()boost::hana::test::_injection149             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>
applyboost::hana::equal_impl160         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>
applyboost::hana::less_impl171         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 
operator &&(Policy a,Policy b)195         constexpr bool operator&&(Policy a, Policy b) {
196             return static_cast<int>(a) && static_cast<int>(b);
197         }
198 
operator &&(Policy a,bool b)199         constexpr bool operator&&(Policy a, bool b) {
200             return static_cast<int>(a) && b;
201         }
202 
operator &&(bool a,Policy b)203         constexpr bool operator&&(bool a, Policy b) {
204             return a && static_cast<int>(b);
205         }
206 
operator ||(Policy a,Policy b)207         constexpr bool operator||(Policy a, Policy b) {
208             return static_cast<int>(a) || static_cast<int>(b);
209         }
210 
operator ||(Policy a,bool b)211         constexpr bool operator||(Policy a, bool b) {
212             return static_cast<int>(a) || b;
213         }
214 
operator ||(bool a,Policy b)215         constexpr bool operator||(bool a, Policy b) {
216             return a || static_cast<int>(b);
217         }
218 
operator !(Policy a)219         constexpr bool operator!(Policy a) {
220             return !static_cast<int>(a);
221         }
222 
operator |(Policy a,Policy b)223         constexpr Policy operator|(Policy a, Policy b) {
224             return static_cast<Policy>(static_cast<int>(a) | static_cast<int>(b));
225         }
226 
operator &(Policy a,Policy b)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;
operator intboost::hana::test::integer246             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;
operator intboost::hana::test::integer254             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>
applyboost::hana::to_impl298         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>
applyboost::hana::equal_impl318         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>
applyboost::hana::equal_impl332         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>
applyboost::hana::less_impl351         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>
applyboost::hana::less_impl364         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
370