Click here to Skip to main content
15,891,704 members
Articles / Programming Languages / C++

Fast C++ Delegate

Rate me:
Please Sign up or sign in to vote.
4.80/5 (52 votes)
27 Mar 2006CPOL19 min read 212.2K   3.6K   110  
An implementation of a fast C++ delegate which is portable and C++ Standard-compliant.
#if !defined(__CONFIG_HPP__INCLUDED__)
#define __CONFIG_HPP__INCLUDED__

// ====================================================================================================
// BEGIN of CONFIG
// ====================================================================================================

#if !defined(FD_MAX_NUM_PARAMS)
#define FD_MAX_NUM_PARAMS               8
#endif

// ====================================================================================================

#if !defined(FD_DEFAULT_ALLOCATOR)
#define FD_DEFAULT_ALLOCATOR            std::allocator<void>
#endif  // #if !defined(FD_DEFAULT_ALLOCATOR)

// ====================================================================================================

// internal buffer size of delegate in count of (void *)
// buf_size = countof_pvoid * sizeof(void *)
#if !defined(FD_BUF_SIZE_IN_COUNTOF_PVOID)
#define FD_BUF_SIZE_IN_COUNTOF_PVOID    2  // // default buf_size = 2 * 4 = 8 bytes
#endif

// ====================================================================================================

//
// to enable fd::bind helper function for easy conversion from boost::bind & boost::function to fd::delegate
// define FD_ENABLE_BIND_HELPER_FUNCTION macro before including, directly or indirectly, "delegate.h"
// 
// ====================================================================================================
// #define FD_ENABLE_BIND_HELPER_FUNCTION
// ====================================================================================================
//

// ====================================================================================================

// --------------------------------------------------
//
// to use delegate with __stdcall member functions,
// define FD_MEM_FN_ENABLE_STDCALL macro before including, directly or indirectly, "delegate.h"
//
// ====================================================================================================
// #define FD_MEM_FN_ENABLE_STDCALL
// ====================================================================================================
//
// --------------------------------------------------
//
// to use delegate with __fastcall member functions,
// define FD_MEM_FN_ENABLE_FASTCALL macro before including, directly or indirectly, "delegate.h"
//
// ====================================================================================================
// #define FD_MEM_FN_ENABLE_FASTCALL
// ====================================================================================================
//
// --------------------------------------------------
//
// to use delegate with __cdecl member functions,
// define FD_MEM_FN_ENABLE_CDECL macro before including, directly or indirectly, "delegate.h"
//
// ====================================================================================================
// #define FD_MEM_FN_ENABLE_CDECL
// ====================================================================================================
//
// --------------------------------------------------
//
// to use delegate with __stdcall functions,
// define FD_FN_ENABLE_STDCALL macro before including, directly or indirectly, "delegate.h"
//
// ====================================================================================================
// #define FD_FN_ENABLE_STDCALL
// ====================================================================================================
//
// --------------------------------------------------
//
// to use delegate with __stdcall functions,
// define FD_FN_ENABLE_FASTCALL macro before including, directly or indirectly, "delegate.h"
//
// ====================================================================================================
// #define FD_FN_ENABLE_FASTCALL
// ====================================================================================================
//
// --------------------------------------------------
//
// to use delegate with pascal functions,
// define FD_FN_ENABLE_PASCAL macro before including, directly or indirectly, "delegate.h"
//
// ====================================================================================================
// #define FD_FN_ENABLE_PASCAL
// ====================================================================================================
//
// --------------------------------------------------

// It is best to define these macros in the project options, via -D on the command line, or as the first
// line in the translation unit (.cpp file) where delegate is used. Not following this rule can lead to
// obscure errors when a header includes delegate.h before the macro has been defined.

// ====================================================================================================

//
// Less restrictive types (type-check relaxation)
//
// Callbacks in C++
// ( http://bpeers.com/articles/callback/ )
//
//
// to use this feature, DO NOT define FD_DISABLE_TYPE_RELAXATION macro so that a function is allowed to be assigned/binded
// to delegate as long as it does meet the follwing three conditions
//
//    a) the number of argument matches,
//    b) each matching argument can be trivially converted (from delegate's argument to target function's argument) by compiler,
//    c) return type can be trivaially converted (from delegate's return type to target function's return type "and vice versa") by compiler.
//
// ex)
//
// int foo(int n1, int n2) { };
// long bar(long n, float f) { };
//
// // #define FD_DISABLE_TYPE_RELAXATION
// #include "delegate.h"
//
// typedef fd::delegate<int (int)> MyDelegate;

// MyDelegate dg1 = &foo; // ok!
// MyDelegate dg2 = &bar; // ok! since the number of arguments (2 arguments) match
//                        // and long and floag can be trivially converted into int by compiler
//                        // but float > int conversion might cause a warning of 'possible loss of data'
//                        //
//                        // if FD_DISABLE_TYPE_RELAXATION were to be defined above, it causes errors here
//
// int result1 = dg1(123, 456); // ok!
// int result2 = dg2(345, 678); // ok!
//
// ====================================================================================================
// #define FD_DISABLE_TYPE_RELAXATION
// ====================================================================================================

//
// Cloning bound object
//

// Cloning bound object feature are added in v1.10. and there are several changes in public member function
// interfaces (member fucntion which related with argument binding,
// ex) delegate::bind(mfn, obj), delegate::operator <<= (ftor). fd::make_delegate(mfn, obj), fd::bind(mfn, obj, ...) )
//
// Unless otherwise FD_DISABLE_CLONE_BOUND_OBJECT is defined, the cloning bound object is enabled (default)
//

// ==============================
// argument binding
// ==============================

// CBase1 obj;
// smart_ptr<CBase1> sp(new CBase1);
//
// typedef int (CBase1::*TMFn)(int);
// TMFn mfn = &CBase1::foo;
//
// dg11.bind(mfn, &obj);
// dg12.bind(mfn, obj);
// dg13.bind(mfn, sp);
//
// delegate<> dg14(mfn, &obj);
// delegate<> dg15(mfn, obj);
// delegate<> dg16(mfn, sp);
//
// dg11(123); // (&obj)->foo(123);
// dg12(123); // (internal copy of obj).foo(123);
// dg13(123); // (internal copy of sp)->foo(123);
//
// dg14(123); // (&obj)->foo(123);
// dg15(123); // (internal copy of obj).foo(123);
// dg16(123); // (internal copy of sp)->foo(123);
//

// ==============================
// functor assignment
// ==============================

// struct bar { int operator()(int n) { return n; } };
//
// bar ftor;
//
// dg21 <<= &ftor;
// dg22 <<= ftor;
//
// // the second bool argument below is meaningless and provided just for the different function signature
// delegate<> dg23(&ftor, true); 
// delegate<> dg24(ftor, true);
//
// dg21(123); // (&ftor)->operator()(123);
// dg22(123); // (internal copy of ftor).operator()(123);
//
// dg23(123); // (&ftor)->operator()(123);
// dg24(123); // (internal copy of ftor).operator()(123);
//

//
// the reason I added cloning bound object feature is mostly to support any smart pointer
// (such as std::auto_ptr, boost::shared_ptr or loki::smartPtr)
// Though there are prerequisite/condition to be accomplished for a smart pointer to be recognized and supported
// by fd::delegate
//
//  a) T * get_pointer(smart_ptr (const) & p); is provided for the smart_ptr class and this function must
//     be seen in qualified namespace (including argument-dependent lookup)
//
//  b) smart_ptr class must expose public interface (typedef) of ' element_type ' that is the type of object at which
//     the smart_ptr points internally (std::auto_ptr, boost smart pointers and loki::smartPtr expose this
//     interface)
//
// If a smart_ptr class does not provide get_pointer() in its own namespace or your compiler poorly implemented the
// argument-dependent lookup (Koenig lookup) nor implemented it at all, then you can explicitly provide get_pointer
// for yourself in namespace fd
//
// namespace fd
// {
// #if defined(_MSC_VER) && _MSC_VER < 1300
// // get_pointer() for boost::shared_ptr is defined in boost namespace but VC6 did not implement koenig look-up
// // so it can't find the definition from namespace boost. therefore we can provide it in namespace fd for ourselves,
//   template<typename T> inline
//     T * get_pointer(boost::shared_ptr const & p)
//   {
//     return p.get();
//   }
// #endif // #if defined(_MSC_VER) && _MSC_VER < 1300
// } // namespace fd
//
// smart pointer will be copied internally which will transfer ownership or increase reference count and when
// the delegate is being destructed, the cloned smart pointer will be destructed, which will delete the object
// or decrease the reference count dependent on their implementation.
//
// Be aware that cloning the bound object involve heap memory allocation/deallocation using new/delete operator.
// The size of the object cloned (either it is bound object or smart pointer) is not known, therefore heap
// memory allocation/deallocation is inevitable. Since the idea of using fast delegate is avoiding
// heap memory allocation/deallocation but cloning object on the heap memory conflict with it. (though member
// function pointer will be still stored in the stack rather than heap). If speed is the only concern, do not use
// cloning bound object feature by defining FD_DISABLE_CLONE_BOUND_OBJECT macro then use reference version of
// bind() and relevant functions only. When FD_DISABLE_CLONE_BOUND_OBJECT is defined, extra 4 bytes space per delegate
// is saved as an additional bonus. (since obj_clone_man_ptr_ is not necessary any more)

// ====================================================================================================
// #define FD_DISABLE_CLONE_BOUND_OBJECT
// ====================================================================================================

// ====================================================================================================
// END of CONFIG
// ====================================================================================================

#endif  // #if !defined(__CONFIG_HPP__INCLUDED__)

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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


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