|
|
- #pragma once
-
- #include "gp/exception.hpp"
- #include "gp/algorithm/modifiers.hpp"
- #include "gp/algorithm/move.hpp"
- #include "gp/algorithm/tmp_manip.hpp"
- #include "gp/allocator/allocator.hpp"
- #include "gp/optional.hpp"
-
- namespace gp{
-
- namespace details {
- template<typename pred, typename returner>
- auto ensure(pred a, returner b) {
- gp_config::assertion(a(), "could not ensure predicate");
- return b();
- }
- }
-
- template <typename fn>
- class function;
-
- template <typename ret, typename ...args>
- class function<ret(args...)>{
- using fn_ptr = char*;
-
- using invoke_fn_t = ret (*)(fn_ptr, args&&...);
- using condestruct_fn_t = void (*) (fn_ptr, fn_ptr);
-
- gp::optional<gp::reference_wrapper<allocator>> 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:
- function(allocator& alloc_v)
- : alloc(alloc_v)
- , invokator(nullptr)
- , condestructor(nullptr)
- , data_ptr(nullptr)
- , data_size(0)
- {}
-
- template<typename func>
- function(func f, allocator& alloc_v)
- : alloc(alloc_v)
- , invokator(reinterpret_cast<invoke_fn_t>(invoke<func>))
- , condestructor(reinterpret_cast<condestruct_fn_t>(condestruct<func>))
- , data_ptr(
- sizeof(func) <= sizeof(data_ptr)
- ? nullptr
- : details::ensure(
- [&](){return alloc.has_value();},
- [&](){return (char*)(alloc.value().get().allocate(sizeof(func)));}
- )
- )
- , data_size(
- sizeof(func)
- )
- {
- gp_config::assertion(data_size <= sizeof(func) || !(alloc.has_value() && data_ptr == nullptr), "allocator failed in function");
- this->condestructor(data_size <= sizeof(data_ptr) ? (char*)&data_ptr : data_ptr, reinterpret_cast<char*>(&f));
- }
-
- template<typename func>
- function(func f, nullopt_t alloc_v)
- : alloc(alloc_v)
- , invokator(reinterpret_cast<invoke_fn_t>(invoke<func>))
- , condestructor(reinterpret_cast<condestruct_fn_t>(condestruct<func>))
- , data_ptr()
- , data_size(
- details::ensure(
- [&](){return sizeof(func) <= sizeof(data_ptr);},
- [&](){return sizeof(func);}
- )
- )
- {
- this->condestructor((char*)&data_ptr, reinterpret_cast<char*>(&f));
- }
-
- function(function const& rhs)
- : alloc(rhs.alloc)
- , invokator(rhs.invokator)
- , condestructor(rhs.condestructor)
- , data_ptr(
- rhs.data_size <= sizeof(data_ptr)
- ? 0
- : details::ensure(
- [&](){return alloc.has_value();},
- [&](){return (char*)(alloc.value().get().allocate(rhs.data_size));}
- )
- )
- , data_size(rhs.data_size)
- {
- gp_config::assertion(data_size <= sizeof(data_ptr) || !(alloc.has_value() && data_ptr == nullptr), "allocator failed in function");
- if(
- data_size != 0
- and rhs.data_size != 0
- ) this->condestructor(
- data_size <= sizeof(data_ptr) ? (char*)&data_ptr : data_ptr,
- data_size <= sizeof(data_ptr) ? (char*)&(rhs.data_ptr) : rhs.data_ptr
- );
- }
-
- function(function&& rhs)
- : alloc(rhs.alloc)
- , invokator(rhs.invokator)
- , condestructor(rhs.condestructor)
- , data_ptr(rhs.data_size <= sizeof(data_ptr) ? nullptr : rhs.data_ptr)
- , data_size(rhs.data_size)
- {
- if(data_size != 0 && data_size <= sizeof(data_ptr)) {
- condestructor((char*)&data_ptr, (char*)&rhs.data_ptr);
- condestructor(nullptr, (char*)&rhs.data_ptr);
- }
- rhs.data_ptr = nullptr;
- rhs.data_size = 0;
- }
-
- ~function(){
- if(data_size <= 0) {
- return;
- }
- if(data_ptr != nullptr && alloc.has_value()) {
- condestructor(nullptr, data_ptr);
- alloc.value().get().deallocate(data_ptr);
- data_ptr = nullptr;
- }
- if(data_size < sizeof(data_ptr))
- {
- condestructor(nullptr, (char*)&data_ptr);
- }
- }
-
- 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;
- }
-
- function& operator=(function& rhs) {
- alloc = rhs.alloc;
- invokator = rhs.invokator;
- condestructor = rhs.condestructor;
-
- /* Cleanup */
-
- if(data_size > sizeof(data_ptr)) {
- condestructor(nullptr, data_ptr);
- alloc.value().get().deallocate(data_ptr);
- } else if(data_size) {
- condestructor(nullptr, (char*)&data_ptr);
- }
-
- /* Reallocation */
-
- data_size = rhs.data_size;
- data_ptr = data_size <= sizeof(data_ptr)
- ? 0
- : details::ensure(
- [&](){return alloc.has_value();},
- [&](){return (char*)(alloc.value().get().allocate(rhs.data_size));}
- );
-
- gp_config::assertion(!(alloc.has_value() && data_size > 8 && data_ptr != nullptr), "allocator failed in function");
-
- if(
- data_size > 0
- ) this->condestructor(
- data_size <= sizeof(data_ptr) ? (char*)&data_ptr : data_ptr,
- data_size <= sizeof(data_ptr) ? (char*)&rhs.data_ptr : rhs.data_ptr
- );
- return *this;
- }
-
- ret operator()(args&&... argv) {
- return invokator(data_size <= sizeof(data_ptr) ? (fn_ptr)&data_ptr : data_ptr, gp::forward<args>(argv)...);
- }
-
- bool ready() {
- return invokator != nullptr;
- }
- };
-
- /*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;
- }
- }
-
- }
- };*/
-
- }
|