Skip to content

Instantly share code, notes, and snippets.

@kuhar
Last active January 1, 2016 00:49
Show Gist options
  • Save kuhar/8068893 to your computer and use it in GitHub Desktop.
Save kuhar/8068893 to your computer and use it in GitHub Desktop.
C++11 delegates for cocos2d-x
/*
* 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
@kuhar
Copy link
Author

kuhar commented Dec 28, 2013

The code isn't entirely type-safe, do not use it, try https://gist.github.com/kuhar/8163865 instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment