Don Clugston's fast delegate C++11 implementation
#ifndef SSVU_FASTFUNC | |
#define SSVU_FASTFUNC | |
#include <cstring> | |
#include <type_traits> | |
#include <cassert> | |
#include <cstddef> | |
#include <memory> | |
#include <new> | |
#include <utility> | |
namespace ssvu | |
{ | |
namespace Internal | |
{ | |
class AnyClass; | |
using AnyPtrThis = AnyClass*; | |
using AnyPtrFunc = void(AnyClass::*)(); | |
template<typename TReturn = void, typename... TArgs> using AnyPtrFuncT = TReturn(AnyClass::*)(TArgs...); | |
template<typename TReturn = void, typename... TArgs> using AnyPtrStaticFuncT = TReturn(*)(TArgs...); | |
constexpr std::size_t SingleMemFuncPtrSize{sizeof(void(AnyClass::*)())}; | |
template<class TOut, class TIn> union HorribleUnion { TOut out; TIn in; }; | |
template<class TOut, class TIn> inline TOut horrible_cast(TIn mIn) noexcept { HorribleUnion<TOut, TIn> u; static_assert(sizeof(TIn) == sizeof(u) && sizeof(TIn) == sizeof(TOut), "Cannot use horrible_cast<>"); u.in = mIn; return u.out; } | |
template<class TOut, class TIn> inline TOut unsafe_horrible_cast(TIn mIn) noexcept { HorribleUnion<TOut, TIn> u; u.in = mIn; return u.out; } | |
template<std::size_t TN> struct SimplifyMemFunc | |
{ | |
template<class TThis, class TFunc> inline static AnyPtrThis convert(const TThis*, TFunc, AnyPtrFunc&) noexcept | |
{ | |
static_assert(TN - 100, "Unsupported member function pointer on this compiler"); | |
return 0; | |
} | |
}; | |
template<> struct SimplifyMemFunc<SingleMemFuncPtrSize> | |
{ | |
template<class TThis, class TFunc> inline static AnyPtrThis convert(const TThis* mThis, TFunc mFunc, AnyPtrFunc& mFuncOut) noexcept | |
{ | |
mFuncOut = reinterpret_cast<AnyPtrFunc>(mFunc); | |
return reinterpret_cast<AnyPtrThis>(const_cast<TThis*>(mThis)); | |
} | |
}; | |
template<typename TReturn, typename... TArgs> struct Closure | |
{ | |
private: | |
using PtrFuncT = AnyPtrFuncT<TReturn, TArgs...>; | |
using PtrStaticFuncT = AnyPtrStaticFuncT<TReturn, TArgs...>; | |
AnyPtrThis ptrThis{nullptr}; | |
AnyPtrFunc ptrFunction{nullptr}; | |
public: | |
template<class TThis, class TFunc> inline void bind(TThis* mThis, TFunc mFunc) noexcept { ptrThis = SimplifyMemFunc<sizeof(mFunc)>::convert(mThis, mFunc, ptrFunction); } | |
template<class TThis, class TInvoker> inline void bind(TThis* mThis, TInvoker mInvoker, PtrStaticFuncT mFunc) noexcept | |
{ | |
if(mFunc == nullptr) ptrFunction = nullptr; else bind(mThis, mInvoker); | |
ptrThis = horrible_cast<AnyPtrThis>(mFunc); | |
} | |
inline bool operator==(std::nullptr_t) const noexcept { return ptrThis == nullptr && ptrFunction == nullptr; } | |
inline bool operator==(const Closure& mRhs) const noexcept { return ptrThis == mRhs.ptrThis && ptrFunction == mRhs.ptrFunction; } | |
inline bool operator==(PtrStaticFuncT mPtr) const noexcept { return mPtr == nullptr ? *this == nullptr : mPtr == reinterpret_cast<PtrStaticFuncT>(getStaticFunc()); } | |
inline bool operator!=(std::nullptr_t) const noexcept { return !operator==(nullptr); } | |
inline bool operator!=(const Closure& mRhs) const noexcept { return !operator==(mRhs); } | |
inline bool operator!=(PtrStaticFuncT mPtr) const noexcept { return !operator==(mPtr); } | |
inline bool operator<(const Closure& mRhs) const { return ptrThis != mRhs.ptrThis ? ptrThis < mRhs.ptrThis : std::memcmp(&ptrFunction, &mRhs.ptrFunction, sizeof(ptrFunction)) < 0; } | |
inline bool operator>(const Closure& mRhs) const { return !operator<(mRhs); } | |
inline std::size_t getHash() const noexcept { return reinterpret_cast<std::size_t>(ptrThis) ^ Internal::unsafe_horrible_cast<std::size_t>(ptrFunction); } | |
inline AnyPtrThis getPtrThis() const noexcept { return ptrThis; } | |
inline PtrFuncT getPtrFunction() const noexcept { return reinterpret_cast<PtrFuncT>(ptrFunction); } | |
inline PtrStaticFuncT getStaticFunc() const noexcept { return horrible_cast<PtrStaticFuncT>(this); } | |
}; | |
template<typename TReturn, typename... TArgs> class FastFuncImpl | |
{ | |
private: | |
using PtrStaticFuncT = AnyPtrStaticFuncT<TReturn, TArgs...>; | |
Closure<TReturn, TArgs...> closure; | |
inline TReturn invokeStaticFunc(TArgs... mArgs) const { return (*(closure.getStaticFunc()))(std::forward<TArgs>(mArgs)...); } | |
protected: | |
template<class TThis, class TFunc> inline void bind(TThis* mThis, TFunc mFunc) noexcept { closure.bind(mThis, mFunc); } | |
template<class TFunc> inline void bind(TFunc mFunc) noexcept { closure.bind(this, &FastFuncImpl::invokeStaticFunc, mFunc); } | |
public: | |
inline FastFuncImpl() noexcept = default; | |
inline FastFuncImpl(std::nullptr_t) noexcept { } | |
inline FastFuncImpl(PtrStaticFuncT mFunc) noexcept { bind(mFunc); } | |
template<typename X, typename Y> inline FastFuncImpl(X* mThis, Y mFunc) noexcept { bind(mThis, mFunc); } | |
inline FastFuncImpl& operator=(PtrStaticFuncT mFunc) noexcept { bind(mFunc); } | |
inline TReturn operator()(TArgs... mArgs) const { return (closure.getPtrThis()->*(closure.getPtrFunction()))(std::forward<TArgs>(mArgs)...); } | |
inline bool operator==(std::nullptr_t) const noexcept { return closure == nullptr; } | |
inline bool operator==(const FastFuncImpl& mImpl) const noexcept { return closure == mImpl.closure; } | |
inline bool operator==(PtrStaticFuncT mFuncPtr) const noexcept { return closure == mFuncPtr; } | |
inline bool operator!=(std::nullptr_t) const noexcept { return !operator==(nullptr); } | |
inline bool operator!=(const FastFuncImpl& mImpl) const noexcept { return !operator==(mImpl); } | |
inline bool operator!=(PtrStaticFuncT mFuncPtr) const noexcept { return !operator==(mFuncPtr); } | |
inline bool operator<(const FastFuncImpl& mImpl) const { return closure < mImpl.closure; } | |
inline bool operator>(const FastFuncImpl& mImpl) const { return !operator<(mImpl); } | |
}; | |
} | |
template<typename T> struct MemFuncToFunc; | |
template<typename TReturn, typename TThis, typename... TArgs> struct MemFuncToFunc<TReturn(TThis::*)(TArgs...) const> { using Type = TReturn(*)(TArgs...); }; | |
#define ENABLE_IF_CONV_TO_FUN_PTR(x) typename std::enable_if<std::is_constructible<typename MemFuncToFunc<decltype(&std::decay<x>::type::operator())>::Type, x>::value>::type* = nullptr | |
#define ENABLE_IF_NOT_CONV_TO_FUN_PTR(x) typename std::enable_if<!std::is_constructible<typename MemFuncToFunc<decltype(&std::decay<x>::type::operator())>::Type, x>::value>::type* = nullptr | |
#define ENABLE_IF_SAME_TYPE(x, y) typename = typename std::enable_if<!std::is_same<x, typename std::decay<y>::type>{}>::type | |
template<typename T> class FastFunc; | |
template<typename TReturn, typename... TArgs> class FastFunc<TReturn(TArgs...)> : public Internal::FastFuncImpl<TReturn, TArgs...> | |
{ | |
private: | |
using BaseType = Internal::FastFuncImpl<TReturn, TArgs...>; | |
std::shared_ptr<void> storage; | |
template<typename T> inline static void funcDeleter(void* mPtr) { static_cast<T*>(mPtr)->~T(); operator delete(mPtr); } | |
public: | |
using BaseType::BaseType; | |
inline FastFunc() noexcept = default; | |
template<typename TFunc, ENABLE_IF_SAME_TYPE(FastFunc, TFunc)> inline FastFunc(TFunc&& mFunc, ENABLE_IF_CONV_TO_FUN_PTR(TFunc)) | |
{ | |
using FuncType = typename std::decay<TFunc>::type; | |
this->bind(&mFunc, &FuncType::operator()); | |
} | |
template<typename TFunc, ENABLE_IF_SAME_TYPE(FastFunc, TFunc)> inline FastFunc(TFunc&& mFunc, ENABLE_IF_NOT_CONV_TO_FUN_PTR(TFunc)) | |
: storage(operator new(sizeof(TFunc)), funcDeleter<typename std::decay<TFunc>::type>) | |
{ | |
using FuncType = typename std::decay<TFunc>::type; | |
new (storage.get()) FuncType(std::forward<TFunc>(mFunc)); | |
this->bind(storage.get(), &FuncType::operator()); | |
} | |
}; | |
#undef ENABLE_IF_CONV_TO_FUN_PTR | |
#undef ENABLE_IF_NOT_CONV_TO_FUN_PTR | |
#undef ENABLE_IF_SAME_TYPE | |
} | |
#endif |
This comment has been minimized.
This comment has been minimized.
Can you post working test? |
This comment has been minimized.
This comment has been minimized.
Looks interesting. But note that "test 6" of Clugston's Here's some comparison-test-code (extracted from Clugston's original; with minor changes ( #include <stdio.h>
#include <iostream>
#include "FastDelegate.h"
#include "FastFunc.hpp"
class CBaseClass {
protected:
const char *m_name;
public:
CBaseClass(const char *name) : m_name(name) {};
void SimpleMemberFunction(int num, const char *str) {
printf("In SimpleMemberFunction in %s. Num=%d, str = %s\n", m_name, num, str); }
int SimpleMemberFunctionReturnsInt(int num, const char *str) {
printf("In SimpleMemberFunction in %s. Num=%d, str = %s\n", m_name, num, str); return -1; }
void ConstMemberFunction(int num, const char *str) const {
printf("In ConstMemberFunction in %s. Num=%d, str = %s\n", m_name, num, str); }
virtual void SimpleVirtualFunction(int num, const char *str) {
printf("In SimpleVirtualFunction in %s. Num=%d, str = %s\n", m_name, num, str); }
static void StaticMemberFunction(int num, const char *str) {
printf("In StaticMemberFunction. Num=%d, str =%s\n", num, str); }
};
class COtherClass {
double rubbish; // to ensure this class has non-zero size.
public:
virtual void UnusedVirtualFunction(void) { }
virtual void TrickyVirtualFunction(int num, const char *str)=0;
};
class VeryBigClass {
int letsMakeThingsComplicated[400];
};
// This declaration ensures that we get a convoluted class heirarchy.
class CDerivedClass : public VeryBigClass, virtual public COtherClass, virtual public CBaseClass
{
double m_somemember[8];
public:
CDerivedClass() : CBaseClass("Base of Derived") { m_somemember[0]=1.2345; }
void SimpleDerivedFunction(int num, const char *str) { printf("In SimpleDerived. num=%d\n", num); }
virtual void AnotherUnusedVirtualFunction(int num, const char *str) {}
virtual void TrickyVirtualFunction(int num, const char *str) {
printf("In Derived TrickyMemberFunction. Num=%d, str = %s\n", num, str);
}
};
using namespace fastdelegate;
int main(void)
{
using FF = ssvu::FastFunc<void(int, const char *)>;
using FD = FastDelegate2<int, const char *>;
FF ssvu_ff;
FD fastdele_ff;
CDerivedClass d;
ssvu_ff = FF(&d, &CBaseClass::SimpleVirtualFunction);
fastdele_ff = MakeDelegate(&d, &CBaseClass::SimpleVirtualFunction);
std::cout << "Calling ssvu_ff: >>> ";
ssvu_ff(6, "asdf");
std::cout << "<<<" << std::endl;
std::cout << "Calling fastdele_ff: >>> ";
fastdele_ff(6, "asdf");
std::cout << "<<<" << std::endl;
} |
This comment has been minimized.
This comment has been minimized.
Which platforms are you running? Do you have the same (differing) behaviour? Since the code uses some non-standard hacks... I should mention my platform c++ -v ## g++ -v shows
|
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
Example benchmark code: