Boost logo

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