From 71515c53837e553f91d12634d8da198482e4d044 Mon Sep 17 00:00:00 2001 From: q66 Date: Sat, 28 Jan 2017 22:57:36 +0100 Subject: [PATCH] actually keep our own function because libstdc++'s has no allocator support --- ostd/event.hh | 11 +- ostd/functional.hh | 426 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 431 insertions(+), 6 deletions(-) diff --git a/ostd/event.hh b/ostd/event.hh index 336d004..7b96958 100644 --- a/ostd/event.hh +++ b/ostd/event.hh @@ -19,7 +19,7 @@ namespace detail { SignalBase(SignalBase const &ev): p_class(ev.p_class), p_nfuncs(ev.p_nfuncs) { - using Func = std::function; + using Func = Function; byte *bufp = new byte[sizeof(Func) * p_nfuncs]; Func *nbuf = reinterpret_cast(bufp); for (Size i = 0; i < p_nfuncs; ++i) @@ -34,7 +34,7 @@ namespace detail { } SignalBase &operator=(SignalBase const &ev) { - using Func = std::function; + using Func = Function; p_class = ev.p_class; p_nfuncs = ev.p_nfuncs; byte *bufp = new byte[sizeof(Func) * p_nfuncs]; @@ -54,9 +54,8 @@ namespace detail { ~SignalBase() { clear(); } void clear() { - using func = std::function; for (Size i = 0; i < p_nfuncs; ++i) { - p_funcs[i].~func(); + p_funcs[i].~Function(); } delete[] reinterpret_cast(p_funcs); p_funcs = nullptr; @@ -65,7 +64,7 @@ namespace detail { template Size connect(F &&func) { - using Func = std::function; + using Func = Function; for (Size i = 0; i < p_nfuncs; ++i) { if (!p_funcs[i]) { p_funcs[i] = std::forward(func); @@ -122,7 +121,7 @@ namespace detail { private: C *p_class; - std::function *p_funcs; + Function *p_funcs; Size p_nfuncs; }; } /* namespace detail */ diff --git a/ostd/functional.hh b/ostd/functional.hh index 79641c3..c75f106 100644 --- a/ostd/functional.hh +++ b/ostd/functional.hh @@ -1,6 +1,7 @@ /* Function objects for OctaSTD. * * This file is part of OctaSTD. See COPYING.md for futher information. + * Portions of this file are originally adapted from the libc++ project. */ #ifndef OSTD_FUNCTIONAL_HH @@ -439,6 +440,431 @@ typename ToHash::Result to_hash(T const &v) { return ToHash()(v); } +/* function impl adapted from libc++ + */ + +template +class Function; + +namespace detail { + template + inline bool func_is_not_null(T const &) { + return true; + } + + template + inline bool func_is_not_null(T *ptr) { + return ptr; + } + + template + inline bool func_is_not_null(R C::*ptr) { + return ptr; + } + + template + inline bool func_is_not_null(Function const &f) { + return !!f; + } + + template + struct FuncInvokeVoidReturnWrapper { + template + static R call(A &&...args) { + return func_invoke(std::forward(args)...); + } + }; + + template<> + struct FuncInvokeVoidReturnWrapper { + template + static void call(A &&...args) { + func_invoke(std::forward(args)...); + } + }; + + template + class FuncBase; + + template + class FuncBase { + FuncBase(FuncBase const &); + FuncBase &operator=(FuncBase const &); + public: + FuncBase() {} + virtual ~FuncBase() {} + virtual FuncBase *clone() const = 0; + virtual void clone(FuncBase *) const = 0; + virtual void destroy() noexcept = 0; + virtual void destroy_deallocate() noexcept = 0; + virtual R operator()(A &&...args) = 0; + }; + + template + class FuncCore; + + template + class FuncCore: public FuncBase { + CompressedPair f_stor; +public: + explicit FuncCore(F &&f): + f_stor( + std::piecewise_construct, + std::forward_as_tuple(std::move(f)), + std::forward_as_tuple() + ) + {} + + explicit FuncCore(F const &f, A const &a): + f_stor( + std::piecewise_construct, + std::forward_as_tuple(f), + std::forward_as_tuple(a) + ) + {} + + explicit FuncCore(F const &f, A &&a): + f_stor( + std::piecewise_construct, + std::forward_as_tuple(f), + std::forward_as_tuple(std::move(a)) + ) + {} + + explicit FuncCore(F &&f, A &&a): + f_stor( + std::piecewise_construct, + std::forward_as_tuple(std::move(f)), + std::forward_as_tuple(std::move(a)) + ) + {} + + virtual FuncBase *clone() const; + virtual void clone(FuncBase *) const; + virtual void destroy() noexcept; + virtual void destroy_deallocate() noexcept; + virtual R operator()(AT &&...args); + }; + + template + FuncBase *FuncCore::clone() const { + using AA = AllocatorRebind; + AA a(f_stor.second()); + using D = AllocatorDestructor; + Box hold(a.allocate(1), D(a, 1)); + ::new(hold.get()) FuncCore(f_stor.first(), A(a)); + return hold.release(); + } + + template + void FuncCore::clone(FuncBase *p) const { + ::new (p) FuncCore(f_stor.first(), f_stor.second()); + } + + template + void FuncCore::destroy() noexcept { + f_stor.~CompressedPair(); + } + + template + void FuncCore::destroy_deallocate() noexcept { + using AA = AllocatorRebind; + AA a(f_stor.second()); + f_stor.~CompressedPair(); + a.deallocate(this, 1); + } + + template + R FuncCore::operator()(AT &&...args) { + using Invoker = FuncInvokeVoidReturnWrapper; + return Invoker::call(f_stor.first(), std::forward(args)...); + } +} /* namespace detail */ + +template +class Function { + using Base = detail::FuncBase; + AlignedStorage<3 * sizeof(void *)> p_buf; + Base *p_f; + + static inline Base *as_base(void *p) { + return reinterpret_cast(p); + } + + template< + typename F, + bool = !IsSame && detail::IsInvokable + > + struct CallableBase; + + template + struct CallableBase { + static constexpr bool value = + IsSame || IsConvertible, R>; + }; + template + struct CallableBase { + static constexpr bool value = false; + }; + + template + static constexpr bool Callable = CallableBase::value; + +public: + using Result = R; + + Function() noexcept: p_f(nullptr) {} + Function(Nullptr) noexcept: p_f(nullptr) {} + + Function(Function const &); + Function(Function &&) noexcept; + + template< + typename F, typename = EnableIf && !IsSame> + > + Function(F); + + template + Function(AllocatorArg, A const &) noexcept: p_f(nullptr) {} + template + Function(AllocatorArg, A const &, Nullptr) noexcept: p_f(nullptr) {} + template + Function(AllocatorArg, A const &, Function const &); + template + Function(AllocatorArg, A const &, Function &&); + template>> + Function(AllocatorArg, A const &, F); + + Function &operator=(Function const &f) { + Function(f).swap(*this); + return *this; + } + + Function &operator=(Function &&) noexcept; + Function &operator=(Nullptr) noexcept; + + template + EnableIf< + Callable> && !IsSame, Function>, + Function & + > operator=(F &&f) { + Function(std::forward(f)).swap(*this); + return *this; + } + + ~Function(); + + void swap(Function &) noexcept; + + explicit operator bool() const noexcept { return p_f; } + + /* deleted overloads close possible hole in the type system */ + template + bool operator==(Function &) const = delete; + template + bool operator!=(Function &) const = delete; + + R operator()(Args ...a) const { + return (*p_f)(std::forward(a)...); + } +}; + +template +Function::Function(Function const &f) { + if (!f.p_f) { + p_f = nullptr; + } else if (static_cast(f.p_f) == &f.p_buf) { + p_f = as_base(&p_buf); + f.p_f->clone(p_f); + } else { + p_f = f.p_f->clone(); + } +} + +template +template +Function::Function( + AllocatorArg, A const &, Function const &f +) { + if (!f.p_f) { + p_f = nullptr; + } else if (static_cast(f.p_f) == &f.p_buf) { + p_f = as_base(&p_buf); + f.p_f->clone(p_f); + } else { + p_f = f.p_f->clone(); + } +} + +template +Function::Function(Function &&f) noexcept { + if (!f.p_f) { + p_f = nullptr; + } else if (static_cast(f.p_f) == &f.p_buf) { + p_f = as_base(&p_buf); + f.p_f->clone(p_f); + } else { + p_f = f.p_f; + f.p_f = nullptr; + } +} + +template +template +Function::Function( + AllocatorArg, A const &, Function &&f +) { + if (!f.p_f) { + p_f = nullptr; + } else if (static_cast(f.p_f) == &f.p_buf) { + p_f = as_base(&p_buf); + f.p_f->clone(p_f); + } else { + p_f = f.p_f; + f.p_f = nullptr; + } +} + +template +template +Function::Function(F f): p_f(nullptr) { + if (!detail::func_is_not_null(f)) { + return; + } + using FF = detail::FuncCore, R(Args...)>; + if ((sizeof(FF) <= sizeof(p_buf)) && IsNothrowCopyConstructible) { + p_f = ::new(static_cast(&p_buf)) FF(std::move(f)); + return; + } + using AA = Allocator; + AA a; + using D = detail::AllocatorDestructor; + Box hold(a.allocate(1), D(a, 1)); + ::new(hold.get()) FF(std::move(f), Allocator(a)); + p_f = hold.release(); +} + +template +template +Function::Function(AllocatorArg, A const &a, F f): + p_f(nullptr) +{ + if (!detail::func_is_not_null(f)) { + return; + } + using FF = detail::FuncCore; + using AA = AllocatorRebind; + AA aa(a); + if ( + (sizeof(FF) <= sizeof(p_buf)) && IsNothrowCopyConstructible && + IsNothrowCopyConstructible + ) { + p_f = ::new(static_cast(&p_buf)) FF(std::move(f), A(aa)); + return; + } + using D = detail::AllocatorDestructor; + Box hold(aa.allocate(1), D(aa, 1)); + ::new(hold.get()) FF(std::move(f), A(aa)); + p_f = hold.release(); +} + +template +Function &Function::operator=(Function &&f) + noexcept +{ + if (static_cast(p_f) == &p_buf) { + p_f->destroy(); + } else if (p_f) { + p_f->destroy_deallocate(); + } + if (f.p_f == nullptr) { + p_f = nullptr; + } else if (static_cast(f.p_f) == &f.p_buf) { + p_f = as_base(&p_buf); + f.p_f->clone(p_f); + } else { + p_f = f.p_f; + f.p_f = nullptr; + } + return *this; +} + +template +Function &Function::operator=(Nullptr) noexcept { + if (static_cast(p_f) == &p_buf) { + p_f->destroy(); + } else if (p_f) { + p_f->destroy_deallocate(); + } + p_f = nullptr; + return *this; +} + +template +Function::~Function() { + if (static_cast(p_f) == &p_buf) { + p_f->destroy(); + } else if (p_f) { + p_f->destroy_deallocate(); + } +} + +template +void Function::swap(Function &f) noexcept { + if ( + (static_cast(p_f) == &p_buf) && + (static_cast(f.p_f) == &f.p_buf) + ) { + /* both in small storage */ + AlignedStorage tmpbuf; + Base *t = as_base(&tmpbuf); + p_f->clone(t); + p_f->destroy(); + p_f = nullptr; + f.p_f->clone(as_base(&p_buf)); + f.p_f->destroy(); + f.p_f = nullptr; + p_f = as_base(&p_buf); + t->clone(as_base(&f.p_buf)); + t->destroy(); + f.p_f = as_base(&f.p_buf); + } else if (static_cast(p_f) == &p_buf) { + /* ours in small storage */ + p_f->clone(as_base(&f.p_buf)); + p_f->destroy(); + p_f = f.p_f; + f.p_f = as_base(&f.p_buf); + } else if (static_cast(f.p_f) == &f.p_buf) { + /* the other in small storage */ + f.p_f->clone(as_base(&p_buf)); + f.p_f->destroy(); + f.p_f = p_f; + p_f = as_base(&p_buf); + } else { + detail::swap_adl(p_f, f.p_f); + } +} + +template +inline bool operator==(Function const &f, Nullptr) noexcept { + return !f; +} + +template +inline bool operator==(Nullptr, Function const &f) noexcept { + return !f; +} + +template +inline bool operator!=(Function const &f, Nullptr) noexcept { + return bool(f); +} + +template +inline bool operator!=(Nullptr, Function const &f) noexcept { + return bool(f); +} + } /* namespace ostd */ #endif