Click here to Skip to main content
15,896,154 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 (c) 2007 JaeWook Choi
//
// Distributed under 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)

#include <boost/test/minimal.hpp>
#include <cassert>
#include <iostream>
#include <boost/thread.hpp>
#include <boost/shared_ptr.hpp>
#include <fd/delegate.hpp>

static std::string test_output;

/**
 * Transfer ownership of the lock when the object is being copied.
 */
template<class Lock>
class transfered_lock
{
  // lock
  Lock & lock_;

  // disabled?
  mutable bool disabled_;

  // no default constructor
  transfered_lock();

protected:
  // Constructor
  explicit transfered_lock(Lock & lk) throw()
    : lock_( lk ), disabled_( false )
  {
    boost::detail::thread::lock_ops<Lock>::lock( lock_ ); // lock.
    test_output += "Locked ";
  }

  // Copy constructor
  transfered_lock(transfered_lock const & other) throw()
    : lock_( other.lock_ ), disabled_( false )
  {
    // Transfer the ownership of the lock from other to this
    other.disabled_ = true; // disable other's lock
  }

  // Destructor.
  ~transfered_lock() throw()
  {
    if( !disabled_ )
    { // Unlock. if and only if it is not disabled

      boost::detail::thread::lock_ops<Lock>::unlock( lock_ );
      test_output += "Unlocked ";
    }
  }

private:
  // No assignment operator
  transfered_lock & operator =(transfered_lock const & other);

};  // class transfered_lock

/**
 *
 */
template<class T, class Lock>
class proxy
  : private transfered_lock<Lock>
{
private:
  T * pt_;  //!< Pointed object.

public:
  proxy(Lock & lock, T * pt)
    : transfered_lock<Lock>( lock ), pt_( pt )
  {
  }

  T * operator ->() const
  {
    assert( 0 != pt_ );
    return pt_;
  }

  T & operator *() const
  {
    assert( 0 != pt_ );
    return *pt_;
  }

};  // class proxy

template<typename T, typename Lock = boost::mutex>
class guarded_ptr
{
private:
  mutable Lock * lock_ptr_;
  boost::shared_ptr<T> sptr_;

  struct delete_lock
  {
    Lock * lock_ptr_;
    delete_lock(Lock * lock_ptr) : lock_ptr_( lock_ptr ) { }
    template<typename U>
    void operator ()(U u)
    {
      boost::checked_delete( u );
      delete lock_ptr_;
    }
  };

public:
  typedef T               value_type;
  typedef Lock            lock_type;
  typedef proxy<T, Lock>  proxy_type;

  guarded_ptr() : sptr_(), lock_ptr_( 0 ) { }

  template<typename U>
  explicit guarded_ptr(U * pt)
    : lock_ptr_( new Lock() ), sptr_( pt, delete_lock( lock_ptr_ ) )
  {
  }

  template<typename U>
  explicit guarded_ptr(guarded_ptr<U, Lock> const & other)
    : lock_ptr_( other.lock_ptr_ ), sptr_( other.sptr_ )
  {
  }

  void swap(guarded_ptr & other)
  {
    std::swap( lock_ptr_, other.lock_ptr_ );
    sptr_.swap( other.sptr_ );
  }

  template<typename U>
  guarded_ptr & operator =(U * pt)
  {
    guarded_ptr<T, Lock>( pt ).swap( *this );
    return *this;
  }

  template<typename U>
  guarded_ptr & operator =(guarded_ptr<U, Lock> const & other)
  {
    if(this != &other)
    {
      guarded_ptr( other ).swap( *this );
    }
    return *this;
  }

  proxy<T, Lock> operator ->() const
  {
    return proxy<T, Lock>( *lock_ptr_, sptr_.get() );
  }

  void reset()
  {
    guarded_ptr().swap( *this );
  }

  template<typename U>
  void reset(U * pt)
  {
    guarded_ptr( pt ).swap( *this );
  }

  template<typename U>
  void reset(guarded_ptr<U, Lock> const & other)
  {
    if( this != &other )
    {
      guarded_ptr( other ).swap( *this );
    }
  }
};  // class guarded_ptr

/**
 * Library designer can provide get_pointer() to make their smart pointer class
 * work with FD.Delegate. However, get_pointer() is not necessarily required
 * to return a raw pointer and instead it can return a proxy object as long as
 * the returned proxy object supports following two operations of a raw pointer.
 *
 * 1) member access;  operator ->
 * 2) dereference;    operator *
 */
template<typename T>
typename guarded_ptr<T>::proxy_type get_pointer(guarded_ptr<T> const & gptr)
{
  return gptr.operator ->();
}

struct foo
{
  void bar()
  {
    test_output += "foo::bar() ";
  }

  void foobar()
  {
    test_output += "foo::foobar() ";
  }
};

int
test_main(int, char*[])
{
  guarded_ptr<foo> foo_gptr( new foo() );

  std::cout << "guarded_ptr #1" << std::endl;
  test_output = "";
  foo_gptr->bar(); std::cout << std::endl;
  BOOST_CHECK(test_output == "Locked foo::bar() Unlocked ");

  std::cout << "guarded_ptr #2" << std::endl;
  test_output = "";
  foo_gptr->foobar(); std::cout << std::endl;
  BOOST_CHECK(test_output == "Locked foo::foobar() Unlocked ");

  std::cout << "guarded_ptr #3" << std::endl;
  test_output = "";
  ( &( *( foo_gptr.operator ->() ) )->*&foo::bar )(); std::cout<< std::endl;
  BOOST_CHECK(test_output == "Locked foo::bar() Unlocked ");

  fd::delegate<void ()> dg_foo1, dg_foo2;
  dg_foo1.bind( &foo::bar, foo_gptr );

  dg_foo2 = dg_foo1;

  std::cout << "guarded_ptr in FD.Delegate #1" << std::endl;
  test_output = "";
  dg_foo1(); std::cout<< std::endl;
  BOOST_CHECK(test_output == "Locked foo::bar() Unlocked ");

  std::cout << "guarded_ptr in FD.Delegate #2" << std::endl;
  test_output = "";
  dg_foo2(); std::cout<< std::endl;
  BOOST_CHECK(test_output == "Locked foo::bar() Unlocked ");

  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