Click here to Skip to main content
15,896,063 members
Articles / Programming Languages / C++

Fast C++ Delegate: Boost.Function 'drop-in' replacement and multicast

Rate me:
Please Sign up or sign in to vote.
4.86/5 (51 votes)
1 Jun 200733 min read 292.9K   1.9K   110  
An article on the implementation of a fast C++ delegate with many advanced features.
// FD.Delegate library

// Copyright Douglas Gregor 2001-2003. 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

// Copyright JaeWook Choi 2007
// , modified from Boost.Signals signal_n_test.cpp

#include <boost/test/minimal.hpp>
#include <boost/signal.hpp>
#include <fd/delegate.hpp>
#include <functional>

template<typename T>
struct max_or_default {
  typedef T result_type;
  template<typename InputIterator>
  typename InputIterator::value_type
  operator()(InputIterator first, InputIterator last) const
  {
    if (first == last)
      return T();

    T max = *first++;
    for (; first != last; ++first)
      max = (*first > max)? *first : max;

    return max;
  }
};

struct make_int {
  make_int(int n, int cn) : N(n), CN(n) {}
  int operator()() { return N; }
  int operator()() const { return CN; }

  int N;
  int CN;
};

bool operator ==(make_int const & lhs, make_int const & rhs)
{
  return lhs.N == rhs.N;
}

FD_DELEGATE_EQUALITY_COMPARABLE_TYPE(make_int);

template<int N>
struct make_increasing_int {
  make_increasing_int() : n(N) {}

  int operator()() const { return n++; }

  mutable int n;
};

int get_37() { return 37; }

static void
test_zero_args()
{
  make_int i42(42, 41);
  make_int i2(2, 1);
  make_int i72(72, 71);
  make_int i63(63, 63);
  make_int i62(62, 61);

  {
    boost::signal0<int, max_or_default<int>, std::string> s0;
    boost::BOOST_SIGNALS_NAMESPACE::connection c2 = s0.connect(i2);
    boost::BOOST_SIGNALS_NAMESPACE::connection c72 = s0.connect("72", i72);
    boost::BOOST_SIGNALS_NAMESPACE::connection c62 = s0.connect("6x", i62);
    boost::BOOST_SIGNALS_NAMESPACE::connection c42 = s0.connect(i42);
    boost::BOOST_SIGNALS_NAMESPACE::connection c37 = s0.connect(&get_37);

    BOOST_CHECK(s0() == 72);

    s0.disconnect("72");
    BOOST_CHECK(s0() == 62);

    c72.disconnect(); // Double-disconnect should be safe
    BOOST_CHECK(s0() == 62);

    s0.disconnect("72"); // Triple-disconect should be safe
    BOOST_CHECK(s0() == 62);

    // Also connect 63 in the same group as 62
    s0.connect("6x", i63);
    BOOST_CHECK(s0() == 63);

    // Disconnect all of the 60's
    s0.disconnect("6x");
    BOOST_CHECK(s0() == 42);

    c42.disconnect();
    BOOST_CHECK(s0() == 37);

    c37.disconnect();
    BOOST_CHECK(s0() == 2);

    c2.disconnect();
    BOOST_CHECK(s0() == 0);


    fd::delegate0<int> d0;
    fd::multicast::token t2 = d0.add(i2);
    fd::multicast::token t72 = d0.add(i72);
    fd::multicast::token t62 = d0.add(i62);
    fd::multicast::token t42 = d0.add(i42);
    fd::multicast::token t37 = d0.add(&get_37);

    BOOST_CHECK(d0(max_or_default<int>()) == 72);

    d0 -= i72;
    BOOST_CHECK(d0(max_or_default<int>()) == 62);

    t72.remove(); // Double-remove should be safe
    BOOST_CHECK(d0(max_or_default<int>()) == 62);

    d0 -= i72; // Triple-remove should be safe
    BOOST_CHECK(d0(max_or_default<int>()) == 62);

    d0 += i63;
    BOOST_CHECK(d0(max_or_default<int>()) == 63);

    // Disconnect all of the 60's
    d0 -= i63;
    d0 -= i62;
    BOOST_CHECK(d0(max_or_default<int>()) == 42);

    t42.remove();
    BOOST_CHECK(d0(max_or_default<int>()) == 37);

    t37.remove();
    BOOST_CHECK(d0(max_or_default<int>()) == 2);

    t2.remove();
    BOOST_CHECK(d0(max_or_default<int>()) == 0);

    BOOST_CHECK(d0.empty());

    try
    {
      d0();
    }
    catch(fd::bad_function_call) { }
  }

  {
    boost::signal0<int, max_or_default<int> > s0;
    boost::BOOST_SIGNALS_NAMESPACE::connection c2 = s0.connect(i2);
    boost::BOOST_SIGNALS_NAMESPACE::connection c72 = s0.connect(i72);
    boost::BOOST_SIGNALS_NAMESPACE::connection c62 = s0.connect(i62);
    boost::BOOST_SIGNALS_NAMESPACE::connection c42 = s0.connect(i42);

    const boost::signal0<int, max_or_default<int> >& cs0 = s0;
    BOOST_CHECK(cs0() == 72);

    fd::delegate0<int> d0;
    fd::multicast::token t2 = d0.add(i2);
    fd::multicast::token t72 = d0.add(i72);
    fd::multicast::token t62 = d0.add(i62);
    fd::multicast::token t42 = d0.add(i42);

    const fd::delegate0<int>& cd0 = d0;
    BOOST_CHECK(cd0(max_or_default<int>()) == 72);
  }

  {
    make_increasing_int<7> i7;
    make_increasing_int<10> i10;

    boost::signal0<int, max_or_default<int> > s0;
    boost::BOOST_SIGNALS_NAMESPACE::connection c7 = s0.connect(i7);
    boost::BOOST_SIGNALS_NAMESPACE::connection c10 = s0.connect(i10);

    BOOST_CHECK(s0() == 10);
    BOOST_CHECK(s0() == 11);

    fd::delegate0<int> d0;
    fd::multicast::token t7 = d0.add(i7);
    fd::multicast::token t10 = d0.add(i10);

    BOOST_CHECK(d0(max_or_default<int>()) == 10);
    BOOST_CHECK(d0(max_or_default<int>()) == 11);
  }
}

static void
test_one_arg()
{
  boost::signal1<int, int, max_or_default<int> > s1;

  s1.connect(std::negate<int>());
  s1.connect(std::bind1st(std::multiplies<int>(), 2));

  BOOST_CHECK(s1(1) == 2);
  BOOST_CHECK(s1(-1) == 1);

  fd::delegate1<int, int> d1;

  d1.add(std::negate<int>());
  d1.add(std::bind1st(std::multiplies<int>(), 2));

  BOOST_CHECK(d1(1, max_or_default<int>()) == 2);
  BOOST_CHECK(d1(-1, max_or_default<int>()) == 1);
}

static void
test_signal_signal_connect()
{
  boost::signal1<int, int, max_or_default<int> > s1;

  s1.connect(std::negate<int>());

  BOOST_CHECK(s1(3) == -3);

  {
    boost::signal1<int, int, max_or_default<int> > s2;
    s1.connect(s2);
    s2.connect(std::bind1st(std::multiplies<int>(), 2));
    s2.connect(std::bind1st(std::multiplies<int>(), -3));

    BOOST_CHECK(s2(-3) == 9);
    BOOST_CHECK(s1(3) == 6);
  } // s2 goes out of scope and disconnects

  BOOST_CHECK(s1(3) == -3);
}

struct EventCounter
{
  EventCounter() : count(0) {}

  void operator()()
  {
    ++count;
  }

  int count;
};

static void
test_ref()
{
  EventCounter ec;
  boost::signal0<void> s;

  {
    boost::BOOST_SIGNALS_NAMESPACE::scoped_connection c = s.connect(boost::ref(ec));
    BOOST_CHECK(ec.count == 0);
    s();
    BOOST_CHECK(ec.count == 1);
  }
  s();
  BOOST_CHECK(ec.count == 1);

  // --------------------------------------------------------------------------------

  EventCounter ec2;
  fd::delegate0<void> d;

  d += boost::ref( ec2 );
  BOOST_CHECK(ec2.count == 0);
  d();
  BOOST_CHECK(ec2.count == 1);
  d -= boost::ref( ec2 );

  BOOST_CHECK(d.empty());
  try
  {
    d();
  }
  catch(fd::bad_function_call) { }
  BOOST_CHECK(ec2.count == 1);
}

int
test_main(int, char* [])
{
  test_zero_args();
  test_one_arg();
  test_signal_signal_connect();
  test_ref();
  return 0;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Other
Canada Canada
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions