Created
April 14, 2019 01:47
-
-
Save kerrytazi/e6ddaeaddd53ff3ab1753a6733490143 to your computer and use it in GitHub Desktop.
Template copy/move and move-only functors with configurable size.
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
#pragma once | |
#ifndef _FUNCTION_HPP_INCLUDED_ | |
#define _FUNCTION_HPP_INCLUDED_ | |
#include <type_traits> | |
namespace | |
{ | |
/* Abstract class of functor implementation */ | |
template <typename result_type, typename... args_type> | |
class __declspec(novtable) _FunctionBaseImpl | |
{ | |
public: | |
using func_type = result_type(*)(args_type...); | |
using this_type = _FunctionBaseImpl<result_type, args_type...>; | |
virtual void copy(this_type* dest) const = 0; | |
virtual void move(this_type* dest) = 0; | |
virtual result_type call(args_type... args) = 0; | |
virtual void destroy() = 0; | |
virtual bool is_valid() const = 0; | |
}; | |
/* Type trait to guarantee template signature */ | |
template <typename _Fty> | |
struct _FunctionBaseUnwrap; | |
template <typename _Rty, typename... _Aty> | |
struct _FunctionBaseUnwrap<_Rty(_Aty...)> | |
{ | |
using impl_type = _FunctionBaseImpl<_Rty, _Aty...>; | |
using result_type = _Rty; | |
}; | |
/* Allocation free implementation for functor */ | |
template < | |
typename callable_type, | |
typename result_type, | |
typename... args_type> | |
class _FunctionRawImpl : | |
public _FunctionBaseImpl<result_type, args_type...> | |
{ | |
private: | |
_FunctionRawImpl(callable_type const& func) : | |
m_callable(func) | |
{} | |
public: | |
using base_type = _FunctionBaseImpl<result_type, args_type...>; | |
using this_type = _FunctionRawImpl<callable_type, result_type, args_type...>; | |
_FunctionRawImpl(callable_type&& func) : | |
m_callable(std::move(func)) | |
{} | |
void copy(base_type* dest) const override | |
{ | |
new (dest) this_type(m_callable); | |
} | |
void move(base_type* dest) override | |
{ | |
new (dest) this_type(std::move(m_callable)); | |
} | |
result_type call(args_type... args) override | |
{ | |
return static_cast<result_type>(m_callable(std::forward<args_type>(args)...)); | |
} | |
void destroy() override | |
{ | |
this->~this_type(); | |
} | |
bool is_valid() const override | |
{ | |
// this[0] - vtptr, this[1] - callable | |
return reinterpret_cast<size_t const*>(this)[1] != 0; | |
} | |
private: | |
callable_type m_callable; | |
}; | |
/* Type trait to guarantee template signature */ | |
template <typename _Fty, typename _Rty> | |
struct _FunctionRawUnwrap; | |
template <typename _Fty, typename _Rty, typename... _Aty> | |
struct _FunctionRawUnwrap<_Fty, _Rty(_Aty...)> | |
{ | |
using impl_type = _FunctionRawImpl<_Fty, _Rty, _Aty...>; | |
using result_type = _Rty; | |
using func_type = _Fty; | |
}; | |
/* Functor implementation with allocation */ | |
template < | |
typename callable_type, | |
typename result_type, | |
typename... args_type> | |
class _FunctionAllocImpl : | |
public _FunctionBaseImpl<result_type, args_type...> | |
{ | |
private: | |
_FunctionAllocImpl(callable_type const& func) : | |
m_callable(new callable_type(func)) | |
{} | |
_FunctionAllocImpl(callable_type* func) : | |
m_callable(func) | |
{} | |
public: | |
using base_type = _FunctionBaseImpl<result_type, args_type...>; | |
using this_type = _FunctionAllocImpl<callable_type, result_type, args_type...>; | |
_FunctionAllocImpl(callable_type&& func) : | |
m_callable(new callable_type(std::move(func))) | |
{} | |
void copy(base_type* dest) const override | |
{ | |
new (dest) this_type(*m_callable); | |
} | |
void move(base_type* dest) override | |
{ | |
new (dest) this_type(m_callable); | |
m_callable = nullptr; | |
} | |
result_type call(args_type... args) override | |
{ | |
return static_cast<result_type>((*m_callable)(std::forward<args_type>(args)...)); | |
} | |
void destroy() override | |
{ | |
if (m_callable) | |
{ | |
delete m_callable; | |
m_callable = nullptr; | |
} | |
} | |
bool is_valid() const override | |
{ | |
// this[0] - vtptr, this[1] - callable | |
return reinterpret_cast<size_t const*>(this)[1] != 0; | |
} | |
private: | |
callable_type* m_callable; | |
}; | |
/* Type trait to guarantee template signature */ | |
template <typename _Fty, typename _Rty> | |
struct _FunctionAllocUnwrap; | |
template <typename _Fty, typename _Rty, typename... _Aty> | |
struct _FunctionAllocUnwrap<_Fty, _Rty(_Aty...)> | |
{ | |
using impl_type = _FunctionAllocImpl<_Fty, _Rty, _Aty...>; | |
using result_type = _Rty; | |
using func_type = _Fty; | |
}; | |
} /* anonymous namespace */ | |
/* Copy+Move Functor. | |
* First template argument is calling signature. | |
* Second template argument is total size of functor. | |
* Minimal size must be enough to hold two pointers | |
*/ | |
template < | |
typename _Fty, | |
size_t _Size = sizeof(char*) * 2> | |
class Function | |
{ | |
static_assert( | |
_Size >= sizeof(char*) * 2, | |
"Function size can't be less then two pointers"); | |
private: | |
using unwrap_type = _FunctionBaseUnwrap<_Fty>; | |
using impl_type = unwrap_type::template impl_type; | |
using result_type = unwrap_type::template result_type; | |
using func_type = impl_type::template func_type; | |
char m_storage[_Size]; | |
impl_type* impl() | |
{ return reinterpret_cast<impl_type*>(m_storage); } | |
impl_type const* impl() const | |
{ return reinterpret_cast<impl_type const*>(m_storage); } | |
template <typename _Func> | |
void create(_Func&& func, std::true_type) | |
{ | |
using _impl = _FunctionRawUnwrap<_Func, _Fty>::template impl_type; | |
new (impl()) _impl(std::move(func)); | |
} | |
template <typename _Func> | |
void create(_Func&& func, std::false_type) | |
{ | |
using _impl = _FunctionAllocUnwrap<_Func, _Fty>::template impl_type; | |
new (impl()) _impl(std::move(func)); | |
} | |
/* Empty struct have one byte size | |
* Add one pointer size for vfptr | |
*/ | |
template <typename _Ty> | |
struct is_little : | |
std::bool_constant<std::is_empty<_Ty>() || (sizeof(_Ty) + sizeof(char*) <= _Size)> {}; | |
public: | |
Function() : | |
Function(static_cast<func_type>(nullptr)) | |
{} | |
Function(std::nullptr_t) : | |
Function(static_cast<func_type>(nullptr)) | |
{} | |
Function(func_type func) | |
{ | |
create<func_type>(std::move(func), std::true_type()); | |
} | |
template <typename _Func> | |
Function(_Func func) | |
{ | |
create<_Func>(std::move(func), is_little<_Func>()); | |
} | |
Function(Function const& other) | |
{ | |
other.impl()->copy(impl()); | |
} | |
Function(Function&& other) | |
{ | |
other.impl()->move(impl()); | |
} | |
Function& operator = (std::nullptr_t) | |
{ | |
impl()->destroy(); | |
create<func_type>(nullptr, std::true_type()); | |
return *this; | |
} | |
Function& operator = (func_type func) | |
{ | |
impl()->destroy(); | |
create<func_type>(std::move(func), std::true_type()); | |
return *this; | |
} | |
template <typename _Func> | |
Function& operator = (_Func func) | |
{ | |
impl()->destroy(); | |
create<_Func>(std::move(func), is_little<_Func>()); | |
return *this; | |
} | |
Function& operator = (Function const& other) | |
{ | |
impl()->destroy(); | |
other.impl()->copy(impl()); | |
return *this; | |
} | |
Function& operator = (Function&& other) | |
{ | |
impl()->destroy(); | |
other.impl()->move(impl()); | |
return *this; | |
} | |
~Function() | |
{ | |
impl()->destroy(); | |
} | |
template <typename... args_type> | |
result_type operator () (args_type... args) | |
{ return impl()->call(std::forward<args_type>(args)...); } | |
operator bool () const | |
{ return impl()->is_valid(); } | |
}; | |
/* Same but for Move-only Functor */ | |
namespace | |
{ | |
/* Abstract class of move-functor implementation */ | |
template <typename result_type, typename... args_type> | |
class __declspec(novtable) _MoveFunctionBaseImpl | |
{ | |
public: | |
using func_type = result_type(*)(args_type...); | |
using this_type = _MoveFunctionBaseImpl<result_type, args_type...>; | |
virtual void move(this_type* dest) = 0; | |
virtual result_type call(args_type... args) = 0; | |
virtual void destroy() = 0; | |
virtual bool is_valid() const = 0; | |
}; | |
/* Type trait to guarantee template signature */ | |
template <typename _Fty> | |
struct _MoveFunctionBaseUnwrap; | |
template <typename _Rty, typename... _Aty> | |
struct _MoveFunctionBaseUnwrap<_Rty(_Aty...)> | |
{ | |
using impl_type = _MoveFunctionBaseImpl<_Rty, _Aty...>; | |
using result_type = _Rty; | |
}; | |
/* Allocation free implementation for move-functor */ | |
template < | |
typename callable_type, | |
typename result_type, | |
typename... args_type> | |
class _MoveFunctionRawImpl : | |
public _MoveFunctionBaseImpl<result_type, args_type...> | |
{ | |
public: | |
using base_type = _MoveFunctionBaseImpl<result_type, args_type...>; | |
using this_type = _MoveFunctionRawImpl<callable_type, result_type, args_type...>; | |
_MoveFunctionRawImpl(callable_type&& func) : | |
m_callable(std::move(func)) | |
{} | |
void move(base_type* dest) override | |
{ | |
new (dest) this_type(std::move(m_callable)); | |
} | |
result_type call(args_type... args) override | |
{ | |
return static_cast<result_type>(m_callable(std::forward<args_type>(args)...)); | |
} | |
void destroy() override | |
{ | |
this->~this_type(); | |
} | |
bool is_valid() const override | |
{ | |
// this[0] - vtptr, this[1] - callable | |
return reinterpret_cast<size_t const*>(this)[1] != 0; | |
} | |
private: | |
callable_type m_callable; | |
}; | |
/* Type trait to guarantee template signature */ | |
template <typename _Fty, typename _Rty> | |
struct _MoveFunctionRawUnwrap; | |
template <typename _Fty, typename _Rty, typename... _Aty> | |
struct _MoveFunctionRawUnwrap<_Fty, _Rty(_Aty...)> | |
{ | |
using impl_type = _MoveFunctionRawImpl<_Fty, _Rty, _Aty...>; | |
using result_type = _Rty; | |
using func_type = _Fty; | |
}; | |
/* Move-functor implementation with allocation */ | |
template < | |
typename callable_type, | |
typename result_type, | |
typename... args_type> | |
class _MoveFunctionAllocImpl : | |
public _MoveFunctionBaseImpl<result_type, args_type...> | |
{ | |
private: | |
_MoveFunctionAllocImpl(callable_type* func) : | |
m_callable(func) | |
{} | |
public: | |
using base_type = _MoveFunctionBaseImpl<result_type, args_type...>; | |
using this_type = _MoveFunctionAllocImpl<callable_type, result_type, args_type...>; | |
_MoveFunctionAllocImpl(callable_type&& func) : | |
m_callable(new callable_type(std::move(func))) | |
{} | |
void move(base_type* dest) override | |
{ | |
new (dest) this_type(m_callable); | |
m_callable = nullptr; | |
} | |
result_type call(args_type... args) override | |
{ | |
return static_cast<result_type>((*m_callable)(std::forward<args_type>(args)...)); | |
} | |
void destroy() override | |
{ | |
if (m_callable) | |
{ | |
delete m_callable; | |
m_callable = nullptr; | |
} | |
} | |
bool is_valid() const override | |
{ | |
// this[0] - vtptr, this[1] - callable | |
return reinterpret_cast<size_t const*>(this)[1] != 0; | |
} | |
private: | |
callable_type* m_callable; | |
}; | |
/* Type trait to guarantee template signature */ | |
template <typename _Fty, typename _Rty> | |
struct _MoveFunctionAllocUnwrap; | |
template <typename _Fty, typename _Rty, typename... _Aty> | |
struct _MoveFunctionAllocUnwrap<_Fty, _Rty(_Aty...)> | |
{ | |
using impl_type = _MoveFunctionAllocImpl<_Fty, _Rty, _Aty...>; | |
using result_type = _Rty; | |
using func_type = _Fty; | |
}; | |
} /* anonymous namespace */ | |
/* Move-only Functor. | |
* First template argument is calling signature. | |
* Second template argument is total size of functor. | |
* Minimal size must be enough to hold two pointers | |
*/ | |
template < | |
typename _Fty, | |
size_t _Size = sizeof(char*) * 2> | |
class MoveFunction | |
{ | |
static_assert( | |
_Size >= sizeof(char*) * 2, | |
"MoveFunction size can't be less then two pointers"); | |
private: | |
using unwrap_type = _MoveFunctionBaseUnwrap<_Fty>; | |
using impl_type = unwrap_type::template impl_type; | |
using result_type = unwrap_type::template result_type; | |
using func_type = impl_type::template func_type; | |
char m_storage[_Size]; | |
impl_type* impl() | |
{ return reinterpret_cast<impl_type*>(m_storage); } | |
impl_type const* impl() const | |
{ return reinterpret_cast<impl_type const*>(m_storage); } | |
template <typename _Func> | |
void create(_Func&& func, std::true_type) | |
{ | |
using _impl = _MoveFunctionRawUnwrap<_Func, _Fty>::template impl_type; | |
new (impl()) _impl(std::move(func)); | |
} | |
template <typename _Func> | |
void create(_Func&& func, std::false_type) | |
{ | |
using _impl = _MoveFunctionAllocUnwrap<_Func, _Fty>::template impl_type; | |
new (impl()) _impl(std::move(func)); | |
} | |
/* Empty struct have one byte size | |
* Add one pointer size for vfptr | |
*/ | |
template <typename _Ty> | |
struct is_little : | |
std::bool_constant<std::is_empty<_Ty>() || (sizeof(_Ty) + sizeof(char*) <= _Size)> {}; | |
public: | |
MoveFunction() : | |
MoveFunction(static_cast<func_type>(nullptr)) | |
{} | |
MoveFunction(std::nullptr_t) : | |
MoveFunction(static_cast<func_type>(nullptr)) | |
{} | |
MoveFunction(func_type&& func) | |
{ create<func_type>(std::move(func), std::true_type()); } | |
template <typename _Func> | |
MoveFunction(_Func&& func) | |
{ create<_Func>(std::move(func), is_little<_Func>()); } | |
MoveFunction(MoveFunction const& other) = delete; | |
MoveFunction(MoveFunction&& other) | |
{ other.impl()->move(impl()); } | |
MoveFunction& operator = (std::nullptr_t) | |
{ | |
impl()->destroy(); | |
create<func_type>(nullptr, std::true_type()); | |
return *this; | |
} | |
MoveFunction& operator = (func_type&& func) | |
{ | |
impl()->destroy(); | |
create<func_type>(std::move(func), std::true_type()); | |
return *this; | |
} | |
template <typename _Func> | |
MoveFunction& operator = (_Func&& func) | |
{ | |
impl()->destroy(); | |
create<_Func>(std::move(func), is_little<_Func>()); | |
return *this; | |
} | |
MoveFunction& operator = (MoveFunction const& other) = delete; | |
MoveFunction& operator = (MoveFunction&& other) | |
{ | |
impl()->destroy(); | |
other.impl()->move(impl()); | |
return *this; | |
} | |
~MoveFunction() | |
{ impl()->destroy(); } | |
template <typename... args_type> | |
result_type operator () (args_type... args) | |
{ return impl()->call(std::forward<args_type>(args)...); } | |
operator bool () const | |
{ return impl()->is_valid(); } | |
}; | |
#endif /* !_FUNCTION_HPP_INCLUDED_ */ | |
/* | |
// example | |
#include <iostream> | |
#include <functional> | |
#include <memory> | |
static int add(int _a, int _b) | |
{ | |
return _a + _b; | |
} | |
static void sum(int _a, int _b, Function<void(int)> callback) | |
{ | |
int result = _a + _b; | |
if (callback) | |
{ | |
callback(result); | |
} | |
} | |
int main() | |
{ | |
int a = 3; | |
int b = 4; | |
Function<void()> f1; | |
Function<void(void)> f2; | |
Function<void(int)> f3; | |
Function<int(void)> f4; | |
Function<int(int)> f5; | |
std::function<int(int, int)> _f6([](int _a, int _b) { return _a + _b; }); | |
Function<int(int, int)> f6(&add); | |
std::cout << a << " + " << b << " = " << f6(a, b) << '\n'; | |
std::cout << "sizeof(f6) = " << sizeof(f6) << '\n'; | |
{ | |
Function<int(int, int), 128> f7([](int _a, int _b) { return _a + _b; }); | |
std::cout << a << " + " << b << " = " << f7(a, b) << '\n'; | |
std::cout << "sizeof(f7) = " << sizeof(f7) << '\n'; | |
f7 = _f6; | |
char d = 9; | |
std::shared_ptr<int> shared = std::make_shared<int>(); | |
std::unique_ptr<int> unique = std::make_unique<int>(); | |
{ | |
Function<int(int, int), 128> f8([d, shared](int _a, int _b) { return _a + _b + d; }); | |
MoveFunction<int(int, int), 128> f9([unique{ std::move(unique) }](int _a, int _b) { return _a + _b + *unique; }); | |
std::cout << a << " + " << b << " + " << d << " = " << f8(a, b) << '\n'; | |
std::cout << "sizeof(f8) = " << sizeof(f8) << '\n'; | |
f7 = f6; | |
Function<int(int, int), 129> f10; | |
Function<int(int, int), 127> f11; | |
f7 = f11; | |
f7 = f10; | |
f9 = std::move(f6); | |
f7 = f8; | |
} | |
std::cout << a << " + " << b << " + " << d << " = " << f7(a, b) << '\n'; | |
} | |
sum(a, b, [a, b](int result) { | |
std::cout << a << " + " << b << " = " << result << '\n'; | |
}); | |
sum(a, b, nullptr); | |
} | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment