let.cpp 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  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. //[ let
  7. #include <boost/yap/yap.hpp>
  8. #include <boost/hana/map.hpp>
  9. #include <boost/hana/at_key.hpp>
  10. #include <boost/hana/contains.hpp>
  11. #include <boost/hana/keys.hpp>
  12. #include <vector>
  13. #include <iostream>
  14. // Here, we introduce special let-placeholders, so we can use them along side
  15. // the normal YAP placeholders without getting them confused.
  16. template<long long I>
  17. struct let_placeholder : boost::hana::llong<I>
  18. {
  19. };
  20. // Replaces each let-terminal with the expression with which it was
  21. // initialized in let(). So in 'let(_a = foo)[ _a + 1 ]', this transform will
  22. // be used on '_a + 1' to replace '_a' with 'foo'. The map_ member holds the
  23. // mapping of let-placeholders to their initializers.
  24. template<typename ExprMap>
  25. struct let_terminal_transform
  26. {
  27. // This matches only let-placeholders. For each one matched, we look up
  28. // its initializer in map_ and return it.
  29. template<long long I>
  30. auto operator()(
  31. boost::yap::expr_tag<boost::yap::expr_kind::terminal>,
  32. let_placeholder<I> i)
  33. {
  34. // If we have an entry in map_ for this placeholder, return the value
  35. // of the entry. Otherwise, pass i through as a terminal.
  36. if constexpr (boost::hana::contains(
  37. decltype(boost::hana::keys(map_))(),
  38. boost::hana::llong_c<I>)) {
  39. return map_[boost::hana::llong_c<I>];
  40. } else {
  41. return boost::yap::make_terminal(i);
  42. }
  43. }
  44. ExprMap map_;
  45. };
  46. // As you can see below, let() is an eager function; this template is used for
  47. // its return values. It contains the mapping from let-placeholders to
  48. // initializer expressions used to transform the expression inside '[]' after
  49. // a let()'. It also has an operator[]() member function that takes the
  50. // expression inside '[]' and returns a version of it with the
  51. // let-placeholders replaced.
  52. template<typename ExprMap>
  53. struct let_result
  54. {
  55. template<typename Expr>
  56. auto operator[](Expr && expr)
  57. {
  58. return boost::yap::transform(
  59. std::forward<Expr>(expr), let_terminal_transform<ExprMap>{map_});
  60. }
  61. ExprMap map_;
  62. };
  63. // Processes the expressions passed to let() one at a time, adding each one to
  64. // a Hana map of hana::llong<>s to YAP expressions.
  65. template<typename Map, typename Expr, typename... Exprs>
  66. auto let_impl(Map && map, Expr && expr, Exprs &&... exprs)
  67. {
  68. static_assert(
  69. Expr::kind == boost::yap::expr_kind::assign,
  70. "Expressions passed to let() must be of the form placeholder = Expression");
  71. if constexpr (sizeof...(Exprs) == 0) {
  72. using I = typename std::remove_reference<decltype(
  73. boost::yap::value(boost::yap::left(expr)))>::type;
  74. auto const i = boost::hana::llong_c<I::value>;
  75. using map_t = decltype(boost::hana::insert(
  76. map, boost::hana::make_pair(i, boost::yap::right(expr))));
  77. return let_result<map_t>{boost::hana::insert(
  78. map, boost::hana::make_pair(i, boost::yap::right(expr)))};
  79. } else {
  80. using I = typename std::remove_reference<decltype(
  81. boost::yap::value(boost::yap::left(expr)))>::type;
  82. auto const i = boost::hana::llong_c<I::value>;
  83. return let_impl(
  84. boost::hana::insert(
  85. map, boost::hana::make_pair(i, boost::yap::right(expr))),
  86. std::forward<Exprs>(exprs)...);
  87. }
  88. }
  89. // Takes N > 0 expressions of the form 'placeholder = expr', and returns an
  90. // object with an overloaded operator[]().
  91. template<typename Expr, typename... Exprs>
  92. auto let(Expr && expr, Exprs &&... exprs)
  93. {
  94. return let_impl(
  95. boost::hana::make_map(),
  96. std::forward<Expr>(expr),
  97. std::forward<Exprs>(exprs)...);
  98. }
  99. int main()
  100. {
  101. // Some handy terminals -- the _a and _b let-placeholders and std::cout as
  102. // a YAP terminal.
  103. boost::yap::expression<
  104. boost::yap::expr_kind::terminal,
  105. boost::hana::tuple<let_placeholder<0>>> const _a;
  106. boost::yap::expression<
  107. boost::yap::expr_kind::terminal,
  108. boost::hana::tuple<let_placeholder<1>>> const _b;
  109. auto const cout = boost::yap::make_terminal(std::cout);
  110. using namespace boost::yap::literals;
  111. {
  112. auto expr = let(_a = 2)[_a + 1];
  113. assert(boost::yap::evaluate(expr) == 3);
  114. }
  115. {
  116. auto expr = let(_a = 123, _b = 456)[_a + _b];
  117. assert(boost::yap::evaluate(expr) == 123 + 456);
  118. }
  119. // This prints out "0 0", because 'i' is passed as an lvalue, so its
  120. // decrement is visible outside the let expression.
  121. {
  122. int i = 1;
  123. boost::yap::evaluate(let(_a = 1_p)[cout << --_a << ' '], i);
  124. std::cout << i << std::endl;
  125. }
  126. // Prints "Hello, World" due to let()'s scoping rules.
  127. {
  128. boost::yap::evaluate(
  129. let(_a = 1_p, _b = 2_p)
  130. [
  131. // _a here is an int: 1
  132. let(_a = 3_p) // hides the outer _a
  133. [
  134. cout << _a << _b // prints "Hello, World"
  135. ]
  136. ],
  137. 1, " World", "Hello,"
  138. );
  139. }
  140. std::cout << "\n";
  141. // Due to the macro-substitution style that this example uses, this prints
  142. // "3132". Phoenix's let() prints "312", because it only evaluates '1_p
  143. // << 3' once.
  144. {
  145. boost::yap::evaluate(
  146. let(_a = 1_p << 3)
  147. [
  148. _a << "1", _a << "2"
  149. ],
  150. std::cout
  151. );
  152. }
  153. std::cout << "\n";
  154. }
  155. //]