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>
assert_result_type(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 {
SS49 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;
BOOST_PROTO_BASIC_EXTENDSmy_terminal59 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
operator ()my_terminal70 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 = {{ &S::x }};
77
78 // Some tests with the default transform
test_refs_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(s)));
93 evaluator()(test1(boost::ref(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
test_refs_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(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(s)));
121 proto::eval(test1(boost::ref(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(test1(rcs), ctx);
130 BOOST_CHECK_EQUAL(&s.x, &s_x);
131 }
132
test_ptrs_transform()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
test_ptrs_context()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(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(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(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(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(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(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 = { &T::x };
235
get_pointer(T const & t)236 int const *get_pointer(T const &t)
237 {
238 return &t.x;
239 }
240
with_get_pointer_transform()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 {
dumb_ptrdumb_ptr251 dumb_ptr(T *p_) : p(p_) {}
252
get_pointer(dumb_ptr const & p)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 {
UU263 U() : dumb_ptr<U>(this), x(42) {}
264 int x;
265 };
266
267 my_terminal<U *dumb_ptr<U>::*> U_p = {{&U::p}};
268 my_terminal<int U::*> U_x = {{&U::x}};
269
potentially_ambiguous_transform()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 //
init_unit_test_suite(int argc,char * argv[])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