|
Boost : |
From: Paul Mensonides (pmenso57_at_[hidden])
Date: 2006-05-23 16:53:02
> -----Original Message-----
> From: boost-bounces_at_[hidden]
> [mailto:boost-bounces_at_[hidden]] On Behalf Of David Abrahams
> "Matt Calabrese" <rivorus_at_[hidden]> writes:
>
> > I suppose the forwarding of arguments could be an issue as it could
> > imply extra copies, but is that really a reason not to support the
> > operation at all? Forgive me if this has been brought up before.
>
> With Paul M.'s help I have optimized the forwarding generator
> I've been using, and I generalized it so this sort of thing
> should be less painful and generate less code in all its
> permutations (if everyone uses the same template). Enjoy...
In this particular case (operator->*), I don't think combinatorial generation is
necessary. You have the type of the pointer to member and, therefore, all of
the exact parameter types. You can implement it with a general closure
(possibly with bind) so that the implementation of operator->* is reduced to a
single and simple function:
// ---------- closure.hpp ---------- //
#if !BOOST_PP_IS_ITERATING
#ifndef CLOSURE_HPP
#define CLOSURE_HPP "closure.hpp"
#include <boost/preprocessor/facilities/apply.hpp>
#include <boost/preprocessor/iteration/iterate.hpp>
#include <boost/preprocessor/repetition/enum_binary_params.hpp>
#include <boost/preprocessor/repetition/enum_params.hpp>
#include <boost/preprocessor/repetition/enum_trailing_params.hpp>
#include <boost/preprocessor/seq/elem.hpp>
#include <cassert>
// identity
template<class T> struct identity {
typedef T type;
};
// enable_if
template<bool, class R = void> struct enable_if { };
template<class R> struct enable_if<true, R> : identity<R> { };
// map_integral
template<class T, T X> struct map_integral {
static const T value = X;
};
template<class T, T X> const T map_integral<T, X>::value;
// is_function
template<class T> class is_function {
private:
template<class U> static char check(U (*)[1]);
template<class U> static char (& check(...))[2];
public:
static const bool value = sizeof(check<T>(0)) != 1;
};
template<class T> const bool is_function<T>::value;
template<> class is_function<void> : public map_integral<bool, false> { };
template<class T> struct is_function<T&> : public map_integral<bool, true> {
};
// closure
namespace detail {
template<class, class = void> class closure;
template<class D, class C>
class closure<D C::*, typename enable_if<!is_function<D>::value>::type>
: public identity<D&> { };
}
template<class T> class closure : public detail::closure<T> { };
#define cv(n) \
BOOST_PP_APPLY(BOOST_PP_SEQ_ELEM( \
n, (BOOST_PP_NIL)((const))((volatile))((const volatile)) \
)) \
/**/
#define BOOST_PP_ITERATION_PARAMS_1 (3, (0, 3, CLOSURE_HPP))
#include BOOST_PP_ITERATE()
#undef cv
#endif
#elif BOOST_PP_ITERATION_DEPTH() == 1
#define i BOOST_PP_FRAME_ITERATION(1)
template<class D, class C> inline
typename enable_if<
!is_function<D>::value,
cv(i) D&
>::type make_closure(cv(i) C* obj, cv(i) D C::* data) {
assert(obj && data);
return obj->*data;
}
#define BOOST_PP_ITERATION_PARAMS_2 \
(3, (0, CLOSURE_MAX_ARITY ? CLOSURE_MAX_ARITY : 15, CLOSURE_HPP)) \
/**/
#include BOOST_PP_ITERATE()
#undef i
#else
#define j BOOST_PP_ITERATION()
#define member(id) \
R (C::* BOOST_PP_APPLY(id))(BOOST_PP_ENUM_PARAMS(j, P)) cv(i) \
/**/
template<class R, class C BOOST_PP_ENUM_TRAILING_PARAMS(j, class P)>
class closure<member(BOOST_PP_NIL)> {
public:
typedef closure type;
inline closure(cv(i) C* obj, member((func)))
: obj_((assert(obj), obj)), func_((assert(func), func)) {
return;
}
inline R operator()(BOOST_PP_ENUM_BINARY_PARAMS(j, P, p)) const {
return (obj_->*func_)(BOOST_PP_ENUM_PARAMS(j, p));
}
private:
cv(i) C* obj_;
member((func_));
};
template<class R, class C BOOST_PP_ENUM_TRAILING_PARAMS(j, class P)>
inline closure<member(BOOST_PP_NIL)> make_closure(cv(i) C* obj,
member((func))) {
assert(obj && func);
return closure<member(BOOST_PP_NIL)>(obj, func);
}
#undef member
#undef j
#endif
// ---------- test.cpp ---------- //
#include "closure.hpp"
#include <iostream>
#include <memory>
template<class T, class M, class C> inline
typename closure<M C::*>::type operator->*(const std::auto_ptr<T>& sp, M C::*
member) {
return make_closure(&*sp, member);
}
struct A {
inline A(void) : x(0), y(123) {
return;
}
int x;
const int y;
int get(void) {
return x;
}
void set(int y) {
x = y;
return;
}
};
int main(void) {
int A::* px = &A::x;
int (A::* pget)(void) = &A::get;
void (A::* pset)(int) = &A::set;
std::auto_ptr<A> sp(new A);
sp->*px = 1;
(sp->*pset)(sp->*px + 1);
std::cout << (sp->*pget)() << ' ' << sp->*&A::y << '\n';
return 0;
}
The code generation yields specializations of 'closure' for different arities
and cv-qualifications of pointer-to-member-function types. It also yields
overloads of 'make_closure' for different arities and cv-qualifications of
pointer-to-member-function types and overloads of 'make_closure' for different
cv-qualifications of pointer-to-data-member types. The specializations of
'closure' and overloads of 'make_closure' are fairly straightforward. E.g. for
a pointer-to-const-qualified-ternary-member-function:
template<class R, class C, class P0, class P1, class P2>
class closure<R (C::*)(P0, P1, P2) const> {
public:
typedef closure type;
inline closure(const C* obj, R (C::* func)(P0, P1, P2) const)
: obj_((assert(obj), obj)), func_((assert(func), func)) {
return;
}
inline R operator()(P0 p0, P1 p1, P2 p2) const {
return (obj_->*func_)(p0, p1, p2);
}
private:
const C* obj_;
R (C::* func_)(P0, P1, P2) const;
};
template<class R, class C, class P0, class P1, class P2>
inline closure<R (C::*)(P0, P1, P2) const>
make_closure(const C* obj, R (C::* func)(P0, P1, P2) const) {
assert(obj && func);
return closure<R (C::*)(P0, P1, P2) const>(obj, func);
}
The overloads of 'make_closure' for pointer-to-data-members types are similarly
simple. E.g. for a pointer-to-const-qualified-data-member:
template<class D, class C> inline
typename enable_if<
!is_function<D>::value,
const D&
>::type make_closure(const C* obj, const D C::* data) {
assert(obj && data);
return obj->*data;
}
The '::type' member of 'closure' and the 'make_closure' functions are used to
generalize the syntax of an operator->* overload into one function. When you
say something like:
template<class T, class M, class C> inline
typename closure<M C::*>::type operator->*(const std::auto_ptr<T>& sp, M C::*
member) {
return make_closure(&*sp, member);
}
The return type 'closure<M C::*>::type' is a (possibly cv-qualified) reference
to 'M' if 'M C::*' is a pointer-to-data-member. If 'M C::*' is a
pointer-to-member-function, the return type 'closure<M C::*>::type' is
'closure<M C::*>'. The 'make_closure' overloads similarly generalize the
difference between pointers-to-member-functions and pointers-to-data-members.
Basically, the implementation above does all the work necessary to implement
operator->* once and for all (for any number of different smart pointers). The
only things that one might need to change for a particular smart pointer is how
the smart pointer is passed to operator->* and how the raw pointer is accessed:
template<class T, class M, class C> inline
typename closure<M C::*>::type operator->*(boost::shared_ptr<T> sp, M C::*
member) {
return make_closure(sp.get(), member);
}
template<class T> class smart_ptr {
// ...
public:
template<class M, class C> inline
typename closure<M C::*>::type operator->*(M C::* member) {
return make_closure(get(), member);
}
// ...
};
Long story short, I don't think that there is a serious forwarding problem here,
nor is there a significant code size overhead on a per smart pointer
implementation to supply this operator.
One other note before I forget: If a smart pointer supplies an implicit
conversion to T*, none of the above is necessary at all because the built-in
operator->* is a free function (allowing the conversion on the first argument
type). E.g. if you have:
template<class T> class smart_ptr {
// ...
public:
inline operator T*(void) const {
return get();
}
// ...
};
...operator->* should already work (without any forwarding at all).
Regards,
Paul Mensonides
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk