diff --git a/Source/Engine/Core/Delegate.h b/Source/Engine/Core/Delegate.h
index 17c0c6afe..bba43eb16 100644
--- a/Source/Engine/Core/Delegate.h
+++ b/Source/Engine/Core/Delegate.h
@@ -5,7 +5,7 @@
#include "Engine/Core/Memory/Allocation.h"
///
-/// The function object.
+/// The function object that supports binding static, member and lambda functions.
///
template
class Function
@@ -45,9 +45,28 @@ private:
return (reinterpret_cast(callee)->*Method)(Forward(params)...);
}
+ struct Lambda
+ {
+ int64 Refs;
+ void (*Dtor)(void*);
+ };
+
void* _callee;
StubSignature _function;
+ Lambda* _lambda;
+ FORCE_INLINE void LambdaCtor() const
+ {
+ Platform::InterlockedIncrement((int64 volatile*)&_lambda->Refs);
+ }
+ FORCE_INLINE void LambdaDtor()
+ {
+ if (Platform::InterlockedDecrement(&_lambda->Refs) == 0)
+ {
+ ((Lambda*)_lambda)->Dtor(_callee);
+ Allocator::Free(_lambda);
+ }
+ }
public:
///
@@ -57,6 +76,7 @@ public:
{
_callee = nullptr;
_function = nullptr;
+ _lambda = nullptr;
}
///
@@ -64,9 +84,32 @@ public:
///
Function(Signature method)
{
- ASSERT(method);
_callee = (void*)method;
_function = &StaticPointerMethodStub;
+ _lambda = nullptr;
+ }
+
+ Function(const Function& other)
+ : _callee(other._callee)
+ , _function(other._function)
+ , _lambda(other._lambda)
+ {
+ if (_lambda)
+ LambdaCtor();
+ }
+
+ Function(Function&& other) noexcept
+ : _callee(other._callee)
+ , _function(other._function)
+ , _lambda(other._lambda)
+ {
+ other._lambda = nullptr;
+ }
+
+ ~Function()
+ {
+ if (_lambda)
+ LambdaDtor();
}
public:
@@ -77,8 +120,11 @@ public:
template
void Bind()
{
+ if (_lambda)
+ LambdaDtor();
_callee = nullptr;
_function = &StaticMethodStub;
+ _lambda = nullptr;
}
///
@@ -88,8 +134,11 @@ public:
template
void Bind(T* callee)
{
+ if (_lambda)
+ LambdaDtor();
_callee = callee;
_function = &ClassMethodStub;
+ _lambda = nullptr;
}
///
@@ -98,8 +147,28 @@ public:
/// The method.
void Bind(Signature method)
{
+ if (_lambda)
+ LambdaDtor();
_callee = (void*)method;
_function = &StaticPointerMethodStub;
+ _lambda = nullptr;
+ }
+
+ ///
+ /// Binds a lambda.
+ ///
+ /// The lambda.
+ template
+ void Bind(const T& lambda)
+ {
+ if (_lambda)
+ LambdaDtor();
+ _lambda = (Lambda*)Allocator::Allocate(sizeof(Lambda) + sizeof(T));
+ _lambda->Refs = 1;
+ _lambda->Dtor = [](void* callee) -> void { static_cast(callee)->~T(); };
+ _function = [](void* callee, Params ... params) -> ReturnType { return (*static_cast(callee))(Forward(params)...); };
+ _callee = (byte*)_lambda + sizeof(Lambda);
+ new(_callee) T(lambda);
}
///
@@ -107,8 +176,11 @@ public:
///
void Unbind()
{
+ if (_lambda)
+ LambdaDtor();
_callee = nullptr;
_function = nullptr;
+ _lambda = nullptr;
}
public:
@@ -116,7 +188,6 @@ public:
///
/// Returns true if any function has been binded.
///
- /// True if any function has been binded, otherwise false.
FORCE_INLINE bool IsBinded() const
{
return _function != nullptr;
@@ -144,6 +215,31 @@ public:
return _function(_callee, Forward(params)...);
}
+ Function& operator=(const Function& other)
+ {
+ if (this == &other)
+ return *this;
+ _callee = other._callee;
+ _function = other._function;
+ _lambda = other._lambda;
+ if (_lambda)
+ LambdaCtor();
+ return *this;
+ }
+
+ Function& operator=(Function&& other) noexcept
+ {
+ if (this == &other)
+ return *this;
+ _callee = other._callee;
+ _function = other._function;
+ _lambda = other._lambda;
+ other._callee = nullptr;
+ other._function = nullptr;
+ other._lambda = nullptr;
+ return *this;
+ }
+
FORCE_INLINE bool operator==(const Function& other) const
{
return _function == other._function && _callee == other._callee;
@@ -189,7 +285,17 @@ public:
~Delegate()
{
- Allocator::Free((void*)_ptr);
+ auto ptr = (FunctionType*)_ptr;
+ if (ptr)
+ {
+ while (_size--)
+ {
+ if (ptr->_lambda)
+ ptr->LambdaDtor();
+ ++ptr;
+ }
+ Allocator::Free((void*)_ptr);
+ }
}
public:
@@ -212,7 +318,6 @@ public:
template
void Bind(T* callee)
{
- ASSERT(callee);
FunctionType f;
f.template Bind(callee);
Bind(f);
@@ -228,6 +333,18 @@ public:
Bind(f);
}
+ ///
+ /// Binds a lambda.
+ ///
+ /// The lambda.
+ template
+ void Bind(const T& lambda)
+ {
+ FunctionType f;
+ f.template Bind(lambda);
+ Bind(f);
+ }
+
///
/// Binds a function.
///
@@ -243,7 +360,10 @@ public:
{
if (Platform::InterlockedCompareExchange((intptr volatile*)&bindings[i]._function, (intptr)f._function, 0) == 0)
{
- Platform::AtomicStore((intptr volatile*)&bindings[i]._callee, (intptr)f._callee);
+ bindings[i]._callee = f._callee;
+ bindings[i]._lambda = f._lambda;
+ if (f._lambda)
+ f.LambdaCtor();
return;
}
}
@@ -293,7 +413,6 @@ public:
template
void Unbind(T* callee)
{
- ASSERT(callee);
FunctionType f;
f.template Bind(callee);
Unbind(f);
@@ -322,6 +441,11 @@ public:
{
if (Platform::AtomicRead((intptr volatile*)&bindings[i]._callee) == (intptr)f._callee && Platform::AtomicRead((intptr volatile*)&bindings[i]._function) == (intptr)f._function)
{
+ if (bindings[i]._lambda)
+ {
+ bindings[i].LambdaDtor();
+ bindings[i]._lambda = nullptr;
+ }
Platform::AtomicStore((intptr volatile*)&bindings[i]._callee, 0);
Platform::AtomicStore((intptr volatile*)&bindings[i]._function, 0);
break;
@@ -343,6 +467,11 @@ public:
FunctionType* bindings = (FunctionType*)Platform::AtomicRead(&_ptr);
for (intptr i = 0; i < size; i++)
{
+ if (bindings[i]._lambda)
+ {
+ bindings[i].LambdaDtor();
+ bindings[i]._lambda = nullptr;
+ }
Platform::AtomicStore((intptr volatile*)&bindings[i]._function, 0);
Platform::AtomicStore((intptr volatile*)&bindings[i]._callee, 0);
}
@@ -398,6 +527,9 @@ public:
if (buffer[count]._function != nullptr)
{
buffer[count]._callee = (void*)Platform::AtomicRead((intptr volatile*)&bindings[i]._callee);
+ buffer[count]._lambda = (typename FunctionType::Lambda*)Platform::AtomicRead((intptr volatile*)&bindings[i]._lambda);
+ if (buffer[count]._lambda)
+ buffer[count].LambdaCtor();
count++;
}
}
@@ -414,12 +546,11 @@ public:
FunctionType* bindings = (FunctionType*)Platform::AtomicRead((intptr volatile*)&_ptr);
for (intptr i = 0; i < size; i++)
{
- FunctionType f;
- f._function = (StubSignature)Platform::AtomicRead((intptr volatile*)&bindings[i]._function);
- if (f._function != nullptr)
+ auto function = (StubSignature)Platform::AtomicRead((intptr volatile*)&bindings[i]._function);
+ if (function != nullptr)
{
- f._callee = (void*)Platform::AtomicRead((intptr volatile*)&bindings[i]._callee);
- f._function(f._callee, Forward(params)...);
+ auto callee = (void*)Platform::AtomicRead((intptr volatile*)&bindings[i]._callee);
+ function(callee, Forward(params)...);
}
}
}