blob: 9e6c64b4ddc45fba949f737127f1004369af6bbd [file] [log] [blame]
///////////////////////////////////////////////////////////////////////////////
// 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 <boost/mpl/print.hpp>
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/proto/proto.hpp>
#include <boost/mpl/assert.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/utility/result_of.hpp>
#include <boost/test/unit_test.hpp>
namespace proto = boost::proto;
using proto::_;
struct evaluator
: proto::when<_, proto::_default<evaluator> >
{};
template<typename Ret, typename Expr>
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<evaluator(Expr &)>::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<proto::functional::eval(Expr &, proto::default_context &)>::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<typename T, typename Dummy = proto::is_proto_expr>
struct my_terminal
{
typedef typename proto::terminal<T>::type my_terminal_base;
BOOST_PROTO_BASIC_EXTENDS(my_terminal_base, my_terminal, proto::default_domain)
template<typename A0>
typename proto::result_of::make_expr<proto::tag::function, my_terminal const &, A0 &>::type const
operator()(A0 &a0) const
{
return proto::make_expr<proto::tag::function>(boost::cref(*this), boost::ref(a0));
}
template<typename A0>
typename proto::result_of::make_expr<proto::tag::function, my_terminal const &, A0 const &>::type const
operator()(A0 const &a0) const
{
return proto::make_expr<proto::tag::function>(boost::cref(*this), boost::cref(a0));
}
};
my_terminal<int S::*> 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<int &>(test1(s));
evaluator()(test1(s)) = 0;
BOOST_CHECK_EQUAL(s.x, 0);
// Ditto for reference_wrappers
assert_result_type<int &>(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<int const &>(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<int &>(test1(s));
proto::eval(test1(s), ctx) = 0;
BOOST_CHECK_EQUAL(s.x, 0);
// Ditto for reference_wrappers
assert_result_type<int &>(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<int const &>(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<int &>(test1(&s));
evaluator()(test1(&s)) = 0;
BOOST_CHECK_EQUAL(s.x, 0);
S* ps = &s;
assert_result_type<int &>(test1(ps));
evaluator()(test1(ps)) = 42;
BOOST_CHECK_EQUAL(s.x, 42);
boost::shared_ptr<S> const sp(new S);
BOOST_REQUIRE_EQUAL(sp->x, -42);
// Ditto for shared_ptr (which hook the get_pointer()
// customization point)
assert_result_type<int &>(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<int const &>(test1(&rcs));
int const &s_x0 = evaluator()(test1(&rcs));
BOOST_CHECK_EQUAL(&s.x, &s_x0);
S const *pcs = &s;
assert_result_type<int const &>(test1(pcs));
int const &s_x1 = evaluator()(test1(pcs));
BOOST_CHECK_EQUAL(&s.x, &s_x1);
boost::shared_ptr<S const> spc(new S);
BOOST_REQUIRE_EQUAL(spc->x, -42);
assert_result_type<int const &>(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<int &>(test1(&s));
proto::eval(test1(&s), ctx) = 0;
BOOST_CHECK_EQUAL(s.x, 0);
S* ps = &s;
assert_result_type<int &>(test1(ps));
proto::eval(test1(ps), ctx) = 42;
BOOST_CHECK_EQUAL(s.x, 42);
boost::shared_ptr<S> const sp(new S);
BOOST_REQUIRE_EQUAL(sp->x, -42);
// Ditto for shared_ptr (which hook the get_pointer()
// customization point)
assert_result_type<int &>(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<int const &>(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<int const &>(test1(pcs));
int const &s_x1 = proto::eval(test1(pcs), ctx);
BOOST_CHECK_EQUAL(&s.x, &s_x1);
boost::shared_ptr<S const> spc(new S);
BOOST_REQUIRE_EQUAL(spc->x, -42);
assert_result_type<int const &>(test1(spc));
int const &s_x2 = proto::eval(test1(spc), ctx);
BOOST_CHECK_EQUAL(&spc->x, &s_x2);
}
///////////////////////////////////////////////////////////////////////////////
struct T
{
int x;
};
proto::terminal<int T::*>::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<typename T>
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>
{
U() : dumb_ptr<U>(this), x(42) {}
int x;
};
my_terminal<U *dumb_ptr<U>::*> U_p = {{&U::p}};
my_terminal<int U::*> 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;
}