#pragma once
|
|
#include "gp/exception.hpp"
|
|
#include "gp/algorithm/tmp_manip.hpp"
|
|
#include "gp/algorithm/move.hpp"
|
|
|
|
namespace gp{
|
|
|
|
template <typename>
|
|
class function;
|
|
|
|
template <typename ret, typename ...args>
|
|
class function<ret(args...)>{
|
|
struct virtual_callable
|
|
{
|
|
virtual void inplace_move(char*) = 0;
|
|
virtual virtual_callable* all_copy() = 0;
|
|
virtual void inplace_copy(char*) = 0;
|
|
virtual virtual_callable* all_move() = 0;
|
|
virtual ~virtual_callable() = default;
|
|
virtual ret operator() (args...) = 0;
|
|
};
|
|
|
|
template<typename fn>
|
|
class callable final : public virtual_callable{
|
|
typename gp::remove_reference<fn>::type internal_representation;
|
|
public:
|
|
callable(const fn func)
|
|
: internal_representation{gp::move(func)}
|
|
{}
|
|
|
|
callable(callable&) = default;
|
|
callable(callable&&) = default;
|
|
|
|
virtual ~callable() override = default;
|
|
|
|
virtual void inplace_copy(char* ptr) override {
|
|
new(ptr) callable(*this);
|
|
}
|
|
|
|
virtual virtual_callable* all_copy() override {
|
|
return new callable(*this);
|
|
}
|
|
|
|
virtual void inplace_move(char* ptr) override {
|
|
new(ptr) callable(gp::move(*this));
|
|
}
|
|
|
|
virtual virtual_callable* all_move() override {
|
|
return new callable(gp::move(*this));
|
|
}
|
|
|
|
ret operator() (args... arg_list) override
|
|
{
|
|
return internal_representation(arg_list...);
|
|
}
|
|
};
|
|
|
|
// tweak a way to store a size in there for trivial copy
|
|
enum state_t : uint8_t{
|
|
INACTIVE = 0,
|
|
ACTIVE = 1,
|
|
NO_SOO = 0,
|
|
SOO = 2
|
|
};
|
|
|
|
state_t state{};
|
|
union{
|
|
virtual_callable* functor = nullptr;
|
|
char inplace[12];
|
|
} self;
|
|
|
|
public:
|
|
template <typename T>
|
|
function& operator=(T& t)
|
|
{
|
|
if(state & ACTIVE)
|
|
{
|
|
if(state & SOO)
|
|
{
|
|
((virtual_callable*)self.inplace)->~virtual_callable();
|
|
}
|
|
else
|
|
{
|
|
delete self.functor;
|
|
}
|
|
}
|
|
if(!(t.state & ACTIVE))
|
|
{
|
|
state = INACTIVE;
|
|
return;
|
|
}
|
|
if constexpr (!std::is_same_v<T, function<ret(args...)>>) {
|
|
if constexpr (sizeof(callable<T>) <= sizeof(self))
|
|
{
|
|
new((void*)self.inplace) callable<T>(t);
|
|
state = (state_t)(ACTIVE | SOO);
|
|
}
|
|
else
|
|
{
|
|
self.functor = new callable<T>(t);
|
|
state = (state_t)(ACTIVE | NO_SOO);
|
|
}
|
|
} else {
|
|
if(t.state & SOO)
|
|
{
|
|
auto& ref = t.self.functor;
|
|
ref->inplace_copy((char*)&self);
|
|
state = (state_t)(ACTIVE | SOO);
|
|
}
|
|
else
|
|
{
|
|
self.functor = t.self.functor->all_copy();
|
|
state = (state_t)(ACTIVE | NO_SOO);
|
|
}
|
|
}
|
|
}
|
|
|
|
function()
|
|
{
|
|
state = INACTIVE;
|
|
}
|
|
|
|
template<typename T>
|
|
function<>(T& t)
|
|
{
|
|
if constexpr (!std::is_same_v<T, function<ret(args...)>>) {
|
|
if constexpr (sizeof(callable<T>) <= sizeof(self))
|
|
{
|
|
new((void*)self.inplace) callable<T>(t);
|
|
state = (state_t)(ACTIVE | SOO);
|
|
}
|
|
else
|
|
{
|
|
self.functor = new callable<T>(t);
|
|
state = (state_t)(ACTIVE | NO_SOO);
|
|
}
|
|
} else {
|
|
if(t.state & SOO)
|
|
{
|
|
t.self.functor->inplace_copy(self.inplace);
|
|
state = (state_t)(ACTIVE | SOO);
|
|
}
|
|
else
|
|
{
|
|
self.functor = t.self.functor->all_copy();
|
|
state = (state_t)(ACTIVE | NO_SOO);
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
function(T&& t)
|
|
{
|
|
if constexpr (!std::is_same_v<T, function<ret(args...)>>) {
|
|
if constexpr (sizeof(callable<T>) <= sizeof(self))
|
|
{
|
|
new((void*)self.inplace) callable<T>(gp::move(t));
|
|
state = (state_t)(ACTIVE | SOO);
|
|
}
|
|
else
|
|
{
|
|
self.functor = new callable<T>(gp::move(t));
|
|
state = (state_t)(ACTIVE | NO_SOO);
|
|
}
|
|
} else {
|
|
if(t.state & SOO)
|
|
{
|
|
auto& ref = t.self.functor;
|
|
ref->inplace_move((char*)&self);
|
|
state = (state_t)(ACTIVE | SOO);
|
|
}
|
|
else
|
|
{
|
|
self.functor = t.self.functor->all_move();
|
|
state = (state_t)(ACTIVE | NO_SOO);
|
|
}
|
|
}
|
|
}
|
|
|
|
ret operator()(args... arg_list) const {
|
|
if constexpr (gp_config::has_exceptions)
|
|
{
|
|
if(!(state & ACTIVE))
|
|
{
|
|
throw bad_functor{};
|
|
}
|
|
}
|
|
if(state & SOO)
|
|
{
|
|
return (*(virtual_callable*)&self)(arg_list...);
|
|
}
|
|
else
|
|
{
|
|
return (*self.functor)(arg_list...);
|
|
}
|
|
|
|
}
|
|
|
|
~function()
|
|
{
|
|
if(state & ACTIVE)
|
|
{
|
|
if(state & SOO)
|
|
{
|
|
((virtual_callable*)&self)->~virtual_callable();
|
|
}
|
|
else
|
|
{
|
|
delete self.functor;
|
|
}
|
|
}
|
|
|
|
}
|
|
};
|
|
|
|
}
|