General Purpose library for Freestanding C++ and POSIX systems
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

417 lines
9.4 KiB

#pragma once
#include "gp/exception.hpp"
#include "gp/functional/bind_front.hpp"
#include "gp/algorithms/move.hpp"
#include "gp/algorithms/tmp_manip.hpp"
#include "gp/utils/allocators/allocator.hpp"
#include "gp/functional/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));
}
/*function(ret f_ptr(args...))
: function(f_ptr, nullopt)
{}*/
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) {
swap(alloc, rhs.alloc);
swap(invokator, rhs.invokator);
swap(condestructor, rhs.condestructor);
swap(data_ptr,rhs.data_ptr);
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;
}
}
}
};*/
}