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