Boost.Signals2 を Boost.Parameter を使用して定義する

Boost.Signals2 の signal は、以下の様に複数のテンプレート引数が存在します。

template<typename Signature,   // Function type R (T1, T2, ..., TN)
         typename Combiner = boost::signals2::optional_last_value<R>,
         typename Group = int,
         typename GroupCompare = std::less<Group>,
         typename SlotFunction = boost::function<Signature>,
         typename ExtendedSlotFunction = boost::function<R (const connection &, T1, T2, ..., TN)>,
         typename Mutex = boost::signals2::mutex>
class signal;


しかし、これだと、Group を float 型にして定義したい場合に、

typedef signal<
    void(),
    boost::signals2::optional_last_value<void>,  // これも定義しなければならない
    float
> group_float_signal;

となってしまい、Combiner もユーザ側で定義し直さなければなりません。
と、いうことで、これをどうにかしたかったので、Boost.Parameter を使用してラップしてみました。

[ソース]

#define BOOST_PARAMETER_MAX_ARITY 10
#include <boost/signals2/signal.hpp>
#include <boost/parameter.hpp>
#include <functional>

BOOST_PARAMETER_TEMPLATE_KEYWORD(combiner)
BOOST_PARAMETER_TEMPLATE_KEYWORD(group)
BOOST_PARAMETER_TEMPLATE_KEYWORD(group_compare)
BOOST_PARAMETER_TEMPLATE_KEYWORD(slot_functon)
BOOST_PARAMETER_TEMPLATE_KEYWORD(extended_slot_function)
BOOST_PARAMETER_TEMPLATE_KEYWORD(mutex)

typedef boost::parameter::parameters<
    boost::parameter::optional<tag::combiner>,
    boost::parameter::optional<tag::group>,
    boost::parameter::optional<tag::group_compare>,
    boost::parameter::optional<tag::slot_functon>,
    boost::parameter::optional<tag::extended_slot_function>,
    boost::parameter::optional<tag::mutex>
> class_signature;

namespace detail{

using namespace boost;
using namespace boost::signals2;

template<
    typename Signature,
    typename Arg0 = parameter::void_,
    typename Arg1 = parameter::void_,
    typename Arg2 = parameter::void_,
    typename Arg3 = parameter::void_,
    typename Arg4 = parameter::void_,
    typename Arg5 = parameter::void_
>
struct signal_impl{
    typedef typename class_signature::bind<
        Arg0, Arg1, Arg2, Arg3, Arg4, Arg5
    >::type args;
    
    typedef typename boost::function_traits<Signature>::result_type result_type;
    
    typedef typename parameter::binding<
        args,
        tag::combiner,
        optional_last_value<
            typename boost::function_traits<Signature>::result_type
        >
    >::type combiner;

    typedef typename parameter::binding<args, tag::group, int>::type group;

    typedef typename parameter::binding<
        args, tag::group_compare, std::less<group>
    >::type group_compare;

    typedef typename parameter::binding<
        args,
        tag::slot_functon,
        boost::function<Signature>
    >::type slot_functon;

    typedef typename parameter::binding<
        args,
        tag::extended_slot_function,
        typename signals2::detail::extended_signature<
            function_traits<Signature>::arity,
            Signature
        >::function_type
    >::type extended_slot_function;
    
    typedef typename parameter::binding<
        args, tag::mutex, boost::signals2::mutex
    >::type mutex;

    typedef signals2::signal<
        Signature, combiner, group, group_compare,
        slot_functon, extended_slot_function, mutex
    > type;
};

}  // namespace detai;

template<
    typename Signature,
    typename Combiner             = boost::parameter::void_,
    typename Group                = boost::parameter::void_,
    typename GroupCompare         = boost::parameter::void_,
    typename SlotFunction         = boost::parameter::void_,
    typename ExtendedSlotFunction = boost::parameter::void_,
    typename Mutex                = boost::parameter::void_
>
struct signal : detail::signal_impl<
    Signature, Combiner, Group, GroupCompare, SlotFunction, ExtendedSlotFunction, Mutex
>::type{
    typedef typename detail::signal_impl<
        Signature, Combiner, Group, GroupCompare, SlotFunction, ExtendedSlotFunction, Mutex
    >::type base_type;
    typedef typename base_type::combiner_type      combiner_type;
    typedef typename base_type::group_compare_type group_compare_type;
    signal(
        const combiner_type& combiner = combiner_type(),
        const group_compare_type& group_compare = group_compare_type()
    ) : base_type(combiner, group_compare){}
};


#include <iostream>
#include <boost/lambda/lambda.hpp>

int
main(){
    namespace lambda = boost::lambda;

    signal<void(), group<float> >    sig;

    sig.connect(0.5f, std::cout << lambda::var(", World!!\n"));
    sig.connect(std::cout << lambda::var("... and good morning!\n"));
    sig.connect(0.3f, std::cout << lambda::var("Hello"));
    
    sig();

    return 0;
}

[出力]

Hello, World!!
... and good morning!

signal のテンプレート引数が多いので、コードは長くなっていますが、特に難しい事はしていません。
Boost.Parameter で定義する側はちょっと手間なんですけども、ユーザ側の定義はすっきりするので、こういう時に Boost.Parameter は便利ですね!

[boost]

  • ver 1.47.0