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