/////////////////////////////////////////////////////////////////////////////// // mem_ptr.hpp // // Copyright 2009 Eric Niebler. Distributed under the Boost // Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include #include #include #include #include #include #include #include namespace proto = boost::proto; using proto::_; struct evaluator : proto::when<_, proto::_default > {}; template void assert_result_type(Expr &) { // check that the return type as calculated by the _default transform // is correct. BOOST_MPL_ASSERT(( boost::is_same< Ret , typename boost::result_of::type > )); // check that the return type as calculated by the default_context // is correct. BOOST_MPL_ASSERT(( boost::is_same< Ret , typename boost::result_of::type > )); } /////////////////////////////////////////////////////////////////////////////// struct S { S() : x(-42) {} int x; }; // like a normal terminal except with an operator() that can // accept non-const lvalues (Proto's only accepts const lvalues) template struct my_terminal { typedef typename proto::terminal::type my_terminal_base; BOOST_PROTO_BASIC_EXTENDS(my_terminal_base, my_terminal, proto::default_domain) template typename proto::result_of::make_expr::type const operator()(A0 &a0) const { return proto::make_expr(boost::ref(*this), boost::ref(a0)); } template typename proto::result_of::make_expr::type const operator()(A0 const &a0) const { return proto::make_expr(boost::ref(*this), boost::ref(a0)); } }; my_terminal test1 = {{ &S::x }}; // Some tests with the default transform void test_refs_transform() { S s; BOOST_REQUIRE_EQUAL(s.x, -42); // Check that evaluating a memptr invocation with a // non-const lvalue argument yields the member as a // non-const lvalue assert_result_type(test1(s)); evaluator()(test1(s)) = 0; BOOST_CHECK_EQUAL(s.x, 0); // Ditto for reference_wrappers assert_result_type(test1(boost::ref(s))); evaluator()(test1(boost::ref(s))) = 42; BOOST_CHECK_EQUAL(s.x, 42); // Check that evaluating a memptr invocation with a // const lvalue argument yields the member as a // const lvalue S const &rcs = s; assert_result_type(test1(rcs)); int const &s_x = evaluator()(test1(rcs)); BOOST_CHECK_EQUAL(&s.x, &s_x); } // Some tests with the default context void test_refs_context() { proto::default_context ctx; S s; BOOST_REQUIRE_EQUAL(s.x, -42); // Check that evaluating a memptr invocation with a // non-const lvalue argument yields the member as a // non-const lvalue assert_result_type(test1(s)); proto::eval(test1(s), ctx) = 0; BOOST_CHECK_EQUAL(s.x, 0); // Ditto for reference_wrappers assert_result_type(test1(boost::ref(s))); proto::eval(test1(boost::ref(s)), ctx) = 42; BOOST_CHECK_EQUAL(s.x, 42); // Check that evaluating a memptr invocation with a // const lvalue argument yields the member as a // const lvalue S const &rcs = s; assert_result_type(test1(rcs)); int const &s_x = proto::eval(test1(rcs), ctx); BOOST_CHECK_EQUAL(&s.x, &s_x); } void test_ptrs_transform() { S s; BOOST_REQUIRE_EQUAL(s.x, -42); // Check that evaluating a memptr invocation with a // pointer to a non-const argument yields the member as a // non-const lvalue assert_result_type(test1(&s)); evaluator()(test1(&s)) = 0; BOOST_CHECK_EQUAL(s.x, 0); S* ps = &s; assert_result_type(test1(ps)); evaluator()(test1(ps)) = 42; BOOST_CHECK_EQUAL(s.x, 42); boost::shared_ptr const sp(new S); BOOST_REQUIRE_EQUAL(sp->x, -42); // Ditto for shared_ptr (which hook the get_pointer() // customization point) assert_result_type(test1(sp)); evaluator()(test1(sp)) = 0; BOOST_CHECK_EQUAL(sp->x, 0); // Check that evaluating a memptr invocation with a // const lvalue argument yields the member as a // const lvalue S const &rcs = s; assert_result_type(test1(&rcs)); int const &s_x0 = evaluator()(test1(&rcs)); BOOST_CHECK_EQUAL(&s.x, &s_x0); S const *pcs = &s; assert_result_type(test1(pcs)); int const &s_x1 = evaluator()(test1(pcs)); BOOST_CHECK_EQUAL(&s.x, &s_x1); boost::shared_ptr spc(new S); BOOST_REQUIRE_EQUAL(spc->x, -42); assert_result_type(test1(spc)); int const &s_x2 = evaluator()(test1(spc)); BOOST_CHECK_EQUAL(&spc->x, &s_x2); } void test_ptrs_context() { proto::default_context ctx; S s; BOOST_REQUIRE_EQUAL(s.x, -42); // Check that evaluating a memptr invocation with a // pointer to a non-const argument yields the member as a // non-const lvalue assert_result_type(test1(&s)); proto::eval(test1(&s), ctx) = 0; BOOST_CHECK_EQUAL(s.x, 0); S* ps = &s; assert_result_type(test1(ps)); proto::eval(test1(ps), ctx) = 42; BOOST_CHECK_EQUAL(s.x, 42); boost::shared_ptr const sp(new S); BOOST_REQUIRE_EQUAL(sp->x, -42); // Ditto for shared_ptr (which hook the get_pointer() // customization point) assert_result_type(test1(sp)); proto::eval(test1(sp), ctx) = 0; BOOST_CHECK_EQUAL(sp->x, 0); // Check that evaluating a memptr invocation with a // const lvalue argument yields the member as a // const lvalue S const &rcs = s; assert_result_type(test1(&rcs)); int const &s_x0 = proto::eval(test1(&rcs), ctx); BOOST_CHECK_EQUAL(&s.x, &s_x0); S const *pcs = &s; assert_result_type(test1(pcs)); int const &s_x1 = proto::eval(test1(pcs), ctx); BOOST_CHECK_EQUAL(&s.x, &s_x1); boost::shared_ptr spc(new S); BOOST_REQUIRE_EQUAL(spc->x, -42); assert_result_type(test1(spc)); int const &s_x2 = proto::eval(test1(spc), ctx); BOOST_CHECK_EQUAL(&spc->x, &s_x2); } /////////////////////////////////////////////////////////////////////////////// struct T { int x; }; proto::terminal::type test2 = { &T::x }; int const *get_pointer(T const &t) { return &t.x; } void with_get_pointer_transform() { T t; evaluator()(test2(t)); } /////////////////////////////////////////////////////////////////////////////// template struct dumb_ptr { dumb_ptr(T *p_) : p(p_) {} friend T const *get_pointer(dumb_ptr const &p) { return p.p; } T *p; }; struct U : dumb_ptr { U() : dumb_ptr(this), x(42) {} int x; }; my_terminal::*> U_p = {{&U::p}}; my_terminal U_x = {{&U::x}}; void potentially_ambiguous_transform() { U u; // This should yield a non-const reference to a pointer-to-const U *&up = evaluator()(U_p(u)); BOOST_CHECK_EQUAL(&up, &u.p); // This should yield a non-const reference to a pointer-to-const int &ux = evaluator()(U_x(u)); BOOST_CHECK_EQUAL(&ux, &u.x); } using namespace boost::unit_test; /////////////////////////////////////////////////////////////////////////////// // init_unit_test_suite // test_suite* init_unit_test_suite( int argc, char* argv[] ) { test_suite *test = BOOST_TEST_SUITE("test handling of member pointers by the default transform and default contexts"); test->add(BOOST_TEST_CASE(&test_refs_transform)); test->add(BOOST_TEST_CASE(&test_refs_context)); test->add(BOOST_TEST_CASE(&test_ptrs_transform)); test->add(BOOST_TEST_CASE(&test_ptrs_context)); test->add(BOOST_TEST_CASE(&with_get_pointer_transform)); test->add(BOOST_TEST_CASE(&potentially_ambiguous_transform)); return test; }