public
Last active

Don Clugston's fast delegate C++11 implementation

  • Download Gist
FastFunc.hpp
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
#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

Example benchmark code:

int tempGlobalVar{0};
bool tempGlobalState{false};
int rawFunc(int& xx, int i) { xx+=i; tempGlobalState = !tempGlobalState; return i + 1; }
struct TestStruct { void rawMemFunc(int& xx) const { tempGlobalState = !tempGlobalState; xx += 10; } };
constexpr int numberOfTests{1};
constexpr int repeats{500000000};

int main()
{
    string tempStr{"hello from testequal (capture)"};
    FastFunc<void()> testEqual = nullptr;
    FastFunc<void()> testEqual2 = nullptr;
    if(testEqual == nullptr) testEqual = []{ lo << "hello from testequal (trivial)" << endl; };
    testEqual();
    if(testEqual != nullptr) testEqual = [&tempStr]{ lo << tempStr << endl; };
    testEqual(); testEqual = nullptr;
    if(testEqual == nullptr) testEqual = []{ lo << "hello from testequal (trivial)" << endl; };
    testEqual();
    if(testEqual != nullptr) testEqual = [&tempStr]{ lo << tempStr << endl; };
    testEqual();
    testEqual = testEqual2;
    testEqual2 = [&tempStr]{ lo << tempStr << endl; };
    testEqual = testEqual2;
    testEqual();
    testEqual2();
    testEqual2 = [&tempStr]{ lo << "new" + tempStr << endl; };
    testEqual = testEqual2;
    testEqual();
    testEqual2();


    int tempVar{0};
    TestStruct testStr;
    for(int nTest = 0; nTest < numberOfTests; ++nTest)
    {
        {
            startBenchmark(); { for(int i = 0; i < repeats; ++i) rawFunc(tempVar, i); } lo << lt("raw func") << endBenchmark() << endl;
        }
        {
            function<int(int&, int)> t1 = &rawFunc;
            startBenchmark(); { for(int i = 0; i < repeats; ++i) t1(tempVar, i); } lo << lt("raw func std::func") << endBenchmark() << endl;
        }
        {
            FastFunc<int(int&, int)> t1 = &rawFunc;
            startBenchmark(); { for(int i = 0; i < repeats; ++i) t1(tempVar, i); } lo << lt("raw func don_delegate") << endBenchmark() << endl;
        }
        {
            delegate<int(int&, int)> t1 = &rawFunc;
            startBenchmark(); { for(int i = 0; i < repeats; ++i) t1(tempVar, i); } lo << lt("raw func staticdelegate") << endBenchmark() << endl;
        } lo << endl;

        {
            startBenchmark(); { for(int i = 0; i < repeats; ++i) testStr.rawMemFunc(tempVar); } lo << lt("raw memfunc") << endBenchmark() << endl;
        }
        {
            function<void()> t1 = [&]{ testStr.rawMemFunc(tempVar); };
            startBenchmark(); { for(int i = 0; i < repeats; ++i) t1(); } lo << lt("raw memfunc  std::func") << endBenchmark() << endl;
        }
        {
            FastFunc<void(int&)> t1(&testStr, &TestStruct::rawMemFunc);
            startBenchmark(); { for(int i = 0; i < repeats; ++i) t1(tempVar); } lo << lt("raw memfunc  don_delegate") << endBenchmark() << endl;
        }
        {
            delegate<void()> t1 = [&]{ testStr.rawMemFunc(tempVar); };
            startBenchmark(); { for(int i = 0; i < repeats; ++i) t1(); } lo << lt("raw memfunc  staticdelegate") << endBenchmark() << endl;
        } lo << endl;

        {
            auto t1 = [](int i){ tempGlobalState = !tempGlobalState; return tempGlobalVar + i + 1; };
            startBenchmark(); { for(int i = 0; i < repeats; ++i) t1(i); } lo << lt("trivial auto") << endBenchmark() << endl;
        }
        {
            function<int(int)> t1 = [](int i){ tempGlobalState = !tempGlobalState; return tempGlobalVar + i + 1; };
            startBenchmark(); { for(int i = 0; i < repeats; ++i) t1(i); } lo << lt("trivial std::func") << endBenchmark() << endl;
        }
        {
            FastFunc<int(int)> t1 = [](int i){ tempGlobalState = !tempGlobalState; return tempGlobalVar + i + 1; };
            startBenchmark(); { for(int i = 0; i < repeats; ++i) t1(i); } lo << lt("trivial don_delegate") << endBenchmark() << endl;
        }
        {
            delegate<int(int)> t1 = [](int i){ tempGlobalState = !tempGlobalState; return tempGlobalVar + i + 1; };
            startBenchmark(); { for(int i = 0; i < repeats; ++i) t1(i); } lo << lt("trivial staticdelegate") << endBenchmark() << endl;
        } lo << endl;

        {
            function<int(int&, int)> t2 = rawFunc;
            function<void(int)> t1 = [&tempVar, &t2](int i){ tempVar = t2(tempVar, i); };
            startBenchmark(); { for(int i = 0; i < repeats; ++i) t1(i); } lo << lt("capture std::func") << endBenchmark() << endl;
        }
        {
            FastFunc<int(int&, int)> t2 = rawFunc;
            FastFunc<void(int)> t1 = [&tempVar, &t2](int i){ tempVar = t2(tempVar, i); };
            startBenchmark(); { for(int i = 0; i < repeats; ++i) t1(i); } lo << lt("capture don_delegate") << endBenchmark() << endl;
        }
        {
            delegate<int(int&, int)> t2 = rawFunc;
            delegate<void(int)> t1 = [&tempVar, &t2](int i){ tempVar = t2(tempVar, i); };
            startBenchmark(); { for(int i = 0; i < repeats; ++i) t1(i); } lo << lt("capture staticdelegate") << endBenchmark() << endl;
        } lo << endl;
    } lo << endl << tempVar << tempGlobalVar;

    return 0;
}

Can you post working test?

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.