// Boost.Signals library



// Copyright Douglas Gregor 2001-2004. Use, modification and

// distribution is subject to the Boost Software License, Version

// 1.0. (See accompanying file LICENSE_1_0.txt or copy at

// http://www.boost.org/LICENSE_1_0.txt)



// For more information, see http://www.boost.org



// This file intentionally does not have include guards, because it is meant

// to be included multiple times (one for each signalN class). The

// BOOST_SIGNALS_SIGNAL_TEMPLATE_HEADER_INCLUDED macro merely serves to

// suppress reinclusion of the files that this header depends on.



#ifndef BOOST_SIGNALS_SIGNAL_TEMPLATE_HEADER_INCLUDED

#define BOOST_SIGNALS_SIGNAL_TEMPLATE_HEADER_INCLUDED

#  include <boost/config.hpp>

#  include <boost/signals/connection.hpp>

#  include <boost/utility.hpp>

#  include <boost/ref.hpp>

#  include <boost/signals/slot.hpp>

#  include <boost/last_value.hpp>

#  include <boost/signals/detail/signal_base.hpp>

#  include <boost/signals/detail/slot_call_iterator.hpp>

#  include <boost/mpl/bool.hpp>

#  include <boost/type_traits/is_convertible.hpp>

#  include <cassert>

#  include <functional>

#  include <memory>

#endif // !BOOST_SIGNALS_SIGNAL_TEMPLATE_HEADER_INCLUDED



#ifdef BOOST_HAS_ABI_HEADERS

#  include BOOST_ABI_PREFIX

#endif



// Include the appropriate functionN header

#define BOOST_SIGNAL_FUNCTION_N_HEADER BOOST_JOIN(<boost/function/function,BOOST_SIGNALS_NUM_ARGS.hpp>)

#include BOOST_SIGNAL_FUNCTION_N_HEADER



// Determine if a comma should follow a listing of the arguments/parameters

#if BOOST_SIGNALS_NUM_ARGS == 0

#  define BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS

#else

#  define BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS ,

#endif // BOOST_SIGNALS_NUM_ARGS > 0



// Define class names used

#define BOOST_SIGNALS_SIGNAL BOOST_JOIN(signal,BOOST_SIGNALS_NUM_ARGS)

#define BOOST_SIGNALS_FUNCTION BOOST_JOIN(function,BOOST_SIGNALS_NUM_ARGS)

#define BOOST_SIGNALS_ARGS_STRUCT BOOST_JOIN(args,BOOST_SIGNALS_NUM_ARGS)

#define BOOST_SIGNALS_CALL_BOUND BOOST_JOIN(call_bound,BOOST_SIGNALS_NUM_ARGS)



// Define commonly-used instantiations

#define BOOST_SIGNALS_ARGS_STRUCT_INST \

  BOOST_SIGNALS_NAMESPACE::detail::BOOST_SIGNALS_ARGS_STRUCT<BOOST_SIGNALS_TEMPLATE_ARGS>



namespace boost {

  namespace BOOST_SIGNALS_NAMESPACE {

    namespace detail {

      // Holds the arguments for a bound slot call in a single place

      template<BOOST_SIGNALS_TEMPLATE_PARMS

               BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS

               typename Dummy = int>

      struct BOOST_SIGNALS_ARGS_STRUCT {

        BOOST_SIGNALS_ARGS_STRUCT(BOOST_SIGNALS_COPY_PARMS)

          BOOST_SIGNALS_INIT_ARGS

        {

        }



        BOOST_SIGNALS_ARGS_AS_MEMBERS

      };



      // Function object that calls the function object given to it, passing

      // the bound arguments along to that underlying function object

      template<typename R>

      struct BOOST_SIGNALS_CALL_BOUND {

        template<BOOST_SIGNALS_TEMPLATE_PARMS

                 BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS

                 typename F>

        struct caller {

          typedef BOOST_SIGNALS_ARGS_STRUCT<BOOST_SIGNALS_TEMPLATE_ARGS>*

            args_type;



          args_type args;



          typedef R result_type;



          caller() {}

          caller(args_type a) : args(a) {}



          template<typename Pair>

          R operator()(const Pair& slot) const

          {

            F* target = const_cast<F*>(unsafe_any_cast<F>(&slot.second));

            return (*target)(BOOST_SIGNALS_BOUND_ARGS);

          }

        };

      };



      template<>

      struct BOOST_SIGNALS_CALL_BOUND<void> {

        template<BOOST_SIGNALS_TEMPLATE_PARMS

                 BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS

                 typename F>

        struct caller {

          typedef BOOST_SIGNALS_ARGS_STRUCT<BOOST_SIGNALS_TEMPLATE_ARGS>*

            args_type;



          args_type args;



          typedef unusable result_type;



          caller(args_type a) : args(a) {}



          template<typename Pair>

          unusable operator()(const Pair& slot) const

          {

            F* target = const_cast<F*>(unsafe_any_cast<F>(&slot.second));

            (*target)(BOOST_SIGNALS_BOUND_ARGS);

            return unusable();

          }

        };

      };

    } // namespace detail

  } // namespace BOOST_SIGNALS_NAMESPACE



  // The actual signalN class

  template<

    typename R,

    BOOST_SIGNALS_TEMPLATE_PARMS

    BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS

    typename Combiner = last_value<R>,

    typename Group = int,

    typename GroupCompare = std::less<Group>,

    typename SlotFunction = BOOST_SIGNALS_FUNCTION<

                              R BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS

                              BOOST_SIGNALS_TEMPLATE_ARGS>

  >

  class BOOST_SIGNALS_SIGNAL :

    public BOOST_SIGNALS_NAMESPACE::detail::signal_base, // management of slot list

    public BOOST_SIGNALS_NAMESPACE::trackable // signals are trackable

  {

  public:

    // The slot function type

    typedef SlotFunction slot_function_type;



    // Result type of a slot

    typedef typename BOOST_SIGNALS_NAMESPACE::detail::slot_result_type<R>::type

      slot_result_type;



    // Argument types

    BOOST_SIGNALS_ARG_TYPES



#if BOOST_SIGNALS_NUM_ARGS == 1

    typedef T1 argument_type;

#elif BOOST_SIGNALS_NUM_ARGS == 2

    typedef T1 first_argument_type;

    typedef T2 second_argument_type;

#endif



  private:

    // The real slot name comparison object type

    typedef BOOST_SIGNALS_NAMESPACE::detail::group_bridge_compare<GroupCompare, Group>

      real_group_compare_type;



    // The function object passed to the slot call iterator that will call

    // the underlying slot function with its arguments bound

    typedef BOOST_SIGNALS_NAMESPACE::detail::BOOST_SIGNALS_CALL_BOUND<R>

      outer_bound_slot_caller;

    typedef typename outer_bound_slot_caller::template

              caller<BOOST_SIGNALS_TEMPLATE_ARGS

                     BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS

                     slot_function_type>

      call_bound_slot;



  public:

    // Combiner's result type

    typedef typename Combiner::result_type result_type;



    // Combiner type

    typedef Combiner combiner_type;



    // Slot type

    typedef slot<slot_function_type> slot_type;



    // Slot name type and comparison

    typedef Group group_type;

    typedef GroupCompare group_compare_type;



    typedef BOOST_SIGNALS_NAMESPACE::detail::slot_call_iterator<

              call_bound_slot, iterator> slot_call_iterator;



    explicit

    BOOST_SIGNALS_SIGNAL(const Combiner& c = Combiner(),

                         const GroupCompare& comp = GroupCompare()) :

      BOOST_SIGNALS_NAMESPACE::detail::signal_base(real_group_compare_type(comp),

                                                   c)

    {

    }



    // Connect a slot to this signal

    BOOST_SIGNALS_NAMESPACE::connection

    connect(const slot_type&,

            BOOST_SIGNALS_NAMESPACE::connect_position at

              = BOOST_SIGNALS_NAMESPACE::at_back);





    BOOST_SIGNALS_NAMESPACE::connection

    connect(const group_type&, const slot_type&,

            BOOST_SIGNALS_NAMESPACE::connect_position at

              = BOOST_SIGNALS_NAMESPACE::at_back);



#if BOOST_WORKAROUND(BOOST_MSVC, <= 1300)

    // MSVC 6.0 and 7.0 don't handle the is_convertible test well

    void disconnect(const group_type& group)

    {

      impl->disconnect(group);

    }

#else

    template<typename T>

    void disconnect(const T& t)

    {

      typedef mpl::bool_<(is_convertible<T, group_type>::value)> is_group;

      this->do_disconnect(t, is_group());

    }



  private:

    // Disconnect a named slot

    void do_disconnect(const group_type& group, mpl::bool_<true>)

    {

      impl->disconnect(group);

    }



    template<typename Function>

    void do_disconnect(const Function& f, mpl::bool_<false>)

    {

      // Notify the slot handling code that we are iterating through the slots

      BOOST_SIGNALS_NAMESPACE::detail::call_notification notification(this->impl);



      for (iterator i = impl->slots_.begin(); i != impl->slots_.end(); ++i) {

        slot_function_type& s = *unsafe_any_cast<slot_function_type>(&i->second);

        if (s == f) i->first.disconnect();

      }

    }

#endif



  public:



    // Emit the signal

    result_type operator()(BOOST_SIGNALS_PARMS);

    result_type operator()(BOOST_SIGNALS_PARMS) const;



    Combiner& combiner()

    { return *unsafe_any_cast<Combiner>(&impl->combiner_); }



    const Combiner& combiner() const

    { return *unsafe_any_cast<const Combiner>(&impl->combiner_); }

  };



  template<

    typename R,

    BOOST_SIGNALS_TEMPLATE_PARMS

    BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS

    typename Combiner,

    typename Group,

    typename GroupCompare,

    typename SlotFunction

  >

  BOOST_SIGNALS_NAMESPACE::connection

  BOOST_SIGNALS_SIGNAL<

    R, BOOST_SIGNALS_TEMPLATE_ARGS

    BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS

    Combiner, Group, GroupCompare, SlotFunction

  >::connect(const slot_type& in_slot,

             BOOST_SIGNALS_NAMESPACE::connect_position at)

  {

    using boost::BOOST_SIGNALS_NAMESPACE::detail::stored_group;



    // If the slot has been disconnected, just return a disconnected

    // connection

    if (!in_slot.is_active()) {

      return BOOST_SIGNALS_NAMESPACE::connection();

    }



    return impl->connect_slot(in_slot.get_slot_function(), stored_group(),

                              in_slot.get_data(), at);

  }



  template<

    typename R,

    BOOST_SIGNALS_TEMPLATE_PARMS

    BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS

    typename Combiner,

    typename Group,

    typename GroupCompare,

    typename SlotFunction

  >

  BOOST_SIGNALS_NAMESPACE::connection

  BOOST_SIGNALS_SIGNAL<

    R, BOOST_SIGNALS_TEMPLATE_ARGS

    BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS

    Combiner, Group, GroupCompare, SlotFunction

  >::connect(const group_type& group,

             const slot_type& in_slot,

             BOOST_SIGNALS_NAMESPACE::connect_position at)

  {

    // If the slot has been disconnected, just return a disconnected

    // connection

    if (!in_slot.is_active()) {

      return BOOST_SIGNALS_NAMESPACE::connection();

    }



    return impl->connect_slot(in_slot.get_slot_function(), group,

                              in_slot.get_data(), at);

  }



  template<

    typename R,

    BOOST_SIGNALS_TEMPLATE_PARMS

    BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS

    typename Combiner,

    typename Group,

    typename GroupCompare,

    typename SlotFunction

  >

  typename BOOST_SIGNALS_SIGNAL<

             R, BOOST_SIGNALS_TEMPLATE_ARGS

             BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS

             Combiner, Group, GroupCompare, SlotFunction>::result_type

  BOOST_SIGNALS_SIGNAL<

    R, BOOST_SIGNALS_TEMPLATE_ARGS

    BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS

    Combiner, Group, GroupCompare, SlotFunction

  >::operator()(BOOST_SIGNALS_PARMS)

  {

    // Notify the slot handling code that we are making a call

    BOOST_SIGNALS_NAMESPACE::detail::call_notification notification(this->impl);



    // Construct a function object that will call the underlying slots

    // with the given arguments.

#if BOOST_SIGNALS_NUM_ARGS == 0

    BOOST_SIGNALS_ARGS_STRUCT_INST args;

#else

    BOOST_SIGNALS_ARGS_STRUCT_INST args(BOOST_SIGNALS_ARGS);

#endif // BOOST_SIGNALS_NUM_ARGS > 0

    call_bound_slot f(&args);



    typedef typename call_bound_slot::result_type result_type;

    optional<result_type> cache;

    // Let the combiner call the slots via a pair of input iterators

    return combiner()(slot_call_iterator(notification.impl->slots_.begin(),

                                         impl->slots_.end(), f, cache),

                      slot_call_iterator(notification.impl->slots_.end(),

                                         impl->slots_.end(), f, cache));

  }



  template<

    typename R,

    BOOST_SIGNALS_TEMPLATE_PARMS

    BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS

    typename Combiner,

    typename Group,

    typename GroupCompare,

    typename SlotFunction

  >

  typename BOOST_SIGNALS_SIGNAL<

             R, BOOST_SIGNALS_TEMPLATE_ARGS

             BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS

             Combiner, Group, GroupCompare, SlotFunction>::result_type

  BOOST_SIGNALS_SIGNAL<

    R, BOOST_SIGNALS_TEMPLATE_ARGS

    BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS

    Combiner, Group, GroupCompare, SlotFunction

  >::operator()(BOOST_SIGNALS_PARMS) const

  {

    // Notify the slot handling code that we are making a call

    BOOST_SIGNALS_NAMESPACE::detail::call_notification notification(this->impl);



    // Construct a function object that will call the underlying slots

    // with the given arguments.

#if BOOST_SIGNALS_NUM_ARGS == 0

    BOOST_SIGNALS_ARGS_STRUCT_INST args;

#else

    BOOST_SIGNALS_ARGS_STRUCT_INST args(BOOST_SIGNALS_ARGS);

#endif // BOOST_SIGNALS_NUM_ARGS > 0



    call_bound_slot f(&args);



    typedef typename call_bound_slot::result_type result_type;

    optional<result_type> cache;



    // Let the combiner call the slots via a pair of input iterators

    return combiner()(slot_call_iterator(notification.impl->slots_.begin(),

                                         impl->slots_.end(), f, cache),

                      slot_call_iterator(notification.impl->slots_.end(),

                                         impl->slots_.end(), f, cache));

  }

} // namespace boost



#undef BOOST_SIGNAL_FUNCTION_N_HEADER

#undef BOOST_SIGNALS_ARGS_STRUCT_INST

#undef BOOST_SIGNALS_CALL_BOUND

#undef BOOST_SIGNALS_ARGS_STRUCT

#undef BOOST_SIGNALS_FUNCTION

#undef BOOST_SIGNALS_SIGNAL

#undef BOOST_SIGNALS_COMMA_IF_NONZERO_ARGS



#ifdef BOOST_HAS_ABI_HEADERS

#  include BOOST_ABI_SUFFIX

#endif

