#pragma once #include "gp/exception.hpp" #include "gp/algorithm/tmp_manip.hpp" #include "gp/algorithm/move.hpp" namespace gp{ template class function; template class function{ 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 class callable final : public virtual_callable{ typename gp::remove_reference::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 function& operator=(T& t) { if(state & ACTIVE) { if(state & SOO) { ((virtual_callable*)self.inplace)->~virtual_callable(); } else { delete self.functor; } } if(t.state | ACTIVE) { if constexpr (sizeof(callable) <= sizeof(self)) { 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); } return; } state = INACTIVE; } function() { state = INACTIVE; } template function<>(T& t) { if constexpr (!std::is_same_v) { if constexpr (sizeof(callable) <= sizeof(self)) { new((void*)self.inplace) callable(t); state = (state_t)(ACTIVE | SOO); } else { self.functor = new callable(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 function(T&& t) { if constexpr (!std::is_same_v) { if constexpr (sizeof(callable) <= sizeof(self)) { new((void*)self.inplace) callable(gp::move(t)); state = (state_t)(ACTIVE | SOO); } else { self.functor = new callable(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; } } } }; }