Click here to Skip to main content
15,891,431 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.2K   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 (C) 2007 JaeWook Choi
// , modified from Boost.Function function_test.cpp

#include <boost/test/minimal.hpp>
#include <fd/delegate.hpp>
#include <functional>
#include <cassert>
#include <string>
#include <utility>

using namespace boost;
using namespace std;
using namespace fd;

int global_int;

struct write_five_obj { void operator()() const { global_int = 5; } };
struct write_three_obj { int operator()() const { global_int = 3; return 7; }};
static void write_five() { global_int = 5; }
static void write_three() { global_int = 3; }
struct generate_five_obj { int operator()() const { return 5; } };
struct generate_three_obj { int operator()() const { return 3; } };
static int generate_five() { return 5; }
static int generate_three() { return 3; }
static string identity_str(const string& s) { return s; }
static string string_cat(const string& s1, const string& s2) { return s1+s2; }
static int sum_ints(int x, int y) { return x+y; }

struct write_const_1_nonconst_2
{
  void operator()() { global_int = 2; }
  void operator()() const { global_int = 1; }
};

struct add_to_obj
{
  add_to_obj(int v) : value(v) {}

  int operator()(int x) const { return value + x; }

  int value;
};

static void
test_zero_args()
{
  typedef delegate0<void> func_void_type;

  write_five_obj five;
  write_three_obj three;

  // Default construction
  func_void_type v1;
  BOOST_CHECK(v1.empty());

  // Assignment to an empty delegate
  v1 = five;
  BOOST_CHECK(v1 != 0);

  // Invocation of a delegate
  global_int = 0;
  v1();
  BOOST_CHECK(global_int == 5);

  // clear() method
  v1.clear();
  BOOST_CHECK(v1 == 0);

  // Assignment to an empty delegate
  v1 = three;
  BOOST_CHECK(!v1.empty());

  // Invocation and self-assignment
  global_int = 0;
  v1 = v1;
  v1();
  BOOST_CHECK(global_int == 3);

  // Assignment to a non-empty delegate
  v1 = five;

  // Invocation and self-assignment
  global_int = 0;
  v1 = (v1);
  v1();
  BOOST_CHECK(global_int == 5);

  // clear
  v1 = 0;
  BOOST_CHECK(0 == v1);

  // Assignment to an empty delegate from a free delegate
  v1 = FD_DELEGATE_TARGET_FIX(&) write_five;
  BOOST_CHECK(0 != v1);

  // Invocation
  global_int = 0;
  v1();
  BOOST_CHECK(global_int == 5);

  // Assignment to a non-empty delegate from a free delegate
  v1 = FD_DELEGATE_TARGET_FIX(&) write_three;
  BOOST_CHECK(!v1.empty());

  // Invocation
  global_int = 0;
  v1();
  BOOST_CHECK(global_int == 3);

  // Assignment
  v1 = five;
  BOOST_CHECK(!v1.empty());

  // Invocation
  global_int = 0;
  v1();
  BOOST_CHECK(global_int == 5);

  // Assignment to a non-empty delegate from a free delegate
  v1 = &write_three;
  BOOST_CHECK(!v1.empty());

  // Invocation
  global_int = 0;
  v1();
  BOOST_CHECK(global_int == 3);

  // Construction from another delegate (that is empty)
  v1.clear();
  func_void_type v2(v1);
  BOOST_CHECK(!v2? true : false);

  // Assignment to an empty delegate
  v2 = three;
  BOOST_CHECK(!v2.empty());

  // Invocation
  global_int = 0;
  v2();
  BOOST_CHECK(global_int == 3);

  // Assignment to a non-empty delegate
  v2 = (five);

  // Invocation
  global_int = 0;
  v2();
  BOOST_CHECK(global_int == 5);

  v2.clear();
  BOOST_CHECK(v2.empty());

  // Assignment to an empty delegate from a free delegate
  v2 = (FD_DELEGATE_TARGET_FIX(&) write_five);
  BOOST_CHECK(v2? true : false);

  // Invocation
  global_int = 0;
  v2();
  BOOST_CHECK(global_int == 5);

  // Assignment to a non-empty delegate from a free delegate
  v2 = FD_DELEGATE_TARGET_FIX(&) write_three;
  BOOST_CHECK(!v2.empty());

  // Invocation
  global_int = 0;
  v2();
  BOOST_CHECK(global_int == 3);

  // Swapping
  v1 = five;
#ifndef BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP
  swap(v1, v2);
#else
  fd::swap(v1, v2);
#endif
  v2();
  BOOST_CHECK(global_int == 5);
  v1();
  BOOST_CHECK(global_int == 3);
#ifndef BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP
  swap(v1, v2);
#else
  fd::swap(v1, v2);
#endif
  v1.clear();

  // Assignment
  v2 = five;
  BOOST_CHECK(!v2.empty());

  // Invocation
  global_int = 0;
  v2();
  BOOST_CHECK(global_int == 5);

  // Assignment to a non-empty delegate from a free delegate
  v2 = &write_three;
  BOOST_CHECK(!v2.empty());

  // Invocation
  global_int = 0;
  v2();
  BOOST_CHECK(global_int == 3);

  // Assignment to a delegate from an empty delegate
  v2 = v1;
  BOOST_CHECK(v2.empty());

  // Assignment to a delegate from a delegate with a functor
  v1 = three;
  v2 = v1;
  BOOST_CHECK(!v1.empty());
  BOOST_CHECK(!v2.empty());

  // Invocation
  global_int = 0;
  v1();
  BOOST_CHECK(global_int == 3);
  global_int = 0;
  v2();
  BOOST_CHECK(global_int == 3);

  // Assign to a delegate from a delegate with a delegate
  v2 = FD_DELEGATE_TARGET_FIX(&) write_five;
  v1 = v2;
  BOOST_CHECK(!v1.empty());
  BOOST_CHECK(!v2.empty());
  global_int = 0;
  v1();
  BOOST_CHECK(global_int == 5);
  global_int = 0;
  v2();
  BOOST_CHECK(global_int == 5);

  // Construct a delegate given another delegate containing a delegate
  func_void_type v3(v1);

  // Invocation of a delegate
  global_int = 0;
  v3();
  BOOST_CHECK(global_int == 5);

  // clear() method
  v3.clear();
  BOOST_CHECK(!v3? true : false);

  // Assignment to an empty delegate
  v3 = three;
  BOOST_CHECK(!v3.empty());

  // Invocation
  global_int = 0;
  v3();
  BOOST_CHECK(global_int == 3);

  // Assignment to a non-empty delegate
  v3 = five;

  // Invocation
  global_int = 0;
  v3();
  BOOST_CHECK(global_int == 5);

  // clear()
  v3.clear();
  BOOST_CHECK(v3.empty());

  // Assignment to an empty delegate from a free delegate
  v3 = &write_five;
  BOOST_CHECK(!v3.empty());

  // Invocation
  global_int = 0;
  v3();
  BOOST_CHECK(global_int == 5);

  // Assignment to a non-empty delegate from a free delegate
  v3 = &write_three;
  BOOST_CHECK(!v3.empty());

  // Invocation
  global_int = 0;
  v3();
  BOOST_CHECK(global_int == 3);

  // Assignment
  v3 = five;
  BOOST_CHECK(!v3.empty());

  // Invocation
  global_int = 0;
  v3();
  BOOST_CHECK(global_int == 5);

  // Construction of a delegate from a delegate containing a functor
  func_void_type v4(v3);

  // Invocation of a delegate
  global_int = 0;
  v4();
  BOOST_CHECK(global_int == 5);

  // clear() method
  v4.clear();
  BOOST_CHECK(v4.empty());

  // Assignment to an empty delegate
  v4 = three;
  BOOST_CHECK(!v4.empty());

  // Invocation
  global_int = 0;
  v4();
  BOOST_CHECK(global_int == 3);

  // Assignment to a non-empty delegate
  v4 = five;

  // Invocation
  global_int = 0;
  v4();
  BOOST_CHECK(global_int == 5);

  // clear()
  v4.clear();
  BOOST_CHECK(v4.empty());

  // Assignment to an empty delegate from a free delegate
  v4 = &write_five;
  BOOST_CHECK(!v4.empty());

  // Invocation
  global_int = 0;
  v4();
  BOOST_CHECK(global_int == 5);

  // Assignment to a non-empty delegate from a free delegate
  v4 = &write_three;
  BOOST_CHECK(!v4.empty());

  // Invocation
  global_int = 0;
  v4();
  BOOST_CHECK(global_int == 3);

  // Assignment
  v4 = five;
  BOOST_CHECK(!v4.empty());

  // Invocation
  global_int = 0;
  v4();
  BOOST_CHECK(global_int == 5);

  // Construction of a delegate from a functor
  func_void_type v5(five);

  // Invocation of a delegate
  global_int = 0;
  v5();
  BOOST_CHECK(global_int == 5);

  // clear() method
  v5.clear();
  BOOST_CHECK(v5.empty());

  // Assignment to an empty delegate
  v5 = three;
  BOOST_CHECK(!v5.empty());

  // Invocation
  global_int = 0;
  v5();
  BOOST_CHECK(global_int == 3);

  // Assignment to a non-empty delegate
  v5 = five;

  // Invocation
  global_int = 0;
  v5();
  BOOST_CHECK(global_int == 5);

  // clear()
  v5.clear();
  BOOST_CHECK(v5.empty());

  // Assignment to an empty delegate from a free delegate
  v5 = &write_five;
  BOOST_CHECK(!v5.empty());

  // Invocation
  global_int = 0;
  v5();
  BOOST_CHECK(global_int == 5);

  // Assignment to a non-empty delegate from a free delegate
  v5 = &write_three;
  BOOST_CHECK(!v5.empty());

  // Invocation
  global_int = 0;
  v5();
  BOOST_CHECK(global_int == 3);

  // Assignment
  v5 = five;
  BOOST_CHECK(!v5.empty());

  // Invocation
  global_int = 0;
  v5();
  BOOST_CHECK(global_int == 5);

  // Construction of a delegate from a delegate
  func_void_type v6(&write_five);

  // Invocation of a delegate
  global_int = 0;
  v6();
  BOOST_CHECK(global_int == 5);

  // clear() method
  v6.clear();
  BOOST_CHECK(v6.empty());

  // Assignment to an empty delegate
  v6 = three;
  BOOST_CHECK(!v6.empty());

  // Invocation
  global_int = 0;
  v6();
  BOOST_CHECK(global_int == 3);

  // Assignment to a non-empty delegate
  v6 = five;

  // Invocation
  global_int = 0;
  v6();
  BOOST_CHECK(global_int == 5);

  // clear()
  v6.clear();
  BOOST_CHECK(v6.empty());

  // Assignment to an empty delegate from a free delegate
  v6 = &write_five;
  BOOST_CHECK(!v6.empty());

  // Invocation
  global_int = 0;
  v6();
  BOOST_CHECK(global_int == 5);

  // Assignment to a non-empty delegate from a free delegate
  v6 = &write_three;
  BOOST_CHECK(!v6.empty());

  // Invocation
  global_int = 0;
  v6();
  BOOST_CHECK(global_int == 3);

  // Assignment
  v6 = five;
  BOOST_CHECK(!v6.empty());

  // Invocation
  global_int = 0;
  v6();
  BOOST_CHECK(global_int == 5);

  // Const vs. non-const
  write_const_1_nonconst_2 one_or_two;
  const delegate0<void> v7(one_or_two);
  delegate0<void> v8(one_or_two);

  global_int = 0;
  v7();
  BOOST_CHECK(global_int == 2);

  global_int = 0;
  v8();
  BOOST_CHECK(global_int == 2);

  // Test construction from 0 and comparison to 0
  func_void_type v9(0);
  BOOST_CHECK(v9 == 0);
  BOOST_CHECK(0 == v9);

  // Test return values
  typedef delegate0<int> func_int_type;
  generate_five_obj gen_five;
  generate_three_obj gen_three;

  func_int_type i0(gen_five);

  BOOST_CHECK(i0() == 5);
  i0 = gen_three;
  BOOST_CHECK(i0() == 3);
  i0 = &generate_five;
  BOOST_CHECK(i0() == 5);
  i0 = &generate_three;
  BOOST_CHECK(i0() == 3);
  BOOST_CHECK(i0? true : false);
  i0.clear();
  BOOST_CHECK(!i0? true : false);

  // Test return values with compatible types
  typedef delegate0<long> func_long_type;
  func_long_type i1(gen_five);

  BOOST_CHECK(i1() == 5);
  i1 = gen_three;
  BOOST_CHECK(i1() == 3);
  i1 = &generate_five;
  BOOST_CHECK(i1() == 5);
  i1 = &generate_three;
  BOOST_CHECK(i1() == 3);
  BOOST_CHECK(i1? true : false);
  i1.clear();
  BOOST_CHECK(!i1? true : false);
}

static void
test_one_arg()
{
  negate<int> neg;

  delegate1<int, int> f1(neg);
  BOOST_CHECK(f1(5) == -5);

  delegate1<string, string> id(&identity_str);
  BOOST_CHECK(id("str") == "str");

  delegate1<string, const char*> id2(&identity_str);
  BOOST_CHECK(id2("foo") == "foo");

  add_to_obj add_to(5);
  delegate1<int, int> f2(add_to);
  BOOST_CHECK(f2(3) == 8);

  const delegate1<int, int> cf2(add_to);
  BOOST_CHECK(cf2(3) == 8);
}

static void
test_two_args()
{
  delegate2<string, const string&, const string&> cat(&string_cat);
  BOOST_CHECK(cat("str", "ing") == "string");

  delegate2<int, short, short> sum(&sum_ints);
  BOOST_CHECK(sum(2, 3) == 5);
}

static void
test_emptiness()
{
  delegate0<float> f1;
  BOOST_CHECK(f1.empty());

  delegate0<float> f2;
  f2 = f1;
  BOOST_CHECK(f2.empty());

  delegate0<double> f3;
  f3 = f2;
  BOOST_CHECK(f3.empty());
}

struct X {
  X(int v) : value(v) {}

  int twice() const { return 2*value; }
  int plus(int v) { return value + v; }

  int value;
};

static void
test_member_functions()
{
  fd::delegate1<int, X*> f1(&X::twice);

  X one(1);
  X five(5);

  BOOST_CHECK(f1(&one) == 2);
  BOOST_CHECK(f1(&five) == 10);

  fd::delegate1<int, X*> f1_2;
  f1_2 = &X::twice;

  BOOST_CHECK(f1_2(&one) == 2);
  BOOST_CHECK(f1_2(&five) == 10);

  fd::delegate2<int, X&, int> f2(&X::plus);
  BOOST_CHECK(f2(one, 3) == 4);
  BOOST_CHECK(f2(five, 4) == 9);
}

struct add_with_throw_on_copy {
  int operator()(int x, int y) const { return x+y; }

  add_with_throw_on_copy() {}

  add_with_throw_on_copy(const add_with_throw_on_copy&)
  {
    throw runtime_error("But this CAN'T throw");
  }

  add_with_throw_on_copy& operator=(const add_with_throw_on_copy&)
  {
    throw runtime_error("But this CAN'T throw");
  }
};

static void
test_ref()
{
  add_with_throw_on_copy atc;
  try {
    fd::delegate2<int, int, int> f(ref(atc));
    BOOST_CHECK(f(1, 3) == 4);
  }
  catch(runtime_error e) {
    BOOST_ERROR("Nonthrowing constructor threw an exception");
  }
}

static void test_exception()
{
  fd::delegate2<int, int, int> f;
  try {
    f(5, 4);
    BOOST_CHECK(false);
  }
  catch(fd::bad_function_call) {
    // okay
  }
}

typedef fd::delegate1<void *, void *> reader_type;
typedef std::pair<int, reader_type> mapped_type;

static void test_implicit()
{
  mapped_type m;
  m = mapped_type();
}

static void test_call_obj(fd::delegate2<int, int, int> f)
{
  assert(!f.empty());
}

static void test_call_cref(const fd::delegate2<int, int, int>& f)
{
  assert(!f.empty());
}

static void test_call()
{
  test_call_obj(std::plus<int>());
  test_call_cref(std::plus<int>());
}

int test_main(int, char* [])
{
  test_zero_args();
  test_one_arg();
  test_two_args();
  test_emptiness();
  test_member_functions();
  test_ref();
  test_exception();
  test_implicit();
  test_call();

  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