
-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of David Abrahams
"Matt Calabrese" <rivorus@gmail.com> 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