I've been trying to implement delegate for my event system. I thought that maybe std::function can do the trick for me, but it turned out to be not comparable, so what i have end up doing is this - std::function replacement.
It allows to bind function pointers to TFunction object and call it later with specified parameters like the std::function does.
It returns what unredlying function returns.
TFunction class is trivially constructable, so move/copy constructors and operators are defined implicitly.
I use it in a context of event & delegate to subscribe callbacks on application events.
TFunction.h:
#pragma once
using uint64 = unsigned long long;
#if _WIN64
#pragma pointers_to_members( full_generality, multiple_inheritance )
#endif
namespace t3d
{
template<typename T> class TFunction;
template<typename Return_T, typename... Args_T>
class TFunction<Return_T(Args_T...)>
{
public:
// Aliases:
template<class C>
using CallbackMember_T = Return_T(C::*)(Args_T...);
using CallbackStatic_T = Return_T(*)(Args_T...);
// Constructors:
TFunction()
: CallbackMember (nullptr)
, FunctionPointers { &TFunction::CallMember, &TFunction::CallStatic }
, CallbackStatic (nullptr)
, Instance (nullptr)
, Index (0)
{
}
template<class C>
TFunction(C* Instance, CallbackMember_T<C> Callback)
: CallbackMember (reinterpret_cast<CallbackMember_T<FInstance>>(Callback))
, FunctionPointers { &TFunction::CallMember, &TFunction::CallStatic }
, CallbackStatic (nullptr)
, Instance (reinterpret_cast<FInstance*>(Instance))
, Index (0)
{
}
TFunction(CallbackStatic_T Callback)
: CallbackMember (nullptr)
, FunctionPointers { &TFunction::CallMember , &TFunction::CallStatic }
, CallbackStatic (Callback)
, Instance (nullptr)
, Index (1)
{
}
// Functions:
template<class C>
constexpr void Bind(C* Instance, CallbackMember_T<C> Callback) noexcept
{
this->Instance = reinterpret_cast<FInstance*>(Instance);
this->CallbackMember = reinterpret_cast<CallbackMember_T<FInstance>>(Callback);
this->CallbackStatic = nullptr;
this->Index = 0;
}
constexpr void Bind(CallbackStatic_T Callback) noexcept
{
this->Instance = nullptr;
this->CallbackMember = nullptr;
this->CallbackStatic = Callback;
this->Index = 1;
}
constexpr Return_T Invoke(Args_T... Args) const
{
return (this->*FunctionPointers[Index])(std::forward<Args_T>(Args)...);
}
template<class C>
constexpr bool8 IsEqual(CallbackMember_T<C> Callback) const
{
return reinterpret_cast<CallbackMember_T<C>>(CallbackMember) == Callback;
}
constexpr bool8 IsEqual(CallbackStatic_T Callback) const noexcept
{
return CallbackStatic == Callback;
}
constexpr bool8 IsEmpty() const noexcept
{
return (Instance == nullptr) && (CallbackMember == nullptr) && (CallbackStatic == nullptr);
}
// Operators:
explicit operator bool () const noexcept
{
return (Instance && CallbackMember) || CallbackStatic;
}
constexpr bool8 operator == (const TFunction& Right) const noexcept
{
return (Instance == Right.Instance) && (CallbackMember == Right.CallbackMember) && (CallbackStatic == Right.CallbackStatic);
}
constexpr bool8 operator != (const TFunction& Right) const noexcept
{
return (Instance != Right.Instance) || (CallbackMember != Right.CallbackMember) || (CallbackStatic != Right.CallbackStatic);
}
private:
using CallbackInternal_T = Return_T(TFunction::*)(Args_T...) const;
// Private Functions:
Return_T CallMember(Args_T... Args) const
{
return (Instance->*CallbackMember)(std::forward<Args_T>(Args)...);
}
Return_T CallStatic(Args_T... Args) const
{
return (*CallbackStatic)(std::forward<Args_T>(Args)...);
}
// Variables:
class FInstance {};
CallbackMember_T<FInstance> CallbackMember;
CallbackInternal_T FunctionPointers[2];
CallbackStatic_T CallbackStatic;
FInstance* Instance;
uint64 Index;
};
}
Usage example:
Main.cpp:
struct FData
{
int A;
int B;
};
struct FResult
{
bool Success;
};
class FClass
{
public:
FResult Function(FData Data)
{
return FResult{ true };
}
};
int main()
{
FClass Class;
// Example 1 (Default construction):
t3d::TFunction<FResult(FData)> TestFunction;
TestFunction.Bind(&Class, &FClass::Function);
TestFunction.Invoke(FData{ 1, 2 });
// Example 2 (Parametrized construction):
t3d::TFunction<FResult(FData)> TestFunction(&Class, &FClass::Function);
TestFunction.Invoke(FData{ 1, 2 });
return 0;
}
Supported target types: Member virtual/non-virtual function pointer, Static/global function pointer;
Can be compared for equality to TFunction or any function pointer/address.
It 3 times slower than function call, does not allocate and is 7 times faster than std::function, but at expense of not supporting lambda expressions and functors.
This is by design however, it supposed to be used as a delegate for event system like this:
using Delegate_T = TFunction<EPropogation(T)>
Any suggestions?