|
|
- #pragma once
- #include "gp/exception.hpp"
- #include "gp/algorithm/tmp_manip.hpp"
- #include "gp/algorithm/move.hpp"
- #include "gp/algorithm/modifiers.hpp"
-
- namespace gp{
- template <typename fn, typename allocator, bool copy_allocator = false>
- class function;
-
- template <typename ret, typename allocator, bool copy_allocator, typename ...args>
- class function<ret(args...), allocator, copy_allocator>{
- 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<allocator>
- >::type;
-
- allocator_t alloc;
- invoke_fn_t invokator;
- condestruct_fn_t condestructor;
- fn_ptr data_ptr;
- size_t data_size;
-
-
- template<typename func>
- static ret invoke(func* fn, args&&... fw_args) {
- return (*fn)(gp::forward<args>(fw_args)...);
- }
-
- template<typename func>
- 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<allocator_t>{*reinterpret_cast<allocator_t*>(0)})
- #pragma gcc pop
- #pragma clang pop
- : alloc(alloc_v)
- , invokator(nullptr)
- , condestructor(nullptr)
- , data_ptr(nullptr)
- , data_size(0)
- {}
-
- template<typename func>
- function(func f, allocator_t alloc_v)
- : alloc(alloc_v)
- , invokator(reinterpret_cast<invoke_fn_t>(invoke<func>))
- , condestructor(reinterpret_cast<condestruct_fn_t>(condestruct<func>))
- , 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<char*>(&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_fn_t>(invoke<ret(args...)>))
- , condestructor(nop_condestruct)
- , data_ptr(reinterpret_cast<char*>(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<args>(argv)...);
- }
- };
-
- /*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[16];
- } 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;
- }
- }
-
- }
- };*/
-
- }
|