Last active
January 1, 2016 00:49
-
-
Save kuhar/8068893 to your computer and use it in GitHub Desktop.
C++11 delegates for cocos2d-x
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Delegate.h | |
* | |
* Created on: Dec 18, 2013 | |
* Author: Jakub 'kuhar' Kuderski | |
*/ | |
#ifndef DELEGATE_H | |
#define DELEGATE_H | |
#define DELEGATE_ENABLE_ASSERTIONS 1 | |
#if DELEGATE_ENABLE_ASSERTIONS == 1 | |
#include <cassert> | |
#endif | |
#include <vector> | |
#include <utility> | |
#include <algorithm> | |
#include <functional> | |
#include "cocos2d.h" | |
namespace Utils | |
{ | |
#if DELEGATE_ENABLE_ASSERTIONS == 1 | |
#define DELEGATE_ASSERT( CONDITION ) assert( CONDITION ) | |
#else | |
#define DELEGATE_ASSERT( CONDITION ) | |
#endif | |
template<typename ReturnType, typename... Args> | |
class Delegate | |
{ | |
typedef cocos2d::CCObject BaseClass; | |
typedef std::pair<BaseClass*, std::function<ReturnType ( BaseClass*, Args... ) > > | |
ObjectMethodPair; | |
public: | |
Delegate() | |
{ | |
} | |
Delegate ( const Delegate& otherDelegate ) | |
{ | |
m_methods = otherDelegate.m_methods; | |
m_functions = otherDelegate.m_functions; | |
} | |
Delegate ( Delegate&& otherDelegate ) | |
{ | |
m_methods.swap ( otherDelegate.m_methods ); | |
m_functions.swap ( otherDelegate.m_functions ); | |
otherDelegate.clear(); | |
} | |
Delegate& operator= ( const Delegate& otherDelegate ) | |
{ | |
if ( this != &otherDelegate ) | |
{ | |
m_methods = otherDelegate.m_methods; | |
m_functions = otherDelegate.m_functions; | |
} | |
return *this; | |
} | |
Delegate& operator= ( Delegate && otherDelegate ) | |
{ | |
if ( this != &otherDelegate ) | |
{ | |
m_methods.swap ( otherDelegate.m_methods ); | |
m_functions.swap ( otherDelegate.m_functions ); | |
otherDelegate.clear(); | |
} | |
return *this; | |
} | |
template<typename U> | |
Delegate ( BaseClass* pObject, const U& method ) | |
{ | |
add ( pObject, method ); | |
} | |
template<typename U> | |
Delegate ( const U& function ) | |
{ | |
add ( function ); | |
} | |
template<typename U> | |
Delegate& add ( BaseClass* pObject, const U& method ) | |
{ | |
m_methods.emplace_back ( pObject, std::mem_fn ( | |
static_cast<ReturnType ( BaseClass::* ) ( Args... ) > ( method ) ) ); | |
return *this; | |
} | |
template<typename U> | |
Delegate& add ( const U& function ) | |
{ | |
m_functions.emplace_back ( function ); | |
return *this; | |
} | |
std::vector<ReturnType> call ( Args... params ) | |
{ | |
DELEGATE_ASSERT ( isCallable() ); | |
std::vector<ReturnType> results; | |
for ( size_t i = 0; i < m_methods.size(); ++i ) | |
{ | |
results.emplace_back ( m_methods[i].second ( m_methods[i].first, params... ) ); | |
} | |
for ( auto & function : m_functions ) | |
{ | |
results.emplace_back ( function ( params... ) ); | |
} | |
DELEGATE_ASSERT ( results.size() == m_methods.size() + m_functions.size() ); | |
return results; | |
} | |
ReturnType callOnlyOne ( Args&&... params ) | |
{ | |
DELEGATE_ASSERT ( isCallable() ); | |
DELEGATE_ASSERT ( m_methods.size() + m_functions.size() == 1 ); | |
if ( m_methods.size() == 1 ) | |
{ | |
return m_methods[0].second ( m_methods[0].first, | |
std::forward<Args> ( params )... ); | |
} | |
return m_functions[0] ( std::forward<Args> ( params )... ); | |
} | |
void removeCallbacksForObject ( const BaseClass* const pObject ) | |
{ | |
m_methods.erase ( std::remove_if ( m_methods.begin(), m_methods.end(), | |
[&] ( const ObjectMethodPair & objectMethodPair ) | |
{ | |
return objectMethodPair.first == pObject; | |
} ) ); | |
} | |
void clear() | |
{ | |
m_methods.clear(); | |
m_functions.clear(); | |
} | |
size_t size() const | |
{ | |
return m_methods.size() + m_functions.size(); | |
} | |
bool isCallable() const | |
{ | |
return std::all_of ( m_methods.begin(), m_methods.end(), | |
[] ( const ObjectMethodPair & objectMethodPair ) | |
{ | |
return objectMethodPair.first != nullptr; | |
} ); | |
} | |
bool empty() const | |
{ | |
return m_methods.empty() || m_functions.empty(); | |
} | |
private: | |
std::vector<ObjectMethodPair> m_methods; | |
std::vector<std::function<ReturnType ( Args... ) > > m_functions; | |
}; | |
// specialization for no return (void) | |
template<typename... Args> | |
class Delegate <void, Args...> | |
{ | |
typedef cocos2d::CCObject BaseClass; | |
typedef std::pair<BaseClass*, std::function<void ( BaseClass*, Args... ) > > | |
ObjectMethodPair; | |
public: | |
Delegate() | |
{ | |
} | |
Delegate ( const Delegate& otherDelegate ) | |
{ | |
m_methods = otherDelegate.m_methods; | |
m_functions = otherDelegate.m_functions; | |
} | |
Delegate ( Delegate&& otherDelegate ) | |
{ | |
m_methods.swap ( otherDelegate.m_methods ); | |
m_functions.swap ( otherDelegate.m_functions ); | |
otherDelegate.clear(); | |
} | |
Delegate& operator= ( const Delegate& otherDelegate ) | |
{ | |
if ( this != &otherDelegate ) | |
{ | |
m_methods = otherDelegate.m_methods; | |
m_functions = otherDelegate.m_functions; | |
} | |
return *this; | |
} | |
Delegate& operator= ( Delegate && otherDelegate ) | |
{ | |
if ( this != &otherDelegate ) | |
{ | |
m_methods.swap ( otherDelegate.m_methods ); | |
m_functions.swap ( otherDelegate.m_functions ); | |
otherDelegate.clear(); | |
} | |
return *this; | |
} | |
template<typename U> | |
Delegate ( BaseClass* pObject, const U& method ) | |
{ | |
add ( pObject, method ); | |
} | |
template<typename U> | |
Delegate ( const U& function ) | |
{ | |
add ( function ); | |
} | |
template<typename U> | |
Delegate& add ( BaseClass* pObject, const U& method ) | |
{ | |
m_methods.emplace_back ( pObject, | |
std::mem_fn ( static_cast<void ( BaseClass::* ) ( Args... ) > ( method ) ) ); | |
return *this; | |
} | |
template<typename U> | |
Delegate& add ( const U& function ) | |
{ | |
m_functions.emplace_back ( function ); | |
return *this; | |
} | |
void call ( Args... params ) | |
{ | |
DELEGATE_ASSERT ( isCallable( ) ); | |
for ( size_t i = 0; i < m_methods.size(); ++i ) | |
{ | |
m_methods[i].second ( m_methods[i].first, params... ); | |
} | |
for ( auto & function : m_functions ) | |
{ | |
function ( params... ); | |
} | |
} | |
void callOnlyOne ( Args&&... params ) | |
{ | |
DELEGATE_ASSERT ( isCallable( ) ); | |
DELEGATE_ASSERT ( m_methods.size() + m_functions.size() == 1 ); | |
if ( m_methods.size( ) == 1 ) | |
{ | |
m_methods[0].second ( m_methods[0].first, std::forward<Args> ( params )... ); | |
return; | |
} | |
m_functions[0] ( std::forward<Args> ( params )... ); | |
} | |
void removeCallbacksForObject ( const BaseClass* const pObject ) | |
{ | |
m_methods.erase ( std::remove_if ( m_methods.begin(), m_methods.end(), | |
[&] ( const ObjectMethodPair & objectMethodPair ) | |
{ | |
return objectMethodPair.first == pObject; | |
} ) ); | |
} | |
void clear() | |
{ | |
m_methods.clear(); | |
m_functions.clear(); | |
} | |
size_t size() const | |
{ | |
return m_methods.size() + m_functions.size(); | |
} | |
bool isCallable() const | |
{ | |
return std::all_of ( m_methods.begin( ), m_methods.end( ), | |
[] ( const ObjectMethodPair & objectMethodPair ) | |
{ | |
return objectMethodPair.first != nullptr; | |
} ); | |
} | |
bool empty() const | |
{ | |
return m_methods.empty() || m_functions.empty(); | |
} | |
private: | |
std::vector<ObjectMethodPair> m_methods; | |
std::vector<std::function<void ( Args... ) > > m_functions; | |
}; | |
#undef DELEGATE_ASSERT | |
} // namespace utils | |
#undef DELEGATE_ENABLE_ASSERTIONS | |
#endif // DELEGATE_H |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The code isn't entirely type-safe, do not use it, try https://gist.github.com/kuhar/8163865 instead.