Last active
August 24, 2023 08:15
-
-
Save vittorioromeo/6462221 to your computer and use it in GitHub Desktop.
Don Clugston's fast delegate C++11 implementation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 |
Can you post working test?
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;
}
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
Example benchmark code: