1 // Copyright (C) 2016-2018 T. Zachary Laine 2 // 3 // Distributed under the Boost Software License, Version 1.0. (See 4 // accompanying file LICENSE_1_0.txt or copy at 5 // http://www.boost.org/LICENSE_1_0.txt) 6 #ifndef BOOST_YAP_DETAIL_TRANSFORM_HPP_INCLUDED 7 #define BOOST_YAP_DETAIL_TRANSFORM_HPP_INCLUDED 8 9 #include <boost/yap/algorithm_fwd.hpp> 10 11 #include <boost/hana/transform.hpp> 12 13 #include <cassert> 14 15 16 namespace boost { namespace yap { namespace detail { 17 18 template<int I, typename T, typename... Ts> 19 struct nth_element_impl 20 { 21 using type = typename nth_element_impl<I - 1, Ts...>::type; 22 }; 23 24 template<typename T, typename... Ts> 25 struct nth_element_impl<0, T, Ts...> 26 { 27 using type = T; 28 }; 29 30 template<int I, typename... Ts> 31 using nth_element = typename nth_element_impl<I, Ts...>::type; 32 33 template<typename T, bool RemoveRefs = std::is_rvalue_reference<T>::value> 34 struct rvalue_ref_to_value; 35 36 template<typename T> 37 struct rvalue_ref_to_value<T, true> 38 { 39 using type = typename std::remove_reference<T>::type; 40 }; 41 42 template<typename T> 43 struct rvalue_ref_to_value<T, false> 44 { 45 using type = T; 46 }; 47 48 template<typename T> 49 using rvalue_ref_to_value_t = typename rvalue_ref_to_value<T>::type; 50 51 template<bool IsRvalueRef> 52 struct rvalue_mover 53 { 54 template<typename T> operator ()boost::yap::detail::rvalue_mover55 constexpr decltype(auto) operator()(T && t) const 56 { 57 return static_cast<T &&>(t); 58 } 59 }; 60 61 template<> 62 struct rvalue_mover<true> 63 { 64 template<typename T> operator ()boost::yap::detail::rvalue_mover65 constexpr std::remove_reference_t<T> operator()(T && t) const 66 { 67 return std::move(t); 68 } 69 }; 70 71 template<typename... PlaceholderArgs> 72 struct placeholder_transform_t 73 { 74 using tuple_t = hana::tuple<rvalue_ref_to_value_t<PlaceholderArgs>...>; 75 placeholder_transform_tboost::yap::detail::placeholder_transform_t76 constexpr placeholder_transform_t(PlaceholderArgs &&... args) : 77 placeholder_args_(static_cast<PlaceholderArgs &&>(args)...) 78 {} 79 80 template<long long I> 81 constexpr decltype(auto) operator ()boost::yap::detail::placeholder_transform_t82 operator()(expr_tag<expr_kind::terminal>, boost::yap::placeholder<I>) const 83 { 84 static_assert( 85 I <= decltype(hana::size(std::declval<tuple_t>()))::value, 86 "Out of range placeholder index,"); 87 using nth_type = nth_element<I - 1, PlaceholderArgs...>; 88 return as_expr<minimal_expr>( 89 rvalue_mover<!std::is_lvalue_reference<nth_type>::value>{}( 90 placeholder_args_[hana::llong<I - 1>{}])); 91 } 92 93 tuple_t placeholder_args_; 94 }; 95 96 template<typename... PlaceholderArgs> 97 struct evaluation_transform_t 98 { 99 using tuple_t = hana::tuple<rvalue_ref_to_value_t<PlaceholderArgs>...>; 100 evaluation_transform_tboost::yap::detail::evaluation_transform_t101 constexpr evaluation_transform_t(PlaceholderArgs &&... args) : 102 placeholder_args_(static_cast<PlaceholderArgs &&>(args)...) 103 {} 104 105 template<long long I> 106 constexpr decltype(auto) operator ()boost::yap::detail::evaluation_transform_t107 operator()(expr_tag<expr_kind::terminal>, boost::yap::placeholder<I>) const 108 { 109 static_assert( 110 I <= decltype(hana::size(std::declval<tuple_t>()))::value, 111 "Out of range placeholder index,"); 112 using nth_type = nth_element<I - 1, PlaceholderArgs...>; 113 return rvalue_mover<!std::is_lvalue_reference<nth_type>::value>{}( 114 placeholder_args_[hana::llong<I - 1>{}]); 115 } 116 117 template<typename T> operator ()boost::yap::detail::evaluation_transform_t118 constexpr decltype(auto) operator()(expr_tag<expr_kind::terminal>, T && t) const 119 { 120 return static_cast<T &&>(t); 121 } 122 123 #define BOOST_YAP_UNARY_OPERATOR_CASE(op, op_name) \ 124 template<typename T> \ 125 constexpr decltype(auto) operator()(expr_tag<expr_kind::op_name>, T && t) const \ 126 { \ 127 return op transform( \ 128 as_expr<minimal_expr>(static_cast<T &&>(t)), *this); \ 129 } 130 131 BOOST_YAP_UNARY_OPERATOR_CASE(+, unary_plus) 132 BOOST_YAP_UNARY_OPERATOR_CASE(-, negate) 133 BOOST_YAP_UNARY_OPERATOR_CASE(*, dereference) 134 BOOST_YAP_UNARY_OPERATOR_CASE(~, complement) 135 BOOST_YAP_UNARY_OPERATOR_CASE(&, address_of) 136 BOOST_YAP_UNARY_OPERATOR_CASE(!, logical_not) 137 BOOST_YAP_UNARY_OPERATOR_CASE(++, pre_inc) 138 BOOST_YAP_UNARY_OPERATOR_CASE(--, pre_dec) 139 140 template<typename T> operator ()boost::yap::detail::evaluation_transform_t141 constexpr decltype(auto) operator()(expr_tag<expr_kind::post_inc>, T && t) const 142 { 143 return transform( 144 as_expr<minimal_expr>(static_cast<T &&>(t)), *this)++; 145 } 146 template<typename T> operator ()boost::yap::detail::evaluation_transform_t147 constexpr decltype(auto) operator()(expr_tag<expr_kind::post_dec>, T && t) const 148 { 149 return transform( 150 as_expr<minimal_expr>(static_cast<T &&>(t)), *this)--; 151 } 152 153 #undef BOOST_YAP_UNARY_OPERATOR_CASE 154 155 #define BOOST_YAP_BINARY_OPERATOR_CASE(op, op_name) \ 156 template<typename T, typename U> \ 157 constexpr decltype(auto) operator()(expr_tag<expr_kind::op_name>, T && t, U && u) const \ 158 { \ 159 return transform(as_expr<minimal_expr>(static_cast<T &&>(t)), *this) \ 160 op transform(as_expr<minimal_expr>(static_cast<U &&>(u)), *this); \ 161 } 162 163 BOOST_YAP_BINARY_OPERATOR_CASE(<<, shift_left) 164 BOOST_YAP_BINARY_OPERATOR_CASE(>>, shift_right) 165 BOOST_YAP_BINARY_OPERATOR_CASE(*, multiplies) 166 BOOST_YAP_BINARY_OPERATOR_CASE(/, divides) 167 BOOST_YAP_BINARY_OPERATOR_CASE(%, modulus) 168 BOOST_YAP_BINARY_OPERATOR_CASE(+, plus) 169 BOOST_YAP_BINARY_OPERATOR_CASE(-, minus) 170 BOOST_YAP_BINARY_OPERATOR_CASE(<, less) 171 BOOST_YAP_BINARY_OPERATOR_CASE(>, greater) 172 BOOST_YAP_BINARY_OPERATOR_CASE(<=, less_equal) 173 BOOST_YAP_BINARY_OPERATOR_CASE(>=, greater_equal) 174 BOOST_YAP_BINARY_OPERATOR_CASE(==, equal_to) 175 BOOST_YAP_BINARY_OPERATOR_CASE(!=, not_equal_to) 176 BOOST_YAP_BINARY_OPERATOR_CASE(||, logical_or) 177 BOOST_YAP_BINARY_OPERATOR_CASE(&&, logical_and) 178 BOOST_YAP_BINARY_OPERATOR_CASE(&, bitwise_and) 179 BOOST_YAP_BINARY_OPERATOR_CASE(|, bitwise_or) 180 BOOST_YAP_BINARY_OPERATOR_CASE (^, bitwise_xor) 181 182 // clang-format off 183 //[ evaluation_transform_comma 184 template<typename T, typename U> operator ()boost::yap::detail::evaluation_transform_t185 constexpr decltype(auto) operator()(expr_tag<expr_kind::comma>, T && t, U && u) const 186 { 187 return transform( 188 as_expr<minimal_expr>(static_cast<T &&>(t)), *this), 189 transform( 190 as_expr<minimal_expr>(static_cast<U &&>(u)), *this); 191 } 192 //] 193 // clang-format on 194 195 BOOST_YAP_BINARY_OPERATOR_CASE(->*, mem_ptr) 196 BOOST_YAP_BINARY_OPERATOR_CASE(=, assign) 197 BOOST_YAP_BINARY_OPERATOR_CASE(<<=, shift_left_assign) 198 BOOST_YAP_BINARY_OPERATOR_CASE(>>=, shift_right_assign) 199 BOOST_YAP_BINARY_OPERATOR_CASE(*=, multiplies_assign) 200 BOOST_YAP_BINARY_OPERATOR_CASE(/=, divides_assign) 201 BOOST_YAP_BINARY_OPERATOR_CASE(%=, modulus_assign) 202 BOOST_YAP_BINARY_OPERATOR_CASE(+=, plus_assign) 203 BOOST_YAP_BINARY_OPERATOR_CASE(-=, minus_assign) 204 BOOST_YAP_BINARY_OPERATOR_CASE(&=, bitwise_and_assign) 205 BOOST_YAP_BINARY_OPERATOR_CASE(|=, bitwise_or_assign) 206 BOOST_YAP_BINARY_OPERATOR_CASE(^=, bitwise_xor_assign) 207 208 template<typename T, typename U> 209 constexpr decltype(auto) operator ()boost::yap::detail::evaluation_transform_t210 operator()(expr_tag<expr_kind::subscript>, T && t, U && u) const 211 { 212 return transform( 213 as_expr<minimal_expr>(static_cast<T &&>(t)), *this)[transform( 214 as_expr<minimal_expr>(static_cast<U &&>(u)), *this)]; 215 } 216 217 #undef BOOST_YAP_BINARY_OPERATOR_CASE 218 219 template<typename T, typename U, typename V> 220 constexpr decltype(auto) operator ()boost::yap::detail::evaluation_transform_t221 operator()(expr_tag<expr_kind::if_else>, T && t, U && u, V && v) const 222 { 223 return transform(as_expr<minimal_expr>(static_cast<T &&>(t)), *this) 224 ? transform( 225 as_expr<minimal_expr>(static_cast<U &&>(u)), *this) 226 : transform( 227 as_expr<minimal_expr>(static_cast<V &&>(v)), 228 *this); 229 } 230 231 // clang-format off 232 //[ evaluation_transform_call 233 template<typename Callable, typename... Args> operator ()boost::yap::detail::evaluation_transform_t234 constexpr decltype(auto) operator()( 235 expr_tag<expr_kind::call>, Callable && callable, Args &&... args) const 236 { 237 return transform(as_expr<minimal_expr>(static_cast<Callable &&>(callable)), *this)( 238 transform(as_expr<minimal_expr>(static_cast<Args &&>(args)), *this)... 239 ); 240 } 241 //] 242 // clang-format on 243 244 tuple_t placeholder_args_; 245 }; 246 247 248 template<bool Strict, int I, bool IsExprRef> 249 struct transform_impl; 250 251 template< 252 bool Strict, 253 typename Expr, 254 typename TransformTuple, 255 int I, 256 expr_arity Arity, 257 typename = void_t<>> 258 struct transform_expression_tag; 259 260 261 // Forward terminals/recurively transform noterminasl; attempted last. 262 263 template<bool IsLvalueRef, bool IsTerminal, bool Strict> 264 struct default_transform 265 { 266 template<typename Expr, typename TransformTuple> operator ()boost::yap::detail::default_transform267 constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const 268 { 269 return static_cast<Expr &&>(expr); 270 } 271 }; 272 273 template<bool IsLvalueRef, bool IsTerminal> 274 struct default_transform<IsLvalueRef, IsTerminal, true> 275 { 276 struct incomplete; 277 278 // If you're getting an error because this function is uncallable, 279 // that's by design. You called yap::transform_strict(expr, xfrom) 280 // and one or more subexpression of 'expr' are not callable with any 281 // overload in 'xform'. 282 template<typename Expr, typename TransformTuple> 283 constexpr incomplete operator()(Expr && expr, TransformTuple transforms) const; 284 }; 285 286 template< 287 expr_kind Kind, 288 template<expr_kind, class> class ExprTemplate, 289 typename OldTuple, 290 typename NewTuple> make_expr_from_tuple(ExprTemplate<Kind,OldTuple> const & expr,NewTuple && tuple)291 constexpr auto make_expr_from_tuple( 292 ExprTemplate<Kind, OldTuple> const & expr, NewTuple && tuple) 293 { 294 return ExprTemplate<Kind, NewTuple>{std::move(tuple)}; 295 } 296 297 template<expr_kind Kind, typename Expr, typename NewTuple> make_expr_from_tuple(Expr const & expr,NewTuple && tuple)298 constexpr auto make_expr_from_tuple(Expr const & expr, NewTuple && tuple) 299 { 300 return minimal_expr<Kind, NewTuple>{std::move(tuple)}; 301 } 302 303 template<typename Expr, typename Tuple, typename TransformTuple> transform_nonterminal(Expr const & expr,Tuple && tuple,TransformTuple transforms)304 constexpr decltype(auto) transform_nonterminal( 305 Expr const & expr, Tuple && tuple, TransformTuple transforms) 306 { 307 auto transformed_tuple = 308 hana::transform(static_cast<Tuple &&>(tuple), [&](auto && element) { 309 using element_t = decltype(element); 310 auto const kind = remove_cv_ref_t<element_t>::kind; 311 ::boost::yap::detail:: 312 transform_impl<false, 0, kind == expr_kind::expr_ref> 313 xform; 314 return xform(static_cast<element_t &&>(element), transforms); 315 }); 316 auto const kind = remove_cv_ref_t<Expr>::kind; 317 return make_expr_from_tuple<kind>(expr, std::move(transformed_tuple)); 318 } 319 320 template<> 321 struct default_transform<true, false, false> 322 { 323 template<typename Expr, typename TransformTuple> operator ()boost::yap::detail::default_transform324 constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const 325 { 326 return transform_nonterminal(expr, expr.elements, transforms); 327 } 328 }; 329 330 template<> 331 struct default_transform<false, false, false> 332 { 333 template<typename Expr, typename TransformTuple> 334 constexpr decltype(auto) operator ()boost::yap::detail::default_transform335 operator()(Expr && expr, TransformTuple transforms) const 336 { 337 return transform_nonterminal( 338 expr, std::move(expr.elements), transforms); 339 } 340 }; 341 342 // Dispatch to the next transform, or to the default transform if there is 343 // no next transform. 344 345 template< 346 bool Strict, 347 typename Expr, 348 typename TransformTuple, 349 int I, 350 bool NextTransformExists> 351 struct next_or_default_transform 352 { operator ()boost::yap::detail::next_or_default_transform353 constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const 354 { 355 // Use the next transform. 356 constexpr expr_kind kind = remove_cv_ref_t<Expr>::kind; 357 return detail:: 358 transform_impl<Strict, I + 1, kind == expr_kind::expr_ref>{}( 359 static_cast<Expr &&>(expr), transforms); 360 } 361 }; 362 363 template<bool Strict, typename Expr, typename TransformTuple, int I> 364 struct next_or_default_transform<Strict, Expr, TransformTuple, I, false> 365 { operator ()boost::yap::detail::next_or_default_transform366 constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const 367 { 368 // No next transform exists; use the default transform. 369 constexpr expr_kind kind = remove_cv_ref_t<Expr>::kind; 370 return default_transform< 371 std::is_lvalue_reference<Expr>::value, 372 kind == expr_kind::terminal, 373 Strict>{}(static_cast<Expr &&>(expr), transforms); 374 } 375 }; 376 377 // Expression-matching; attempted second. 378 379 template< 380 bool Strict, 381 typename Expr, 382 typename TransformTuple, 383 int I, 384 typename = detail::void_t<>> 385 struct transform_expression_expr 386 { operator ()boost::yap::detail::transform_expression_expr387 constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const 388 { 389 // No expr-matching succeeded; use the next or default transform. 390 return next_or_default_transform< 391 Strict, 392 Expr, 393 TransformTuple, 394 I, 395 I + 1 < decltype(hana::size( 396 std::declval<TransformTuple>()))::value>{}( 397 static_cast<Expr &&>(expr), transforms); 398 } 399 }; 400 401 template<bool Strict, typename Expr, typename TransformTuple, int I> 402 struct transform_expression_expr< 403 Strict, 404 Expr, 405 TransformTuple, 406 I, 407 void_t<decltype((*std::declval<TransformTuple>()[hana::llong<I>{}])( 408 std::declval<Expr>()))>> 409 { operator ()boost::yap::detail::transform_expression_expr410 constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const 411 { 412 return (*transforms[hana::llong<I>{}])(static_cast<Expr &&>(expr)); 413 } 414 }; 415 416 417 // Tag-matching; attempted first. 418 419 template< 420 bool Strict, 421 typename Expr, 422 typename TransformTuple, 423 int I, 424 expr_arity Arity, 425 typename> 426 struct transform_expression_tag 427 { operator ()boost::yap::detail::transform_expression_tag428 constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const 429 { 430 // No tag-matching succeeded; try expr-matching. 431 return transform_expression_expr<Strict, Expr, TransformTuple, I>{}( 432 static_cast<Expr &&>(expr), transforms); 433 } 434 }; 435 436 template<typename T> terminal_value(T && x)437 constexpr decltype(auto) terminal_value(T && x) 438 { 439 return value_impl<true>(static_cast<T &&>(x)); 440 } 441 442 443 template<bool Strict, typename Expr, typename TransformTuple, int I> 444 struct transform_expression_tag< 445 Strict, 446 Expr, 447 TransformTuple, 448 I, 449 expr_arity::one, 450 void_t<decltype((*std::declval<TransformTuple>()[hana::llong<I>{}])( 451 expr_tag<remove_cv_ref_t<Expr>::kind>{}, 452 terminal_value(::boost::yap::value(std::declval<Expr>()))))>> 453 { operator ()boost::yap::detail::transform_expression_tag454 constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const 455 { 456 return (*transforms[hana::llong<I>{}])( 457 expr_tag<remove_cv_ref_t<Expr>::kind>{}, 458 terminal_value( 459 ::boost::yap::value(static_cast<Expr &&>(expr)))); 460 } 461 }; 462 463 template<bool Strict, typename Expr, typename TransformTuple, int I> 464 struct transform_expression_tag< 465 Strict, 466 Expr, 467 TransformTuple, 468 I, 469 expr_arity::two, 470 void_t<decltype((*std::declval<TransformTuple>()[hana::llong<I>{}])( 471 expr_tag<remove_cv_ref_t<Expr>::kind>{}, 472 terminal_value(::boost::yap::left(std::declval<Expr>())), 473 terminal_value(::boost::yap::right(std::declval<Expr>()))))>> 474 { operator ()boost::yap::detail::transform_expression_tag475 constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const 476 { 477 return (*transforms[hana::llong<I>{}])( 478 expr_tag<remove_cv_ref_t<Expr>::kind>{}, 479 terminal_value(::boost::yap::left(static_cast<Expr &&>(expr))), 480 terminal_value( 481 ::boost::yap::right(static_cast<Expr &&>(expr)))); 482 } 483 }; 484 485 template<bool Strict, typename Expr, typename TransformTuple, int I> 486 struct transform_expression_tag< 487 Strict, 488 Expr, 489 TransformTuple, 490 I, 491 expr_arity::three, 492 void_t<decltype((*std::declval<TransformTuple>()[hana::llong<I>{}])( 493 expr_tag<remove_cv_ref_t<Expr>::kind>{}, 494 terminal_value(::boost::yap::cond(std::declval<Expr>())), 495 terminal_value(::boost::yap::then(std::declval<Expr>())), 496 terminal_value(::boost::yap::else_(std::declval<Expr>()))))>> 497 { operator ()boost::yap::detail::transform_expression_tag498 constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const 499 { 500 return (*transforms[hana::llong<I>{}])( 501 expr_tag<remove_cv_ref_t<Expr>::kind>{}, 502 terminal_value(::boost::yap::cond(static_cast<Expr &&>(expr))), 503 terminal_value(::boost::yap::then(static_cast<Expr &&>(expr))), 504 terminal_value( 505 ::boost::yap::else_(static_cast<Expr &&>(expr)))); 506 } 507 }; 508 509 template<typename Expr, typename Transform> 510 struct transform_call_unpacker 511 { 512 template<long long... I> operator ()boost::yap::detail::transform_call_unpacker513 constexpr auto operator()( 514 Expr && expr, 515 Transform & transform, 516 std::integer_sequence<long long, I...>) const 517 -> decltype(transform( 518 expr_tag<expr_kind::call>{}, 519 terminal_value(::boost::yap::get( 520 static_cast<Expr &&>(expr), hana::llong_c<I>))...)) 521 { 522 return transform( 523 expr_tag<expr_kind::call>{}, 524 terminal_value(::boost::yap::get( 525 static_cast<Expr &&>(expr), hana::llong_c<I>))...); 526 } 527 }; 528 529 template<typename Expr> indices_for(Expr const & expr)530 constexpr auto indices_for(Expr const & expr) 531 { 532 constexpr long long size = decltype(hana::size(expr.elements))::value; 533 return std::make_integer_sequence<long long, size>(); 534 } 535 536 template<bool Strict, typename Expr, typename TransformTuple, int I> 537 struct transform_expression_tag< 538 Strict, 539 Expr, 540 TransformTuple, 541 I, 542 expr_arity::n, 543 void_t<decltype( 544 transform_call_unpacker< 545 Expr, 546 decltype(*std::declval<TransformTuple>()[hana::llong<I>{}])>{}( 547 std::declval<Expr>(), 548 *std::declval<TransformTuple>()[hana::llong<I>{}], 549 indices_for(std::declval<Expr>())))>> 550 { operator ()boost::yap::detail::transform_expression_tag551 constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const 552 { 553 using transform_t = decltype(*transforms[hana::llong<I>{}]); 554 return transform_call_unpacker<Expr, transform_t>{}( 555 static_cast<Expr &&>(expr), 556 *transforms[hana::llong<I>{}], 557 indices_for(expr)); 558 } 559 }; 560 561 template<bool Strict, int I, bool IsExprRef> 562 struct transform_impl 563 { 564 template<typename Expr, typename TransformTuple> operator ()boost::yap::detail::transform_impl565 constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const 566 { 567 constexpr expr_kind kind = detail::remove_cv_ref_t<Expr>::kind; 568 return detail::transform_expression_tag< 569 Strict, 570 Expr, 571 TransformTuple, 572 I, 573 detail::arity_of<kind>()>{}( 574 static_cast<Expr &&>(expr), transforms); 575 } 576 }; 577 578 template<bool Strict, int I> 579 struct transform_impl<Strict, I, true> 580 { 581 template<typename Expr, typename TransformTuple> operator ()boost::yap::detail::transform_impl582 constexpr decltype(auto) operator()(Expr && expr, TransformTuple transforms) const 583 { 584 return detail::transform_impl<Strict, I, false>{}( 585 ::boost::yap::deref(static_cast<Expr &&>(expr)), transforms); 586 } 587 }; 588 589 }}} 590 591 #endif 592