1 | /////////////////////////////////////////////////////////////////////////////// |
2 | // mem_ptr.hpp |
3 | // |
4 | // Copyright 2009 Eric Niebler. Distributed under the Boost |
5 | // Software License, Version 1.0. (See accompanying file |
6 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
7 | |
8 | #include <boost/mpl/print.hpp> |
9 | #include <iostream> |
10 | #include <boost/shared_ptr.hpp> |
11 | #include <boost/proto/proto.hpp> |
12 | #include <boost/mpl/assert.hpp> |
13 | #include <boost/type_traits/is_same.hpp> |
14 | #include <boost/utility/result_of.hpp> |
15 | #include <boost/test/unit_test.hpp> |
16 | |
17 | namespace proto = boost::proto; |
18 | using proto::_; |
19 | |
20 | struct evaluator |
21 | : proto::when<_, proto::_default<evaluator> > |
22 | {}; |
23 | |
24 | template<typename Ret, typename Expr> |
25 | void assert_result_type(Expr &) |
26 | { |
27 | // check that the return type as calculated by the _default transform |
28 | // is correct. |
29 | BOOST_MPL_ASSERT(( |
30 | boost::is_same< |
31 | Ret |
32 | , typename boost::result_of<evaluator(Expr &)>::type |
33 | > |
34 | )); |
35 | |
36 | // check that the return type as calculated by the default_context |
37 | // is correct. |
38 | BOOST_MPL_ASSERT(( |
39 | boost::is_same< |
40 | Ret |
41 | , typename boost::result_of<proto::functional::eval(Expr &, proto::default_context &)>::type |
42 | > |
43 | )); |
44 | } |
45 | |
46 | /////////////////////////////////////////////////////////////////////////////// |
47 | struct S |
48 | { |
49 | S() : x(-42) {} |
50 | int x; |
51 | }; |
52 | |
53 | // like a normal terminal except with an operator() that can |
54 | // accept non-const lvalues (Proto's only accepts const lvalues) |
55 | template<typename T, typename Dummy = proto::is_proto_expr> |
56 | struct my_terminal |
57 | { |
58 | typedef typename proto::terminal<T>::type my_terminal_base; |
59 | BOOST_PROTO_BASIC_EXTENDS(my_terminal_base, my_terminal, proto::default_domain) |
60 | |
61 | template<typename A0> |
62 | typename proto::result_of::make_expr<proto::tag::function, my_terminal const &, A0 &>::type const |
63 | operator()(A0 &a0) const |
64 | { |
65 | return proto::make_expr<proto::tag::function>(boost::ref(*this), boost::ref(a0)); |
66 | } |
67 | |
68 | template<typename A0> |
69 | typename proto::result_of::make_expr<proto::tag::function, my_terminal const &, A0 const &>::type const |
70 | operator()(A0 const &a0) const |
71 | { |
72 | return proto::make_expr<proto::tag::function>(boost::ref(*this), boost::ref(a0)); |
73 | } |
74 | }; |
75 | |
76 | my_terminal<int S::*> test1 = {.proto_expr_: { .child0: &S::x }}; |
77 | |
78 | // Some tests with the default transform |
79 | void test_refs_transform() |
80 | { |
81 | S s; |
82 | BOOST_REQUIRE_EQUAL(s.x, -42); |
83 | |
84 | // Check that evaluating a memptr invocation with a |
85 | // non-const lvalue argument yields the member as a |
86 | // non-const lvalue |
87 | assert_result_type<int &>(test1(s)); |
88 | evaluator()(test1(s)) = 0; |
89 | BOOST_CHECK_EQUAL(s.x, 0); |
90 | |
91 | // Ditto for reference_wrappers |
92 | assert_result_type<int &>(test1(boost::ref(t&: s))); |
93 | evaluator()(test1(boost::ref(t&: s))) = 42; |
94 | BOOST_CHECK_EQUAL(s.x, 42); |
95 | |
96 | // Check that evaluating a memptr invocation with a |
97 | // const lvalue argument yields the member as a |
98 | // const lvalue |
99 | S const &rcs = s; |
100 | assert_result_type<int const &>(test1(rcs)); |
101 | int const &s_x = evaluator()(test1(rcs)); |
102 | BOOST_CHECK_EQUAL(&s.x, &s_x); |
103 | } |
104 | |
105 | // Some tests with the default context |
106 | void test_refs_context() |
107 | { |
108 | proto::default_context ctx; |
109 | S s; |
110 | BOOST_REQUIRE_EQUAL(s.x, -42); |
111 | |
112 | // Check that evaluating a memptr invocation with a |
113 | // non-const lvalue argument yields the member as a |
114 | // non-const lvalue |
115 | assert_result_type<int &>(test1(s)); |
116 | proto::eval(e: test1(s), ctx) = 0; |
117 | BOOST_CHECK_EQUAL(s.x, 0); |
118 | |
119 | // Ditto for reference_wrappers |
120 | assert_result_type<int &>(test1(boost::ref(t&: s))); |
121 | proto::eval(e: test1(boost::ref(t&: s)), ctx) = 42; |
122 | BOOST_CHECK_EQUAL(s.x, 42); |
123 | |
124 | // Check that evaluating a memptr invocation with a |
125 | // const lvalue argument yields the member as a |
126 | // const lvalue |
127 | S const &rcs = s; |
128 | assert_result_type<int const &>(test1(rcs)); |
129 | int const &s_x = proto::eval(e: test1(rcs), ctx); |
130 | BOOST_CHECK_EQUAL(&s.x, &s_x); |
131 | } |
132 | |
133 | void test_ptrs_transform() |
134 | { |
135 | S s; |
136 | BOOST_REQUIRE_EQUAL(s.x, -42); |
137 | |
138 | // Check that evaluating a memptr invocation with a |
139 | // pointer to a non-const argument yields the member as a |
140 | // non-const lvalue |
141 | assert_result_type<int &>(test1(&s)); |
142 | evaluator()(test1(&s)) = 0; |
143 | BOOST_CHECK_EQUAL(s.x, 0); |
144 | |
145 | S* ps = &s; |
146 | assert_result_type<int &>(test1(ps)); |
147 | evaluator()(test1(ps)) = 42; |
148 | BOOST_CHECK_EQUAL(s.x, 42); |
149 | |
150 | boost::shared_ptr<S> const sp(new S); |
151 | BOOST_REQUIRE_EQUAL(sp->x, -42); |
152 | |
153 | // Ditto for shared_ptr (which hook the get_pointer() |
154 | // customization point) |
155 | assert_result_type<int &>(test1(sp)); |
156 | evaluator()(test1(sp)) = 0; |
157 | BOOST_CHECK_EQUAL(sp->x, 0); |
158 | |
159 | // Check that evaluating a memptr invocation with a |
160 | // const lvalue argument yields the member as a |
161 | // const lvalue |
162 | S const &rcs = s; |
163 | assert_result_type<int const &>(test1(&rcs)); |
164 | int const &s_x0 = evaluator()(test1(&rcs)); |
165 | BOOST_CHECK_EQUAL(&s.x, &s_x0); |
166 | |
167 | S const *pcs = &s; |
168 | assert_result_type<int const &>(test1(pcs)); |
169 | int const &s_x1 = evaluator()(test1(pcs)); |
170 | BOOST_CHECK_EQUAL(&s.x, &s_x1); |
171 | |
172 | boost::shared_ptr<S const> spc(new S); |
173 | BOOST_REQUIRE_EQUAL(spc->x, -42); |
174 | |
175 | assert_result_type<int const &>(test1(spc)); |
176 | int const &s_x2 = evaluator()(test1(spc)); |
177 | BOOST_CHECK_EQUAL(&spc->x, &s_x2); |
178 | } |
179 | |
180 | void test_ptrs_context() |
181 | { |
182 | proto::default_context ctx; |
183 | S s; |
184 | BOOST_REQUIRE_EQUAL(s.x, -42); |
185 | |
186 | // Check that evaluating a memptr invocation with a |
187 | // pointer to a non-const argument yields the member as a |
188 | // non-const lvalue |
189 | assert_result_type<int &>(test1(&s)); |
190 | proto::eval(e: test1(&s), ctx) = 0; |
191 | BOOST_CHECK_EQUAL(s.x, 0); |
192 | |
193 | S* ps = &s; |
194 | assert_result_type<int &>(test1(ps)); |
195 | proto::eval(e: test1(ps), ctx) = 42; |
196 | BOOST_CHECK_EQUAL(s.x, 42); |
197 | |
198 | boost::shared_ptr<S> const sp(new S); |
199 | BOOST_REQUIRE_EQUAL(sp->x, -42); |
200 | |
201 | // Ditto for shared_ptr (which hook the get_pointer() |
202 | // customization point) |
203 | assert_result_type<int &>(test1(sp)); |
204 | proto::eval(e: test1(sp), ctx) = 0; |
205 | BOOST_CHECK_EQUAL(sp->x, 0); |
206 | |
207 | // Check that evaluating a memptr invocation with a |
208 | // const lvalue argument yields the member as a |
209 | // const lvalue |
210 | S const &rcs = s; |
211 | assert_result_type<int const &>(test1(&rcs)); |
212 | int const &s_x0 = proto::eval(e: test1(&rcs), ctx); |
213 | BOOST_CHECK_EQUAL(&s.x, &s_x0); |
214 | |
215 | S const *pcs = &s; |
216 | assert_result_type<int const &>(test1(pcs)); |
217 | int const &s_x1 = proto::eval(e: test1(pcs), ctx); |
218 | BOOST_CHECK_EQUAL(&s.x, &s_x1); |
219 | |
220 | boost::shared_ptr<S const> spc(new S); |
221 | BOOST_REQUIRE_EQUAL(spc->x, -42); |
222 | |
223 | assert_result_type<int const &>(test1(spc)); |
224 | int const &s_x2 = proto::eval(e: test1(spc), ctx); |
225 | BOOST_CHECK_EQUAL(&spc->x, &s_x2); |
226 | } |
227 | |
228 | /////////////////////////////////////////////////////////////////////////////// |
229 | struct T |
230 | { |
231 | int x; |
232 | }; |
233 | |
234 | proto::terminal<int T::*>::type test2 = { .child0: &T::x }; |
235 | |
236 | int const *get_pointer(T const &t) |
237 | { |
238 | return &t.x; |
239 | } |
240 | |
241 | void with_get_pointer_transform() |
242 | { |
243 | T t; |
244 | evaluator()(test2(t)); |
245 | } |
246 | |
247 | /////////////////////////////////////////////////////////////////////////////// |
248 | template<typename T> |
249 | struct dumb_ptr |
250 | { |
251 | dumb_ptr(T *p_) : p(p_) {} |
252 | |
253 | friend T const *get_pointer(dumb_ptr const &p) |
254 | { |
255 | return p.p; |
256 | } |
257 | |
258 | T *p; |
259 | }; |
260 | |
261 | struct U : dumb_ptr<U> |
262 | { |
263 | U() : dumb_ptr<U>(this), x(42) {} |
264 | int x; |
265 | }; |
266 | |
267 | my_terminal<U *dumb_ptr<U>::*> U_p = {.proto_expr_: {.child0: &U::p}}; |
268 | my_terminal<int U::*> U_x = {.proto_expr_: {.child0: &U::x}}; |
269 | |
270 | void potentially_ambiguous_transform() |
271 | { |
272 | U u; |
273 | |
274 | // This should yield a non-const reference to a pointer-to-const |
275 | U *&up = evaluator()(U_p(u)); |
276 | BOOST_CHECK_EQUAL(&up, &u.p); |
277 | |
278 | // This should yield a non-const reference to a pointer-to-const |
279 | int &ux = evaluator()(U_x(u)); |
280 | BOOST_CHECK_EQUAL(&ux, &u.x); |
281 | } |
282 | |
283 | |
284 | using namespace boost::unit_test; |
285 | /////////////////////////////////////////////////////////////////////////////// |
286 | // init_unit_test_suite |
287 | // |
288 | test_suite* init_unit_test_suite( int argc, char* argv[] ) |
289 | { |
290 | test_suite *test = BOOST_TEST_SUITE("test handling of member pointers by the default transform and default contexts" ); |
291 | |
292 | test->add(BOOST_TEST_CASE(&test_refs_transform)); |
293 | test->add(BOOST_TEST_CASE(&test_refs_context)); |
294 | |
295 | test->add(BOOST_TEST_CASE(&test_ptrs_transform)); |
296 | test->add(BOOST_TEST_CASE(&test_ptrs_context)); |
297 | |
298 | test->add(BOOST_TEST_CASE(&with_get_pointer_transform)); |
299 | |
300 | test->add(BOOST_TEST_CASE(&potentially_ambiguous_transform)); |
301 | |
302 | return test; |
303 | } |
304 | |