Skip to content

Instantly share code, notes, and snippets.

@kerrytazi
Created April 14, 2019 01:47
Show Gist options
  • Save kerrytazi/e6ddaeaddd53ff3ab1753a6733490143 to your computer and use it in GitHub Desktop.
Save kerrytazi/e6ddaeaddd53ff3ab1753a6733490143 to your computer and use it in GitHub Desktop.
Template copy/move and move-only functors with configurable size.
#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