#pragma once #include "gp/exception.hpp" #include "gp/algorithm/modifiers.hpp" #include "gp/algorithm/move.hpp" #include "gp/algorithm/tmp_manip.hpp" namespace gp{ template class function; template class function{ using fn_ptr = char*; using invoke_fn_t = ret (*)(fn_ptr, args&&...); using condestruct_fn_t = void (*) (fn_ptr, fn_ptr); using allocator_t = typename gp::either< copy_allocator, allocator, gp::reference_wrapper >::type; allocator_t alloc; invoke_fn_t invokator; condestruct_fn_t condestructor; fn_ptr data_ptr; size_t data_size; template static ret invoke(func* fn, args&&... fw_args) { return (*fn)(gp::forward(fw_args)...); } template static void condestruct(func* dest, func* src) { if(dest != nullptr) { new (dest) func(*src); } else { src->~func(); } } static void nop_condestruct(char*, char*) { return; } public: #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wnull-dereference" #pragma gcc diagnostic push #pragma gcc diagnostic ignored "-Wnull-dereference" function(allocator_t alloc_v = gp::reference_wrapper{*reinterpret_cast(0)}) #pragma gcc pop #pragma clang pop : alloc(alloc_v) , invokator(nullptr) , condestructor(nullptr) , data_ptr(nullptr) , data_size(0) {} template function(func f, allocator_t alloc_v) : alloc(alloc_v) , invokator(reinterpret_cast(invoke)) , condestructor(reinterpret_cast(condestruct)) , data_ptr((char*)((allocator&)alloc).allocate(sizeof(func))) , data_size(sizeof(func)) { gp_config::assertion(data_ptr != nullptr, "allocator failed in function"); if(data_ptr != nullptr) this->condestructor(data_ptr, reinterpret_cast(&f)); } function(function const& rhs) : alloc(rhs.alloc) , invokator(rhs.invokator) , condestructor(rhs.condestructor) , data_ptr(rhs.data_size != 0 ? (char*)((allocator&)alloc).allocate(rhs.data_size) : nullptr) , data_size(rhs.data_size) { gp_config::assertion(data_ptr != nullptr, "allocator failed in function"); if( data_ptr != nullptr and rhs.data_ptr != nullptr ) this->condestructor(data_ptr, rhs.data_ptr); } function(function&& rhs) : alloc(rhs.alloc) , invokator(rhs.invokator) , condestructor(rhs.condestructor) , data_ptr(rhs.data_ptr) , data_size(rhs.data_size) { rhs.data_ptr = nullptr; } constexpr function(ret(*fn)(args...), allocator_t alloc_v = allocator_t{}) : alloc(alloc_v) , invokator(reinterpret_cast(invoke)) , condestructor(nop_condestruct) , data_ptr(reinterpret_cast(fn)) , data_size(0) {} ~function(){ if(data_size == 0) { return; } if(data_ptr != nullptr) { condestructor(nullptr, data_ptr); ((allocator&)alloc).deallocate(data_ptr); data_ptr = nullptr; } } function& operator=(function&& rhs) { gp::swap(alloc, rhs.alloc); gp::swap(invokator, rhs.invokator); gp::swap(condestructor, rhs.condestructor); gp::swap(data_ptr,rhs.data_ptr); gp::swap(data_size,rhs.data_size); return *this; } ret operator()(args&&... argv) { return invokator(data_ptr, gp::forward(argv)...); } }; /*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[16]; } 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)) { state = INACTIVE; return; } 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) { 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 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; } } } };*/ }