// Copyright Louis Dionne 2013-2017 // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) #ifndef BOOST_HANA_TEST_LAWS_BASE_HPP #define BOOST_HANA_TEST_LAWS_BASE_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace hana { ////////////////////////////////////////////////////////////////////////// // Misc ////////////////////////////////////////////////////////////////////////// namespace test { struct laws; template struct for_each_n_t { static_assert(i > 0, "can't use for_each_n with i < 0"); template constexpr auto operator()(Xs const& xs, F const& f) const { hana::for_each(xs, hana::compose( hana::partial(for_each_n_t{}, xs), hana::partial(hana::partial, f) ) ); } }; template <> struct for_each_n_t<1> { template constexpr auto operator()(Xs const& xs, F const& f) const { hana::for_each(xs, f); } }; template constexpr for_each_n_t for_each_n{}; auto foreach = hana::for_each; constexpr auto foreach3 = for_each_n<3>; constexpr auto foreach2 = for_each_n<2>; struct implies_t { template constexpr decltype(auto) operator()(P&& p, Q&& q) const { return hana::or_(hana::not_(static_cast(p)), static_cast(q)); } }; constexpr auto implies = hana::infix(implies_t{}); struct iff_t { template constexpr decltype(auto) operator()(P&& p, Q&& q) const { return hana::and_(implies(p, q), implies(q, p)); } }; constexpr auto iff = hana::infix(iff_t{}); template constexpr decltype(auto) only_when_(Cond cond, F f) { return hana::eval_if(cond, f, [](auto){ }); } // A type with a constructor that must not be instantiated. // This is to make sure we don't instantiate something else than // the copy-constructor of the elements inside a container when we // copy the container. struct trap_construct { trap_construct() = default; trap_construct(trap_construct const&) = default; #ifndef BOOST_HANA_WORKAROUND_MSVC_MULTIPLECTOR_106654 trap_construct(trap_construct&) = default; #endif trap_construct(trap_construct&&) = default; template trap_construct(X&&) { static_assert(detail::wrong{}, "this constructor must not be instantiated"); } }; // A move-only type. Useful for testing containers. struct move_only { move_only() = default; move_only(move_only const&) = delete; move_only(move_only&&) = default; }; ////////////////////////////////////////////////////////////////////// // InjectionResult ////////////////////////////////////////////////////////////////////// struct InjectionResult { }; template struct injection_result { using hana_tag = InjectionResult; static constexpr int injection_id = i; hana::tuple args; Tracked tracker; template {std::declval()...})> constexpr explicit injection_result(Y&& ...y) : args{static_cast(y)...}, tracker{i} { } }; //! A monotonic injective function. //! //! This is used in the unit tests, where we often just need a function //! which preserves equality and order, but which also satisfies the //! following law for all `Injection`s `f` and `g`: //! @code //! f(x) == g(x) if and only if f === g //! @endcode //! where `===` means _was created by the same call to `injection`_. //! This allows creating several such functions in the unit tests while //! conserving precious information such as the fact that //! `f(g(x)) != g(f(x))`. template struct _injection { template constexpr auto operator()(X&& ...x) const { return injection_result::type... >{static_cast(x)...}; } }; } // end namespace test template <> struct equal_impl { template static constexpr auto apply(X x, Y y) { return hana::and_( hana::bool_c, hana::equal(x.args, y.args) ); } }; template <> struct less_impl { template static constexpr auto apply(X x, Y y) { static_assert(X::injection_id == Y::injection_id, "can't order the result of two different injections"); return hana::less(x.args, y.args); } }; ////////////////////////////////////////////////////////////////////////// // Integer ////////////////////////////////////////////////////////////////////////// namespace test { enum class Policy : int { // One of those is mandatory Constant = 1 << 0 , Constexpr = 1 << 1 , Runtime = 1 << 2 // Those are optional , Tracked = 1 << 3 , Comparable = 1 << 4 , Orderable = 1 << 5 }; constexpr bool operator&&(Policy a, Policy b) { return static_cast(a) && static_cast(b); } constexpr bool operator&&(Policy a, bool b) { return static_cast(a) && b; } constexpr bool operator&&(bool a, Policy b) { return a && static_cast(b); } constexpr bool operator||(Policy a, Policy b) { return static_cast(a) || static_cast(b); } constexpr bool operator||(Policy a, bool b) { return static_cast(a) || b; } constexpr bool operator||(bool a, Policy b) { return a || static_cast(b); } constexpr bool operator!(Policy a) { return !static_cast(a); } constexpr Policy operator|(Policy a, Policy b) { return static_cast(static_cast(a) | static_cast(b)); } constexpr Policy operator&(Policy a, Policy b) { return static_cast(static_cast(a) & static_cast(b)); } template struct Integer { }; template struct Integer> { using value_type = int; }; template struct integer { static_assert( !!(policy & (Policy::Constant | Policy::Constexpr | Policy::Runtime)) , "You must choose at least one of Constant, Constexpr and Runtime."); static constexpr int value = i; constexpr operator int() const { return value; } using hana_tag = Integer; Tracked tracker{i}; }; template struct integer > { static constexpr int value = i; constexpr operator int() const { return value; } using hana_tag = Integer; }; template struct eq : integer { }; template struct ct_eq : integer { }; template struct cx_eq : integer { }; template struct ord : integer { }; template struct ct_ord : integer { }; template struct cx_ord : integer { }; template struct _constant : integer { }; } ////////////////////////////////////////////////////////////////////////// // Model of Constant/IntegralConstant ////////////////////////////////////////////////////////////////////////// template struct IntegralConstant> { static constexpr bool value = static_cast(policy & test::Policy::Constant); }; template struct to_impl, C, when< (policy & test::Policy::Constant) && hana::IntegralConstant::value >> : embedding::value> { template static constexpr auto apply(N const&) { return test::integer{}; } }; ////////////////////////////////////////////////////////////////////////// // Model of Comparable ////////////////////////////////////////////////////////////////////////// template struct equal_impl, test::Integer, when< // both Comparable or Orderable (p1 & (test::Policy::Comparable | test::Policy::Orderable)) && (p2 & (test::Policy::Comparable | test::Policy::Orderable)) && // one Constexpr and the other Constant, or both Constexpr (((p1 & test::Policy::Constant) && (p2 & test::Policy::Constexpr)) || ((p1 & test::Policy::Constexpr) && (p2 & test::Policy::Constant)) || ((p1 & test::Policy::Constexpr) && (p2 & test::Policy::Constexpr))) >> { template static constexpr bool apply(X const&, Y const&) { return X::value == Y::value; } }; template struct equal_impl, test::Integer, when< // both Comparable or Orderable (p1 & (test::Policy::Comparable | test::Policy::Orderable)) && (p2 & (test::Policy::Comparable | test::Policy::Orderable)) && // either one is Runtime ((p1 & test::Policy::Runtime) || (p2 & test::Policy::Runtime)) >> { template static bool apply(X const&, Y const&) { return X::value == Y::value; } }; ////////////////////////////////////////////////////////////////////////// // Model of Orderable ////////////////////////////////////////////////////////////////////////// template struct less_impl, test::Integer, when< // both Orderable (p1 & test::Policy::Orderable) && (p2 & test::Policy::Orderable) && // one Constexpr and the other Constant, or both Constexpr (((p1 & test::Policy::Constant) && (p2 & test::Policy::Constexpr)) || ((p1 & test::Policy::Constexpr) && (p2 & test::Policy::Constant)) || ((p1 & test::Policy::Constexpr) && (p2 & test::Policy::Constexpr))) >> { template static constexpr bool apply(X const&, Y const&) { return X::value < Y::value; } }; template struct less_impl, test::Integer, when< // both Orderable (p1 & test::Policy::Orderable) && (p2 & test::Policy::Orderable) && // either one is Runtime ((p1 & test::Policy::Runtime) || (p2 & test::Policy::Runtime)) >> { template static bool apply(X const&, Y const&) { return X::value < Y::value; } }; }} // end namespace boost::hana #endif // !BOOST_HANA_TEST_LAWS_BASE_HPP