|
|
@ -5,8 +5,18 @@ |
|
|
|
#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; |
|
|
|
|
|
|
@ -17,7 +27,7 @@ namespace gp{ |
|
|
|
using invoke_fn_t = ret (*)(fn_ptr, args&&...); |
|
|
|
using condestruct_fn_t = void (*) (fn_ptr, fn_ptr); |
|
|
|
|
|
|
|
gp::reference_wrapper<allocator> alloc; |
|
|
|
gp::optional<gp::reference_wrapper<allocator>> alloc; |
|
|
|
invoke_fn_t invokator; |
|
|
|
condestruct_fn_t condestructor; |
|
|
|
fn_ptr data_ptr; |
|
|
@ -45,10 +55,10 @@ namespace gp{ |
|
|
|
public: |
|
|
|
#pragma clang diagnostic push
|
|
|
|
#pragma clang diagnostic ignored "-Wnull-dereference"
|
|
|
|
#pragma gcc diagnostic push
|
|
|
|
#pragma gcc diagnostic ignored "-Wnull-dereference"
|
|
|
|
#pragma GCC diagnostic push
|
|
|
|
#pragma GCC diagnostic ignored "-Wnull-dereference"
|
|
|
|
function(allocator& alloc_v) |
|
|
|
#pragma gcc pop
|
|
|
|
#pragma GCC diagnostic pop
|
|
|
|
#pragma clang diagnostic pop
|
|
|
|
: alloc(alloc_v) |
|
|
|
, invokator(nullptr) |
|
|
@ -62,54 +72,98 @@ namespace gp{ |
|
|
|
: alloc(alloc_v) |
|
|
|
, invokator(reinterpret_cast<invoke_fn_t>(invoke<func>)) |
|
|
|
, condestructor(reinterpret_cast<condestruct_fn_t>(condestruct<func>)) |
|
|
|
, data_ptr((char*)(alloc.get().allocate(sizeof(func)))) |
|
|
|
, data_size(sizeof(func)) |
|
|
|
, data_ptr( |
|
|
|
sizeof(func) <= sizeof(data_ptr) |
|
|
|
? 0 |
|
|
|
: details::ensure( |
|
|
|
[&](){return alloc.has_value();}, |
|
|
|
[&](){return (char*)(alloc.value().get().allocate(sizeof(func)));} |
|
|
|
) |
|
|
|
) |
|
|
|
, data_size( |
|
|
|
sizeof(func) <= sizeof(data_ptr) |
|
|
|
? 0 |
|
|
|
: sizeof(func) |
|
|
|
) |
|
|
|
{ |
|
|
|
gp_config::assertion(data_ptr != nullptr, "allocator failed in function"); |
|
|
|
if(data_ptr != nullptr) this->condestructor(data_ptr, reinterpret_cast<char*>(&f)); |
|
|
|
gp_config::assertion(!(alloc.has_value() && data_ptr == nullptr), "allocator failed in function"); |
|
|
|
if(data_ptr != nullptr) 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 = nullopt) |
|
|
|
: 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) |
|
|
|
? 0 |
|
|
|
: details::ensure( |
|
|
|
[&](){return alloc.has_value();}, |
|
|
|
[&](){return (char*)(alloc.value().get().allocate(sizeof(func)));} |
|
|
|
) |
|
|
|
) |
|
|
|
, data_size( |
|
|
|
sizeof(func) <= sizeof(data_ptr) |
|
|
|
? 0 |
|
|
|
: sizeof(func) |
|
|
|
) |
|
|
|
{ |
|
|
|
gp_config::assertion(!(alloc.has_value() && data_ptr == nullptr), "allocator failed in function"); |
|
|
|
if(data_ptr != nullptr) this->condestructor(data_size < sizeof(data_ptr) ? (char*)&data_ptr : 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*)(alloc.get().allocate(rhs.data_size)) : nullptr) |
|
|
|
, 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_ptr != nullptr, "allocator failed in function"); |
|
|
|
gp_config::assertion(o">!(alloc.has_value() && data_ptr == nullptr), "allocator failed in function"); |
|
|
|
if( |
|
|
|
data_ptr != nullptr |
|
|
|
and rhs.data_ptr != nullptr |
|
|
|
) this->condestructor(data_ptr, rhs.data_ptr); |
|
|
|
) this->condestructor(data_size <= sizeof(data_ptr) ? (char*)&data_ptr : data_ptr, rhs.data_ptr); |
|
|
|
} |
|
|
|
|
|
|
|
function(function&& rhs) |
|
|
|
: alloc(rhs.alloc) |
|
|
|
, invokator(rhs.invokator) |
|
|
|
, condestructor(rhs.condestructor) |
|
|
|
, data_ptr(rhs.data_ptr) |
|
|
|
, data_ptr(data_size <= sizeof(data_ptr) ? 0 : rhs.data_ptr) |
|
|
|
, data_size(rhs.data_size) |
|
|
|
{ |
|
|
|
if(data_size <= sizeof(data_ptr) && data_size != 0 && data_ptr != nullptr && alloc.has_value()) { |
|
|
|
condestructor((char*)&data_ptr, (char*)&rhs.data_ptr); |
|
|
|
condestructor(nullptr, (char*)&rhs.data_ptr); |
|
|
|
} |
|
|
|
rhs.data_ptr = nullptr; |
|
|
|
} |
|
|
|
|
|
|
|
constexpr function(ret(*fn)(args...), allocator alloc_v) |
|
|
|
: 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) { |
|
|
|
if(data_size <= 0) { |
|
|
|
return; |
|
|
|
} |
|
|
|
if(data_ptr != nullptr && alloc.has_value()) { |
|
|
|
condestructor(nullptr, data_ptr); |
|
|
|
alloc.get().deallocate(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) { |
|
|
@ -121,6 +175,28 @@ namespace gp{ |
|
|
|
return *this; |
|
|
|
} |
|
|
|
|
|
|
|
function& operator=(function& rhs) { |
|
|
|
alloc = rhs.alloc; |
|
|
|
invokator = rhs.invokator; |
|
|
|
condestructor = rhs.condestructor; |
|
|
|
condestructor(nullptr, data_ptr); |
|
|
|
alloc.value().get().deallocate(data_ptr); |
|
|
|
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(!(alloc.has_value() && data_ptr == nullptr), "allocator failed in function"); |
|
|
|
if( |
|
|
|
data_ptr != nullptr |
|
|
|
and rhs.data_ptr != nullptr |
|
|
|
) this->condestructor(data_size <= sizeof(data_ptr) ? (char*)&data_ptr : data_ptr, rhs.data_ptr); |
|
|
|
return *this; |
|
|
|
} |
|
|
|
|
|
|
|
ret operator()(args&&... argv) { |
|
|
|
return invokator(data_ptr, gp::forward<args>(argv)...); |
|
|
|
} |
|
|
|