123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369 |
- // 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 <boost/hana/and.hpp>
- #include <boost/hana/bool.hpp>
- #include <boost/hana/core/when.hpp>
- #include <boost/hana/detail/wrong.hpp>
- #include <boost/hana/equal.hpp>
- #include <boost/hana/eval_if.hpp>
- #include <boost/hana/for_each.hpp>
- #include <boost/hana/functional/compose.hpp>
- #include <boost/hana/functional/infix.hpp>
- #include <boost/hana/functional/partial.hpp>
- #include <boost/hana/fwd/concept/integral_constant.hpp>
- #include <boost/hana/fwd/core/to.hpp>
- #include <boost/hana/fwd/less.hpp>
- #include <boost/hana/not.hpp>
- #include <boost/hana/or.hpp>
- #include <boost/hana/tuple.hpp>
- #include <support/tracked.hpp>
- #include <type_traits>
- #include <utility>
- namespace boost { namespace hana {
- //////////////////////////////////////////////////////////////////////////
- // Misc
- //////////////////////////////////////////////////////////////////////////
- namespace test {
- struct laws;
- template <int i>
- struct for_each_n_t {
- static_assert(i > 0, "can't use for_each_n with i < 0");
- template <typename Xs, typename F>
- constexpr auto operator()(Xs const& xs, F const& f) const {
- hana::for_each(xs,
- hana::compose(
- hana::partial(for_each_n_t<i - 1>{}, xs),
- hana::partial(hana::partial, f)
- )
- );
- }
- };
- template <>
- struct for_each_n_t<1> {
- template <typename Xs, typename F>
- constexpr auto operator()(Xs const& xs, F const& f) const {
- hana::for_each(xs, f);
- }
- };
- template <int i>
- constexpr for_each_n_t<i> 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 <typename P, typename Q>
- constexpr decltype(auto) operator()(P&& p, Q&& q) const {
- return hana::or_(hana::not_(static_cast<P&&>(p)),
- static_cast<Q&&>(q));
- }
- };
- constexpr auto implies = hana::infix(implies_t{});
- struct iff_t {
- template <typename P, typename Q>
- 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 <typename Cond, typename F>
- 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 <typename X>
- trap_construct(X&&) {
- static_assert(detail::wrong<X>{},
- "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 <int i, typename ...X>
- struct injection_result {
- using hana_tag = InjectionResult;
- static constexpr int injection_id = i;
- hana::tuple<X...> args;
- Tracked tracker;
- template <typename ...Y, typename = decltype(tuple<X...>{std::declval<Y>()...})>
- constexpr explicit injection_result(Y&& ...y)
- : args{static_cast<Y&&>(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 <int i>
- struct _injection {
- template <typename ...X>
- constexpr auto operator()(X&& ...x) const {
- return injection_result<i,
- typename std::decay<X>::type...
- >{static_cast<X&&>(x)...};
- }
- };
- } // end namespace test
- template <>
- struct equal_impl<test::InjectionResult, test::InjectionResult> {
- template <typename X, typename Y>
- static constexpr auto apply(X x, Y y) {
- return hana::and_(
- hana::bool_c<X::injection_id == Y::injection_id>,
- hana::equal(x.args, y.args)
- );
- }
- };
- template <>
- struct less_impl<test::InjectionResult, test::InjectionResult> {
- template <typename X, typename Y>
- 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<int>(a) && static_cast<int>(b);
- }
- constexpr bool operator&&(Policy a, bool b) {
- return static_cast<int>(a) && b;
- }
- constexpr bool operator&&(bool a, Policy b) {
- return a && static_cast<int>(b);
- }
- constexpr bool operator||(Policy a, Policy b) {
- return static_cast<int>(a) || static_cast<int>(b);
- }
- constexpr bool operator||(Policy a, bool b) {
- return static_cast<int>(a) || b;
- }
- constexpr bool operator||(bool a, Policy b) {
- return a || static_cast<int>(b);
- }
- constexpr bool operator!(Policy a) {
- return !static_cast<int>(a);
- }
- constexpr Policy operator|(Policy a, Policy b) {
- return static_cast<Policy>(static_cast<int>(a) | static_cast<int>(b));
- }
- constexpr Policy operator&(Policy a, Policy b) {
- return static_cast<Policy>(static_cast<int>(a) & static_cast<int>(b));
- }
- template <Policy policy, typename = void>
- struct Integer { };
- template <Policy policy>
- struct Integer<policy, std::enable_if_t<!!(policy & Policy::Constant)>> {
- using value_type = int;
- };
- template <int i, Policy policy, typename = void>
- 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<policy>;
- Tracked tracker{i};
- };
- template <int i, Policy policy>
- struct integer <i, policy, std::enable_if_t<!!(policy & Policy::Constexpr)>> {
- static constexpr int value = i;
- constexpr operator int() const { return value; }
- using hana_tag = Integer<policy>;
- };
- template <int i>
- struct eq : integer<i, Policy::Comparable | Policy::Runtime> { };
- template <int i>
- struct ct_eq : integer<i, Policy::Comparable | Policy::Constant> { };
- template <int i>
- struct cx_eq : integer<i, Policy::Comparable | Policy::Constexpr> { };
- template <int i>
- struct ord : integer<i, Policy::Orderable | Policy::Runtime> { };
- template <int i>
- struct ct_ord : integer<i, Policy::Orderable | Policy::Constant> { };
- template <int i>
- struct cx_ord : integer<i, Policy::Orderable | Policy::Constexpr> { };
- template <int i>
- struct _constant
- : integer<i, Policy::Constant | Policy::Comparable | Policy::Orderable>
- { };
- }
- //////////////////////////////////////////////////////////////////////////
- // Model of Constant/IntegralConstant
- //////////////////////////////////////////////////////////////////////////
- template <test::Policy policy>
- struct IntegralConstant<test::Integer<policy>> {
- static constexpr bool value = static_cast<bool>(policy & test::Policy::Constant);
- };
- template <test::Policy policy, typename C>
- struct to_impl<test::Integer<policy>, C, when<
- (policy & test::Policy::Constant) &&
- hana::IntegralConstant<C>::value
- >>
- : embedding<is_embedded<typename C::value_type, int>::value>
- {
- template <typename N>
- static constexpr auto apply(N const&) {
- return test::integer<N::value, policy>{};
- }
- };
- //////////////////////////////////////////////////////////////////////////
- // Model of Comparable
- //////////////////////////////////////////////////////////////////////////
- template <test::Policy p1, test::Policy p2>
- struct equal_impl<test::Integer<p1>, test::Integer<p2>, 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 <typename X, typename Y>
- static constexpr bool apply(X const&, Y const&)
- { return X::value == Y::value; }
- };
- template <test::Policy p1, test::Policy p2>
- struct equal_impl<test::Integer<p1>, test::Integer<p2>, 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 <typename X, typename Y>
- static bool apply(X const&, Y const&)
- { return X::value == Y::value; }
- };
- //////////////////////////////////////////////////////////////////////////
- // Model of Orderable
- //////////////////////////////////////////////////////////////////////////
- template <test::Policy p1, test::Policy p2>
- struct less_impl<test::Integer<p1>, test::Integer<p2>, 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 <typename X, typename Y>
- static constexpr bool apply(X const&, Y const&)
- { return X::value < Y::value; }
- };
- template <test::Policy p1, test::Policy p2>
- struct less_impl<test::Integer<p1>, test::Integer<p2>, when<
- // both Orderable
- (p1 & test::Policy::Orderable) && (p2 & test::Policy::Orderable) &&
- // either one is Runtime
- ((p1 & test::Policy::Runtime) || (p2 & test::Policy::Runtime))
- >> {
- template <typename X, typename Y>
- static bool apply(X const&, Y const&)
- { return X::value < Y::value; }
- };
- }} // end namespace boost::hana
- #endif // !BOOST_HANA_TEST_LAWS_BASE_HPP
|