Boost logo

Boost Users :

From: Ames Andreas (Andreas.Ames_at_[hidden])
Date: 2006-03-08 14:18:25


Hi all,

I'm new to using the MPL and I want to write a generic class, that
makes it easy to provide other classes with a mechanism to notify
their clients about member changes. I'll paste in some code that
illustrates how I imagine this thing could look like. I've marked
three places with comments ((1), (2), (3)) that contain the specific
questions but I also welcome more general suggestions. For example
I'd appreciate alternatives to the std::map within the Notifier.

<code>

template <class OBJECT_TYPE, typename MEMBER_ID_TYPE, typename ID_TO_MEMBER_MAP_TYPE>
class Notifier
{
    // an alternative combiner for signals
    struct notifier_combiner
    {
        typedef boost::optional< int > result_type;

        template <typename INPUT_ITERATOR>
        result_type operator()(INPUT_ITERATOR pFirst, INPUT_ITERATOR pLast) const
        {
            result_type res;
            for (; pFirst != pLast; ++pFirst)
                res = *pFirst;
            return res;
        }
    };

    template <MEMBER_ID_TYPE id, typename MEMBER_TYPE>
    struct get_signal_type
    {
        typedef boost::signal< int (OBJECT_TYPE const*, MEMBER_TYPE const&), notifier_combiner > type;
    };

    // (1) How can I get SOMETHING? It should be a sequence of all
    // possible signal types, most probably an mpl::set, so that each
    // signal type is only present once. I guess I should iterate
    // over the elements in ID_TO_MEMBER_MAP_TYPE and then call
    // get_signal_type< elem::first::value, elem::second::type >, but
    // I don't see exactly how to use one of the mpl-algorithms or
    // generic iteration.
    typedef boost::make_variant_over< SOMETHING >::type signals_t;

    // maps member ids to a variant containing the 'right' signal type
    typedef std::map< MEMBER_ID_TYPE, signals_t > signal_map_t;

    signal_map_t signal_map;

public:
    Notifier()
    {
        // (2) How do I initialise signal_map so that all ids map to a
        // signals_t instance with the right default constructed
        // signal type assigned? I guess the answer is somewhere in
        // chapter 9 of "C++ template metaprogramming" but that's the
        // chapter that is the hardest one to grasp for me :-)
    }

    template <MEMBER_ID_TYPE id, typename MEMBER_TYPE>
    boost::connection connect(get_signal_type< id, MEMBER_TYPE >::type::slot_type slot)
    {
        return boost::get< get_signal_type< id, MEMBER_TYPE >::type >(signal_map[id]).connect(slot);
    }

    template <MEMBER_ID_TYPE id, typename MEMBER_TYPE>
    int notify(OBJECT_TYPE const *pObj, MEMBER_TYPE const &member_val) const
    {
        boost::optional< int > res = boost::get< get_signal_type< id, MEMBER_TYPE >::type >(signal_map[id])(pObj, member_val);
        return res ? res.get() : 0;
    }
};

// how I'd like to use it:
strcut sth_
{
    int i;
    long j;
}

class RandomClass
{
    bool yesno;

    sth_ sth;

    enum MemberId
    {
      YESNO
      , STH
    };

    typedef mpl::map<
            mpl::pair< mpl::integral_c< MemberId, YESNO >, bool >
            , mpl::pair< mpl::integral_c< MemberId, STH >, sth_ >
> id_to_member_type_t;
    template < typename INTEGRAL_CONST >
    struct get_type_by_id
    {
        typedef mpl::at< id_to_member_type_t, INTEGRAL_CONST >::type type;
    };
    Notifier< RandomClass // I hope incomplete type is ok here
                                // (because Notifier only uses
                                // RandomClass const*; OTOH, Notifier
                                // uses a std::map which somehow
                                // refers to RandomClass)? Anyway,
                                // this is not crucial.
            , MemberId
            , mpl::map<
                      mpl::pair< mpl::integral_c< MemberId, YESNO >, bool >
                      , mpl::pair< mpl::integral_c< MemberId, YESNO >, sth_ >
>
> m_notifier;

public:

    RandomClass()
    : yesno(false)
        , sth({1, 2})
    {
    }

    // a client connects by specifying the member id and a callback
    template < MemberId id >
    boost::connection notify(int (pF*)(RandomClass const*
                                       , get_type_by_id<
                                       mpl::integral_c< MemberId, id >
>::type const&))
    {
        // (3) Can the compiler deduce the second template argument of
        // Notifier::connect? I guess not, so how could I change the
        // method's declaration to make that happen?
        return m_notifier.connect< id >(pF);
    }

    void setYesNo(bool b)
    {
        yesno = b;
        m_notifier<YESNO>(this, b);
    }

    void setSthI(int i)
    {
        sth.i = i;
        m_notifier<STH>(this, sth);
    }
};

</code>

Obviously this doesn't yet compile and there may be all sorts of
errors. I hope that's ok, because I still need very basic help.

TIA,

aa

-- 
Andreas Ames | Programmer | Comergo GmbH |
Voice:  +49 69 7505 3213 | andreas . ames AT comergo . com
"I imagine Sisiphus as happy ..." Albert Camus

Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net