Instantly share code, notes, and snippets.

Embed
What would you like to do?
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
@SuperV1234

This comment has been minimized.

Copy link
Owner Author

SuperV1234 commented Sep 6, 2013

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;
}
@slyshykO

This comment has been minimized.

Copy link

slyshykO commented Sep 14, 2013

Can you post working test?

@ajneu

This comment has been minimized.

Copy link

ajneu commented Nov 4, 2015

Looks interesting.

But note that "test 6" of Clugston's Demo.cpp (reference), produces a different result than ssvu::FastFunc (in Revision 6):

Here's some comparison-test-code (extracted from Clugston's original; with minor changes (const char*)):

#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;
}
@ajneu

This comment has been minimized.

Copy link

ajneu commented Nov 4, 2015

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

Using built-in specs.
COLLECT_GCC=c++
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 5.2.1-23' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-5-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-5-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-5-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --with-arch-32=i586 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 5.2.1 20151028 (Debian 5.2.1-23)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment