Click here to Skip to main content
15,884,298 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 291.5K   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_test.cpp

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

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(cn) {}

  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;
};

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::signal<int (), max_or_default<int> > s0;
    fd::delegate<int ()> d0;

    std::cout << "sizeof(signal) = " << sizeof(s0) << std::endl;
    std::cout << "sizeof(delegate) = " << sizeof(d0) << std::endl;
    boost::BOOST_SIGNALS_NAMESPACE::connection c2 = s0.connect(i2);
    d0 += i2;
    boost::BOOST_SIGNALS_NAMESPACE::connection c72 = s0.connect(72, i72);
    d0 += i72;
    boost::BOOST_SIGNALS_NAMESPACE::connection c62 = s0.connect(60, i62);
    d0 += i62;
    boost::BOOST_SIGNALS_NAMESPACE::connection c42 = s0.connect(i42);
    d0 += i42;

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

    s0.disconnect(72);
    d0 -= i72;
    BOOST_CHECK(s0() == 62);
    BOOST_CHECK(d0(max_or_default<int>()) == 62);

    c72.disconnect(); // Double-disconnect should be safe
    d0 -= i72;
    BOOST_CHECK(s0() == 62);
    BOOST_CHECK(d0(max_or_default<int>()) == 62);

    s0.disconnect(72); // Triple-disconect should be safe
    d0 -= i72;
    BOOST_CHECK(s0() == 62);
    BOOST_CHECK(d0(max_or_default<int>()) == 62);

    // Also connect 63 in the same group as 62
    s0.connect(60, i63);
    d0 += i63;
    BOOST_CHECK(s0() == 63);
    BOOST_CHECK(d0(max_or_default<int>()) == 63);

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

    c42.disconnect();
    d0 -= i42;
    BOOST_CHECK(s0() == 2);
    BOOST_CHECK(d0(max_or_default<int>()) == 2);

    c2.disconnect();
    d0 -= i2;
    BOOST_CHECK(s0() == 0);
    BOOST_CHECK(d0(max_or_default<int>()) == 0);
    BOOST_CHECK(d0.empty());
    try
    {
      d0();
    } catch(fd::bad_function_call) { }
  }

  {
    boost::signal<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::signal<int (), max_or_default<int> >& cs0 = s0;
    BOOST_CHECK(cs0() == 72);

    fd::delegate<int ()> d0;
    d0 += i2;
    d0 += i72;
    d0 += i62;
    d0 += i42;

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

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

    boost::signal<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::signal<int (), max_or_default<int> > s1( s0 );  // Not copy-constructible,
    // s1 = s0;                                               // nor assignable.

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

    fd::delegate<int ()> d0, d1, d2;
    d0 += i7;
    d0 += i10;

    d1 = d0;

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

    d2 = d0;

    BOOST_CHECK(d1(max_or_default<int>()) == 10);
    BOOST_CHECK(d1(max_or_default<int>()) == 11);

    BOOST_CHECK(d2(max_or_default<int>()) == 12);
    BOOST_CHECK(d2(max_or_default<int>()) == 13);
  }
}

static void
test_one_arg()
{
  boost::signal<int (int value), 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::delegate<int (int value)> d1;

  d1 += std::negate<int>();
  d1 += 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::signal<int (int value), max_or_default<int> > s1;

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

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

  fd::delegate<int (int value)> d1;

  d1 += std::negate<int>();

  BOOST_CHECK(d1(3, max_or_default<int>()) == -3);

  {
    boost::signal<int (int value), 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);

    {
      fd::delegate<int (int value)> d2;

      // Reference to other FD.Delegate is added so any changes made on d2 afterward
      // will affect d1 as well.
      fd::multicast::scoped_token stok1 = d1.add(boost::ref( d2 ));

      d2 += std::bind1st(std::multiplies<int>(), 2);
      d2 += std::bind1st(std::multiplies<int>(), -3);

      BOOST_CHECK(d2(-3, max_or_default<int>()) == 9);

      BOOST_CHECK(d1(3, max_or_default<int>()) != 6);
      BOOST_CHECK(d1(3, max_or_default<int>()) == -3);
      // Multicast can not pass down the combiner interface into the referenced
      // multicast of compatible FD.Delegate type since a callable entity is internally
      // stored without type information nor the combiner interface is not a part of
      // FD.Delegate type.
      //
      // Therefore when d1(3, max_or_default<int>()) is called,
      //   a) std::negate<int>(3) and
      //   b) d2(3),
      // are called in an order with max_or_default<int>() combiner.
      //
      // Since d2 itself is a multicast, it will call
      //   b-1) std::multiplies<int>()(2, 3) and
      //   b-2) std::multiplies<int>()(-3, 3)
      // without combiner interface thus will return -9 which is the last return
      // value of delegates in the multicast.
      //
      // Combiner interface is only applied for between -3 from std::negate<int>(3) and
      // -9 from d2(3) and -3 will be returned as result.

    } // stok1 goes out of scope and disconnects

    {
      fd::delegate<int (int value)> d3;

      // Non-reference to other FD.Delegate is added so all delegates stored
      // in d3 at the moment will be copied to d1 but any changes made on d3 afterward
      // will not affect d1 at all.
      fd::multicast::scoped_token stok2 = d1.add( d3 );

      // d3 is an empty delegate so no delegate has been copied into d1.

      d3 += std::bind1st(std::multiplies<int>(), 2);
      d3 += std::bind1st(std::multiplies<int>(), -3);

      BOOST_CHECK(d3(-3, max_or_default<int>()) == 9);

      BOOST_CHECK(d1(3, max_or_default<int>()) != 6);
      BOOST_CHECK(d1(3, max_or_default<int>()) == -3);
      // When d1(3, max_or_default<int>()) is called, only
      // std::negate<int>(3) will be called as it is a only
      // delegate in d1.

    } // stok2 goes out of scope and disconnects

  } // s2 goes out of scope and disconnects

  BOOST_CHECK(s1(3) == -3);
  BOOST_CHECK(d1(3, max_or_default<int>()) == -3);
}

int
test_main(int, char* [])
{
  test_zero_args();
  test_one_arg();
  test_signal_signal_connect();
  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