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