Skip to content

Commit

Permalink
Moved a virtual function definition
Browse files Browse the repository at this point in the history
Implemented the CachedDelegate Type

Updated the example code to utilize a CachedDelegate

Updated Version Information
  • Loading branch information
Ragora committed Sep 25, 2014
1 parent 5a71868 commit 9400c93
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 24 deletions.
2 changes: 1 addition & 1 deletion easydelegates.doxyfile
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ PROJECT_NAME = "Easy Delegates"
# could be handy for archiving the generated documentation or if some version
# control system is used.

PROJECT_NUMBER =1.0
PROJECT_NUMBER =1.1

# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
Expand Down
9 changes: 8 additions & 1 deletion ex_delegateset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,15 @@ int main(int argc, char *argv[])
std::cout << "---------- Removed Delegate is still usable ------------" << std::endl;
delegateToRemove->invoke("Foo", 3.14, 3.14159);

// Create a cached delegate with the removed member delegate above
std::cout << "---------- CACHED DELEGATES ---------------" << std::endl;
typedef EasyDelegate::CachedDelegate<unsigned int, char*, float, double> MyCachedDelegateType;

MyCachedDelegateType *cachedDelegate = new MyCachedDelegateType(delegateToRemove, "Cached", 3.14, 3.14159);
cachedDelegate->dispatch(); // Call this at any point in your application to have a deferred function call

// Cleanup
delete delegateToRemove;
delete cachedDelegate; // Also deletes delegateToRemove
delete myCustomClassInstance;

return 0;
Expand Down
156 changes: 136 additions & 20 deletions include/easydelegate.hpp
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
/**
* @file easydelegate.hpp
* @version 1.0
* @date 9/23/2014
* @version 1.1
* @date 9/25/2014
* @author <a href="https://github.com/Ragora">Robert MacGregor</a>
* @brief Portable delegate system that should work on any C++11 compliant compiler.
* @details EasyDelegate is a library that exposes an easy and flexible delegate system with
* the use of C++11's variatic templates.
* @todo CachedDelegate types which can be used for deferred C++ calls which are helpful in a system
* where specific function calls are to be cached and executed at a later date. This is particularily
* helpful in a game simulation where there may be code that is executed in response to a specific event,
* but not immediately. This would allow for internal caching of the arguments so that the system would
* only have to worry about storing the CachedDelegate instances somewhere and invoke them with no
* arguments when appropriate. Said CachedDelegate types cannot be stored in a DelegateSet unless the
* definition fits that of the CachedDelegate::invoke() method.
* @todo Mapping of various delegate for the DelegateSet type values like the this pointer, proc address
* and the delegate instance pointer to helpful data that will provide non O(N) access when using methods
* such as remove_delegate. This would also allow for a system in which the end programmer can quickly
Expand All @@ -35,6 +28,7 @@
#include <stdexcept> // Standard exception types
#include <assert.h> // assert(expr)
#include <vector> // std::vector<type>
#include <tuple> // std::tuple<...>

#ifndef _INCLUDE_EASYDELEGATE_HPP_
#define _INCLUDE_EASYDELEGATE_HPP_
Expand All @@ -57,19 +51,13 @@ namespace EasyDelegate
* @param isMemberDelegate A boolean representing whether or not this delegate
* is an instance of MemberDelegate.
*/
GenericDelegate(const bool isMemberDelegate) : mIsMemberDelegate(isMemberDelegate) { }

/**
* @brief Returns whether or not this delegate calls against the given this pointer.
* @param thisPointer A pointer referring to the object of interest.
* @return A boolean representing whether or not this delegate calls a member function
* against the given this pointer.
* @note Always returns false for StaticDelegate types because they do not use a this pointer.
*/
virtual bool has_thispointer(void *thisPointer) = 0;
GenericDelegate(const bool &isMemberDelegate, const bool &isCachedDelegate) : mIsMemberDelegate(isMemberDelegate),
mIsCachedDelegate(isCachedDelegate) { }

//! A boolean representing whether or not this delegate is a member delegate.
const bool mIsMemberDelegate;
//! A boolean representing whether or not this delegate is a cached delegate.
const bool mIsCachedDelegate;
};

/**
Expand Down Expand Up @@ -512,6 +500,124 @@ namespace EasyDelegate
std::vector<DelegateSet::DelegateBaseType *> mDelegateVector;
};


// Taken from the chosen answer of
// http://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching-function-pointer
template<int ...> struct seq {};
template<int N, int ...S> struct gens : gens<N-1, N-1, S...> {};
template<int ...S> struct gens<0, S...>{ typedef seq<S...> type; };

/**
* @brief A type whose purpose is to provide deferred calling capabilities, as the name applies.
*/
class GenericCachedDelegate : public GenericDelegate
{
public:
/**
* @brief Constructor accepting a GenericDelegate pointer.
* @param delegate A pointer to the GenericDelegate to be stored on the CachedDelegate.
*/
GenericCachedDelegate(GenericDelegate *delegate) : GenericDelegate(delegate->mIsMemberDelegate, true)
{

}

/**
* @brief Dispatches the GenericCachedDelegate, ignoring the return value.
* @details This behaves exactly as the dispatch method available on CachedDelegate
* types except it does not care about the return value of the invoked delegate. You
* have to cast to a CachedDelegate with the proper function signature in order to
* dispatch it and get a return value.
*/
virtual void generic_dispatch(void) = 0;
};

/**
* @brief A delegate type with which you can perform deferred calls.
* @details This is essentially just a wrapper for either a StaticDelegate or MemberDelegate
* instance but it caches any arguments for later invocation.
*/
template <typename returnType, typename... parameters>
class CachedDelegate : public GenericCachedDelegate
{
// Public Methods
public:
/**
* @brief Constructor accepting a delegate instance and function call parameters.
* @param delegate A pointer to the delegate instance that this CachedDelegate is supposed to invoke.
* @param params Anything; these parameters depend on the function signature defined in the template.
*/
CachedDelegate(DelegateBase<returnType, parameters...> *delegate, parameters... params) : GenericCachedDelegate(delegate),
mDelegate(delegate),
mParameters(params...)
{

}

/**
* @brief Standard destructor.
* @note This deletes the stored delegate instance as well, so the deletion of
* any CachedDelegate types will invalidate the delegate it was calling.
*/
~CachedDelegate(void) { delete mDelegate; mDelegate = NULL; }

/**
* @brief Dispatches the CachedDelegate.
* @details This is equivalent to the invoke() method on all other delegate
* types except the parameters were cached at creation. Said cached parameters
* will be passed in automatically upon calling this, so it is completely safe
* to store.
* @return Anything; it depends on the function signature defined in the template.
*/
returnType dispatch(void)
{
return performCachedCall(typename gens<sizeof...(parameters)>::type());
}

/**
* @brief Dispatches the CachedDelegate, ignoring the return value.
* @details This behaves exactly as the dispatch method above except it does not
* care about the return of the called function. This method is also callable on
* the CachedDelegateBase type, unlike the normal dispatch method.
*/
void generic_dispatch(void) { dispatch(); }

/**
* @brief Gets the internally invoked delegate.
* @return A pointer to the internally invoked delegate.
*/
const DelegateBase<returnType, parameters...> *get_delegate(void) { return mDelegate; }

// Private Methods
private:
//! Internal templated method to invoke the stored delegate instance.
template<int ...S>
returnType performCachedCall(seq<S...>)
{
assert(mDelegate);

if (!mDelegate)
throw std::runtime_error("Bad CachedDelegate mDelegate pointer!");

return mDelegate->invoke(std::get<S>(mParameters) ...);
}

// Public Members
public:
//! Helper typedef to a StaticDelegate.
typedef StaticDelegate<returnType, parameters...> StaticDelegateType;
template <typename classType>
//! Helper typedef to a MemberDelegate.
using MemberDelegateType = MemberDelegate<classType, returnType, parameters...>;

// Private Members
private:
//! Internal std::tuple that is utilized to cache the parameter list.
const std::tuple<parameters...> mParameters;
//! Pointer to the internally invoked delegate instance.
DelegateBase<returnType, parameters...> *mDelegate;
};

/**
* @brief Base delegate type.
* @details Inheritance from this type allows for both the StaticDelegate
Expand Down Expand Up @@ -541,7 +647,7 @@ namespace EasyDelegate
* @param isMemberDelegate A boolean representing whether or not this delegate is a
* member delegate.
*/
DelegateBase(const bool &isMemberDelegate) : GenericDelegate(isMemberDelegate) { }
DelegateBase(const bool &isMemberDelegate) : GenericDelegate(isMemberDelegate, false) { }

/**
* @brief Returns whether or not this delegate calls the given static proc address.
Expand Down Expand Up @@ -573,6 +679,16 @@ namespace EasyDelegate
return memberDelegateObj->has_procaddress(procAddress);
}


/**
* @brief Returns whether or not this delegate calls against the given this pointer.
* @param thisPointer A pointer referring to the object of interest.
* @return A boolean representing whether or not this delegate calls a member function
* against the given this pointer.
* @note Always returns false for StaticDelegate types because they do not use a this pointer.
*/
virtual bool has_thispointer(void *thisPointer) = 0;

/**
* @brief Invoke the delegate with the given arguments and return a value, if any.
* @param params Anything; It depends on the function signature specified in the template.
Expand Down
4 changes: 2 additions & 2 deletions include/mainpage.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* @file mainpage.h
* @version 1.0
* @date 9/23/2014
* @version 1.1
* @date 9/25/2014
* @author <a href="https://github.com/Ragora">Robert MacGregor</a>
* @brief Main page file for use with Doxygen. This file does absolutely nothing otherwise.
*
Expand Down

0 comments on commit 9400c93

Please sign in to comment.