diff options
Diffstat (limited to 'src/util/callback.h')
| -rw-r--r-- | src/util/callback.h | 88 |
1 files changed, 88 insertions, 0 deletions
diff --git a/src/util/callback.h b/src/util/callback.h new file mode 100644 index 0000000..df93b59 --- /dev/null +++ b/src/util/callback.h @@ -0,0 +1,88 @@ +#pragma once +#include "typedef.h" + +template <typename T> +struct FN; + +template <typename RET, typename... ARGS> +struct FN<RET(ARGS...)> { + // voodoo + template <typename T> struct __strip_ref { using type = T; }; + template <typename T> struct __strip_ref<T&> { using type = T; }; + template <typename T> struct __strip_ref<T&&> { using type = T; }; + template <typename R, typename... A> struct __strip_ref<R(A...)> { using type = R(*)(A...); }; + template <typename R, typename... A> struct __strip_ref<R(&)(A...)> { using type = R(*)(A...); }; + + void* data; + U32 size; + RET( *invoke )( void*, ARGS... ); + void( *destroy )( void* ); + void*( *clone )( void* ); + + template <typename F> + FN( F&& f ) { + using __stripped = typename __strip_ref<F>::type; + data = new __stripped( static_cast<F&&>(f) ); + size = sizeof( __stripped ); + invoke = pfn( void* d, ARGS... args ) -> RET { + return ( *(__stripped*)d )( args... ); + }; + destroy = pfn( void* d ) { + delete (__stripped*)d; + }; + clone = pfn( void* data ) { + return (void*)new __stripped( *(__stripped*)data ); + }; + } + + template <> + FN( int&& ) : data( 0 ), invoke( 0 ), destroy( 0 ) {} + FN() : data( 0 ), invoke( 0 ), destroy( 0 ) {} + + FN( FN&& other ) : data( other.data ), invoke( other.invoke ), destroy( other.destroy ) { + other.data = 0; + other.invoke = 0; + other.destroy = 0; + } + + FN& operator=( FN&& other ) { + if( this == &other ) return *this; + if( destroy ) destroy( data ); + data = other.data; + invoke = other.invoke; + destroy = other.destroy; + other.data = 0; + other.invoke = 0; + other.destroy = 0; + return *this; + } + + FN( const FN& other ) { + *this = other; + }; + + FN& operator=( const FN& other ) { + if( !other.data ) { + data = 0; + invoke = 0; + destroy = 0; + size = 0; + return *this; + } + + size = other.size; + data = other.clone( other.data ); + invoke = other.invoke; + destroy = other.destroy; + clone = other.clone; + return *this; + } + + ~FN() { if( destroy ) destroy( data ); } + operator bool() const { return !!invoke; } + + RET operator()( ARGS... args ) const { + return invoke( data, args... ); + } + +}; |
