forked from OctaForge/libostd
actually keep our own function because libstdc++'s has no allocator support
parent
780f7e5b21
commit
71515c5383
|
@ -19,7 +19,7 @@ namespace detail {
|
||||||
SignalBase(SignalBase const &ev):
|
SignalBase(SignalBase const &ev):
|
||||||
p_class(ev.p_class), p_nfuncs(ev.p_nfuncs)
|
p_class(ev.p_class), p_nfuncs(ev.p_nfuncs)
|
||||||
{
|
{
|
||||||
using Func = std::function<void(C &, A...)>;
|
using Func = Function<void(C &, A...)>;
|
||||||
byte *bufp = new byte[sizeof(Func) * p_nfuncs];
|
byte *bufp = new byte[sizeof(Func) * p_nfuncs];
|
||||||
Func *nbuf = reinterpret_cast<Func *>(bufp);
|
Func *nbuf = reinterpret_cast<Func *>(bufp);
|
||||||
for (Size i = 0; i < p_nfuncs; ++i)
|
for (Size i = 0; i < p_nfuncs; ++i)
|
||||||
|
@ -34,7 +34,7 @@ namespace detail {
|
||||||
}
|
}
|
||||||
|
|
||||||
SignalBase &operator=(SignalBase const &ev) {
|
SignalBase &operator=(SignalBase const &ev) {
|
||||||
using Func = std::function<void(C &, A...)>;
|
using Func = Function<void(C &, A...)>;
|
||||||
p_class = ev.p_class;
|
p_class = ev.p_class;
|
||||||
p_nfuncs = ev.p_nfuncs;
|
p_nfuncs = ev.p_nfuncs;
|
||||||
byte *bufp = new byte[sizeof(Func) * p_nfuncs];
|
byte *bufp = new byte[sizeof(Func) * p_nfuncs];
|
||||||
|
@ -54,9 +54,8 @@ namespace detail {
|
||||||
~SignalBase() { clear(); }
|
~SignalBase() { clear(); }
|
||||||
|
|
||||||
void clear() {
|
void clear() {
|
||||||
using func = std::function<void(C &, A...)>;
|
|
||||||
for (Size i = 0; i < p_nfuncs; ++i) {
|
for (Size i = 0; i < p_nfuncs; ++i) {
|
||||||
p_funcs[i].~func();
|
p_funcs[i].~Function<void(C &, A...)>();
|
||||||
}
|
}
|
||||||
delete[] reinterpret_cast<byte *>(p_funcs);
|
delete[] reinterpret_cast<byte *>(p_funcs);
|
||||||
p_funcs = nullptr;
|
p_funcs = nullptr;
|
||||||
|
@ -65,7 +64,7 @@ namespace detail {
|
||||||
|
|
||||||
template<typename F>
|
template<typename F>
|
||||||
Size connect(F &&func) {
|
Size connect(F &&func) {
|
||||||
using Func = std::function<void(C &, A...)>;
|
using Func = Function<void(C &, A...)>;
|
||||||
for (Size i = 0; i < p_nfuncs; ++i) {
|
for (Size i = 0; i < p_nfuncs; ++i) {
|
||||||
if (!p_funcs[i]) {
|
if (!p_funcs[i]) {
|
||||||
p_funcs[i] = std::forward<F>(func);
|
p_funcs[i] = std::forward<F>(func);
|
||||||
|
@ -122,7 +121,7 @@ namespace detail {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
C *p_class;
|
C *p_class;
|
||||||
std::function<void(C &, A...)> *p_funcs;
|
Function<void(C &, A...)> *p_funcs;
|
||||||
Size p_nfuncs;
|
Size p_nfuncs;
|
||||||
};
|
};
|
||||||
} /* namespace detail */
|
} /* namespace detail */
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/* Function objects for OctaSTD.
|
/* Function objects for OctaSTD.
|
||||||
*
|
*
|
||||||
* This file is part of OctaSTD. See COPYING.md for futher information.
|
* 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
|
#ifndef OSTD_FUNCTIONAL_HH
|
||||||
|
@ -439,6 +440,431 @@ typename ToHash<T>::Result to_hash(T const &v) {
|
||||||
return ToHash<T>()(v);
|
return ToHash<T>()(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* function impl adapted from libc++
|
||||||
|
*/
|
||||||
|
|
||||||
|
template<typename>
|
||||||
|
class Function;
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
template<typename T>
|
||||||
|
inline bool func_is_not_null(T const &) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline bool func_is_not_null(T *ptr) {
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename R, typename C>
|
||||||
|
inline bool func_is_not_null(R C::*ptr) {
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline bool func_is_not_null(Function<T> const &f) {
|
||||||
|
return !!f;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename R>
|
||||||
|
struct FuncInvokeVoidReturnWrapper {
|
||||||
|
template<typename ...A>
|
||||||
|
static R call(A &&...args) {
|
||||||
|
return func_invoke(std::forward<A>(args)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct FuncInvokeVoidReturnWrapper<void> {
|
||||||
|
template<typename ...A>
|
||||||
|
static void call(A &&...args) {
|
||||||
|
func_invoke(std::forward<A>(args)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class FuncBase;
|
||||||
|
|
||||||
|
template<typename R, typename ...A>
|
||||||
|
class FuncBase<R(A...)> {
|
||||||
|
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<typename F, typename A, typename AT>
|
||||||
|
class FuncCore;
|
||||||
|
|
||||||
|
template<typename F, typename A, typename R, typename ...AT>
|
||||||
|
class FuncCore<F, A, R(AT...)>: public FuncBase<R(AT...)> {
|
||||||
|
CompressedPair<F, A> 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<R(AT...)> *clone() const;
|
||||||
|
virtual void clone(FuncBase<R(AT...)> *) const;
|
||||||
|
virtual void destroy() noexcept;
|
||||||
|
virtual void destroy_deallocate() noexcept;
|
||||||
|
virtual R operator()(AT &&...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename F, typename A, typename R, typename ...AT>
|
||||||
|
FuncBase<R(AT...)> *FuncCore<F, A, R(AT...)>::clone() const {
|
||||||
|
using AA = AllocatorRebind<A, FuncCore>;
|
||||||
|
AA a(f_stor.second());
|
||||||
|
using D = AllocatorDestructor<AA>;
|
||||||
|
Box<FuncCore, D> hold(a.allocate(1), D(a, 1));
|
||||||
|
::new(hold.get()) FuncCore(f_stor.first(), A(a));
|
||||||
|
return hold.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename F, typename A, typename R, typename ...AT>
|
||||||
|
void FuncCore<F, A, R(AT...)>::clone(FuncBase<R(AT...)> *p) const {
|
||||||
|
::new (p) FuncCore(f_stor.first(), f_stor.second());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename F, typename A, typename R, typename ...AT>
|
||||||
|
void FuncCore<F, A, R(AT...)>::destroy() noexcept {
|
||||||
|
f_stor.~CompressedPair<F, A>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename F, typename A, typename R, typename ...AT>
|
||||||
|
void FuncCore<F, A, R(AT...)>::destroy_deallocate() noexcept {
|
||||||
|
using AA = AllocatorRebind<A, FuncCore>;
|
||||||
|
AA a(f_stor.second());
|
||||||
|
f_stor.~CompressedPair<F, A>();
|
||||||
|
a.deallocate(this, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename F, typename A, typename R, typename ...AT>
|
||||||
|
R FuncCore<F, A, R(AT...)>::operator()(AT &&...args) {
|
||||||
|
using Invoker = FuncInvokeVoidReturnWrapper<R>;
|
||||||
|
return Invoker::call(f_stor.first(), std::forward<AT>(args)...);
|
||||||
|
}
|
||||||
|
} /* namespace detail */
|
||||||
|
|
||||||
|
template<typename R, typename ...Args>
|
||||||
|
class Function<R(Args...)> {
|
||||||
|
using Base = detail::FuncBase<R(Args...)>;
|
||||||
|
AlignedStorage<3 * sizeof(void *)> p_buf;
|
||||||
|
Base *p_f;
|
||||||
|
|
||||||
|
static inline Base *as_base(void *p) {
|
||||||
|
return reinterpret_cast<Base *>(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<
|
||||||
|
typename F,
|
||||||
|
bool = !IsSame<F, Function> && detail::IsInvokable<F &, Args...>
|
||||||
|
>
|
||||||
|
struct CallableBase;
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
struct CallableBase<F, true> {
|
||||||
|
static constexpr bool value =
|
||||||
|
IsSame<R, void> || IsConvertible<detail::InvokeOf<F &, Args...>, R>;
|
||||||
|
};
|
||||||
|
template<typename F>
|
||||||
|
struct CallableBase<F, false> {
|
||||||
|
static constexpr bool value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
static constexpr bool Callable = CallableBase<F>::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<Callable<F> && !IsSame<F, Function>>
|
||||||
|
>
|
||||||
|
Function(F);
|
||||||
|
|
||||||
|
template<typename A>
|
||||||
|
Function(AllocatorArg, A const &) noexcept: p_f(nullptr) {}
|
||||||
|
template<typename A>
|
||||||
|
Function(AllocatorArg, A const &, Nullptr) noexcept: p_f(nullptr) {}
|
||||||
|
template<typename A>
|
||||||
|
Function(AllocatorArg, A const &, Function const &);
|
||||||
|
template<typename A>
|
||||||
|
Function(AllocatorArg, A const &, Function &&);
|
||||||
|
template<typename F, typename A, typename = EnableIf<Callable<F>>>
|
||||||
|
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<typename F>
|
||||||
|
EnableIf<
|
||||||
|
Callable<Decay<F>> && !IsSame<RemoveReference<F>, Function>,
|
||||||
|
Function &
|
||||||
|
> operator=(F &&f) {
|
||||||
|
Function(std::forward<F>(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<typename RR, typename ...AA>
|
||||||
|
bool operator==(Function<RR(AA...)> &) const = delete;
|
||||||
|
template<typename RR, typename ...AA>
|
||||||
|
bool operator!=(Function<RR(AA...)> &) const = delete;
|
||||||
|
|
||||||
|
R operator()(Args ...a) const {
|
||||||
|
return (*p_f)(std::forward<Args>(a)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename R, typename ...Args>
|
||||||
|
Function<R(Args...)>::Function(Function const &f) {
|
||||||
|
if (!f.p_f) {
|
||||||
|
p_f = nullptr;
|
||||||
|
} else if (static_cast<void *>(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<typename R, typename ...Args>
|
||||||
|
template<typename A>
|
||||||
|
Function<R(Args...)>::Function(
|
||||||
|
AllocatorArg, A const &, Function const &f
|
||||||
|
) {
|
||||||
|
if (!f.p_f) {
|
||||||
|
p_f = nullptr;
|
||||||
|
} else if (static_cast<void *>(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<typename R, typename ...Args>
|
||||||
|
Function<R(Args...)>::Function(Function &&f) noexcept {
|
||||||
|
if (!f.p_f) {
|
||||||
|
p_f = nullptr;
|
||||||
|
} else if (static_cast<void *>(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<typename R, typename ...Args>
|
||||||
|
template<typename A>
|
||||||
|
Function<R(Args...)>::Function(
|
||||||
|
AllocatorArg, A const &, Function &&f
|
||||||
|
) {
|
||||||
|
if (!f.p_f) {
|
||||||
|
p_f = nullptr;
|
||||||
|
} else if (static_cast<void *>(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<typename R, typename ...Args>
|
||||||
|
template<typename F, typename>
|
||||||
|
Function<R(Args...)>::Function(F f): p_f(nullptr) {
|
||||||
|
if (!detail::func_is_not_null(f)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
using FF = detail::FuncCore<F, Allocator<F>, R(Args...)>;
|
||||||
|
if ((sizeof(FF) <= sizeof(p_buf)) && IsNothrowCopyConstructible<F>) {
|
||||||
|
p_f = ::new(static_cast<void *>(&p_buf)) FF(std::move(f));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
using AA = Allocator<FF>;
|
||||||
|
AA a;
|
||||||
|
using D = detail::AllocatorDestructor<AA>;
|
||||||
|
Box<FF, D> hold(a.allocate(1), D(a, 1));
|
||||||
|
::new(hold.get()) FF(std::move(f), Allocator<F>(a));
|
||||||
|
p_f = hold.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename R, typename ...Args>
|
||||||
|
template<typename F, typename A, typename>
|
||||||
|
Function<R(Args...)>::Function(AllocatorArg, A const &a, F f):
|
||||||
|
p_f(nullptr)
|
||||||
|
{
|
||||||
|
if (!detail::func_is_not_null(f)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
using FF = detail::FuncCore<F, A, R(Args...)>;
|
||||||
|
using AA = AllocatorRebind<A, FF>;
|
||||||
|
AA aa(a);
|
||||||
|
if (
|
||||||
|
(sizeof(FF) <= sizeof(p_buf)) && IsNothrowCopyConstructible<F> &&
|
||||||
|
IsNothrowCopyConstructible<AA>
|
||||||
|
) {
|
||||||
|
p_f = ::new(static_cast<void *>(&p_buf)) FF(std::move(f), A(aa));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
using D = detail::AllocatorDestructor<AA>;
|
||||||
|
Box<FF, D> hold(aa.allocate(1), D(aa, 1));
|
||||||
|
::new(hold.get()) FF(std::move(f), A(aa));
|
||||||
|
p_f = hold.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename R, typename ...Args>
|
||||||
|
Function<R(Args...)> &Function<R(Args...)>::operator=(Function &&f)
|
||||||
|
noexcept
|
||||||
|
{
|
||||||
|
if (static_cast<void *>(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<void *>(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<typename R, typename ...Args>
|
||||||
|
Function<R(Args...)> &Function<R(Args...)>::operator=(Nullptr) noexcept {
|
||||||
|
if (static_cast<void *>(p_f) == &p_buf) {
|
||||||
|
p_f->destroy();
|
||||||
|
} else if (p_f) {
|
||||||
|
p_f->destroy_deallocate();
|
||||||
|
}
|
||||||
|
p_f = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename R, typename ...Args>
|
||||||
|
Function<R(Args...)>::~Function() {
|
||||||
|
if (static_cast<void *>(p_f) == &p_buf) {
|
||||||
|
p_f->destroy();
|
||||||
|
} else if (p_f) {
|
||||||
|
p_f->destroy_deallocate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename R, typename ...Args>
|
||||||
|
void Function<R(Args...)>::swap(Function &f) noexcept {
|
||||||
|
if (
|
||||||
|
(static_cast<void *>(p_f) == &p_buf) &&
|
||||||
|
(static_cast<void *>(f.p_f) == &f.p_buf)
|
||||||
|
) {
|
||||||
|
/* both in small storage */
|
||||||
|
AlignedStorage<sizeof(p_buf)> 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<void *>(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<void *>(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<typename R, typename ...Args>
|
||||||
|
inline bool operator==(Function<R(Args...)> const &f, Nullptr) noexcept {
|
||||||
|
return !f;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename R, typename ...Args>
|
||||||
|
inline bool operator==(Nullptr, Function<R(Args...)> const &f) noexcept {
|
||||||
|
return !f;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename R, typename ...Args>
|
||||||
|
inline bool operator!=(Function<R(Args...)> const &f, Nullptr) noexcept {
|
||||||
|
return bool(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename R, typename ...Args>
|
||||||
|
inline bool operator!=(Nullptr, Function<R(Args...)> const &f) noexcept {
|
||||||
|
return bool(f);
|
||||||
|
}
|
||||||
|
|
||||||
} /* namespace ostd */
|
} /* namespace ostd */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue