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
17namespace proto = boost::proto;
18using proto::_;
19
20struct evaluator
21 : proto::when<_, proto::_default<evaluator> >
22{};
23
24template<typename Ret, typename Expr>
25void 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///////////////////////////////////////////////////////////////////////////////
47struct 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)
55template<typename T, typename Dummy = proto::is_proto_expr>
56struct 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
76my_terminal<int S::*> test1 = {.proto_expr_: { .child0: &S::x }};
77
78// Some tests with the default transform
79void 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
106void 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
133void 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
180void 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///////////////////////////////////////////////////////////////////////////////
229struct T
230{
231 int x;
232};
233
234proto::terminal<int T::*>::type test2 = { .child0: &T::x };
235
236int const *get_pointer(T const &t)
237{
238 return &t.x;
239}
240
241void with_get_pointer_transform()
242{
243 T t;
244 evaluator()(test2(t));
245}
246
247///////////////////////////////////////////////////////////////////////////////
248template<typename T>
249struct 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
261struct U : dumb_ptr<U>
262{
263 U() : dumb_ptr<U>(this), x(42) {}
264 int x;
265};
266
267my_terminal<U *dumb_ptr<U>::*> U_p = {.proto_expr_: {.child0: &U::p}};
268my_terminal<int U::*> U_x = {.proto_expr_: {.child0: &U::x}};
269
270void 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
284using namespace boost::unit_test;
285///////////////////////////////////////////////////////////////////////////////
286// init_unit_test_suite
287//
288test_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

source code of boost/libs/proto/test/mem_ptr.cpp