Skip to content

Instantly share code, notes, and snippets.

@bazhenovc
Last active September 23, 2015 01:29
Show Gist options
  • Save bazhenovc/bdb82db9b88d6188ee43 to your computer and use it in GitHub Desktop.
Save bazhenovc/bdb82db9b88d6188ee43 to your computer and use it in GitHub Desktop.
namespace internal
{
template <typename R, typename ...Args>
struct FunctionImplBase
{
virtual FORCE_INLINE ~FunctionImplBase() {}
virtual R Invoke(Args... args) const = 0;
virtual FunctionImplBase* Clone(uint8_t* storage) = 0;
};
template <typename T, typename R, typename ...Args>
struct FunctionImpl final : public FunctionImplBase<R, Args...>
{
mutable T _function;
FORCE_INLINE FunctionImpl(T func) : _function(func) {}
virtual FORCE_INLINE R Invoke(Args... args) const override
{
return _function(static_cast<Args&&>(args)...);
}
virtual FunctionImplBase* Clone(uint8_t* storage) override
{
return new (storage) FunctionImpl(*this);
}
};
}
template <size_t C, typename T> struct FunctionBase;
template <size_t C, typename R, typename ...Args>
class FunctionBase<C, R(Args...)> final
{
private:
enum
{
kDesiredSize = C,
kMaxCapacity = kDesiredSize - sizeof(internal::FunctionImplBase<R, Args...>*)
};
uint8_t _storage[kMaxCapacity];
internal::FunctionImplBase<R, Args...>* _function = nullptr;
public:
FORCE_INLINE FunctionBase() { static_assert(sizeof(FunctionBase) == kDesiredSize, "Error: wrong function size!"); }
FORCE_INLINE ~FunctionBase()
{
static_assert(sizeof(FunctionBase) == kDesiredSize, "Error: wrong function size!");
if (_function != nullptr) _function->~FunctionImplBase();
}
FORCE_INLINE FunctionBase(const FunctionBase& other)
{
static_assert(sizeof(FunctionBase) == kDesiredSize, "Error: wrong function size!");
if (other._function != nullptr)
_function = other._function->Clone(_storage);
}
template <typename T>
FORCE_INLINE FunctionBase(T fn)
{
static_assert(sizeof(FunctionBase) == kDesiredSize, "Error: wrong function size!");
static_assert(sizeof(T) <= kMaxCapacity, "Error: function size is too big!");
_function = new (_storage) internal::FunctionImpl<T, R, Args...>(fn);
}
FORCE_INLINE FunctionBase& operator=(const FunctionBase& other)
{
static_assert(sizeof(FunctionBase) == kDesiredSize, "Error: wrong function size!");
if (_function != nullptr) {
_function->~FunctionImplBase();
_function = nullptr;
}
if (other._function != nullptr)
_function = other._function->Clone(_storage);
return *this;
}
template <typename T>
FORCE_INLINE FunctionBase& operator=(T fn)
{
static_assert(sizeof(FunctionBase) == kDesiredSize, "Error: wrong function size!");
static_assert(sizeof(T) <= kMaxCapacity, "Error: function size is too big!");
if (_function)
_function->~FunctionImplBase();
_function = new (_storage) internal::FunctionImpl<T, R, Args...>(fn);
return *this;
}
FORCE_INLINE R operator()(Args... args)
{
return _function->Invoke(static_cast<Args&&>(args)...);
}
FORCE_INLINE R operator()(Args... args) const
{
return _function->Invoke(static_cast<Args&&>(args)...);
}
FORCE_INLINE operator bool() const { return _function != nullptr; }
};
template <typename T>
using Function = FunctionBase<32, T>;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment