/* 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 #define OSTD_FUNCTIONAL_HH #include #include #include "ostd/platform.hh" #include "ostd/memory.hh" #include "ostd/utility.hh" #include "ostd/type_traits.hh" #include namespace ostd { /* basic function objects */ #define OSTD_DEFINE_BINARY_OP(name, op, RT) \ template \ struct name { \ RT operator()(T const &x, T const &y) const { \ return x op y; \ } \ using FirstArgument = T; \ using SecondARgument = T; \ using Result = RT; \ }; OSTD_DEFINE_BINARY_OP(Less, <, bool) OSTD_DEFINE_BINARY_OP(LessEqual, <=, bool) OSTD_DEFINE_BINARY_OP(Greater, >, bool) OSTD_DEFINE_BINARY_OP(GreaterEqual, >=, bool) OSTD_DEFINE_BINARY_OP(Equal, ==, bool) OSTD_DEFINE_BINARY_OP(NotEqual, !=, bool) OSTD_DEFINE_BINARY_OP(LogicalAnd, &&, bool) OSTD_DEFINE_BINARY_OP(LogicalOr, ||, bool) OSTD_DEFINE_BINARY_OP(Modulo, %, T) OSTD_DEFINE_BINARY_OP(Multiply, *, T) OSTD_DEFINE_BINARY_OP(Divide, /, T) OSTD_DEFINE_BINARY_OP(Add, +, T) OSTD_DEFINE_BINARY_OP(Subtract, -, T) OSTD_DEFINE_BINARY_OP(BitAnd, &, T) OSTD_DEFINE_BINARY_OP(BitOr, |, T) OSTD_DEFINE_BINARY_OP(BitXor, ^, T) #undef OSTD_DEFINE_BINARY_OP namespace detail { template, char>> struct CharEqual { using FirstArgument = T *; using SecondArgument = T *; using Result = bool; bool operator()(T *x, T *y) const { return !strcmp(x, y); } }; template struct CharEqual { using FirstArgument = T *; using SecondArgument = T *; using Result = bool; bool operator()(T *x, T *y) const { return x == y; } }; } template struct EqualWithCstr { using FirstArgument = T; using SecondArgument = T; bool operator()(T const &x, T const &y) const { return x == y; } }; template struct EqualWithCstr: detail::CharEqual {}; template struct LogicalNot { bool operator()(T const &x) const { return !x; } using Argument = T; using Result = bool; }; template struct Negate { bool operator()(T const &x) const { return -x; } using Argument = T; using Result = T; }; template struct BinaryNegate { using FirstArgument = typename T::FirstArgument; using SecondArgument = typename T::SecondArgument; using Result = bool; explicit BinaryNegate(T const &f): p_fn(f) {} bool operator()(FirstArgument const &x, SecondArgument const &y) { return !p_fn(x, y); } private: T p_fn; }; template struct UnaryNegate { using Argument = typename T::Argument; using Result = bool; explicit UnaryNegate(T const &f): p_fn(f) {} bool operator()(Argument const &x) { return !p_fn(x); } private: T p_fn; }; template UnaryNegate not1(T const &fn) { return UnaryNegate(fn); } template BinaryNegate not2(T const &fn) { return BinaryNegate(fn); } /* endian swap */ template> struct EndianSwap; template struct EndianSwap { using Argument = T; using Result = T; T operator()(T v) const { union { T iv; uint16_t sv; } u; u.iv = v; u.sv = endian_swap16(u.sv); return u.iv; } }; template struct EndianSwap { using Argument = T; using Result = T; T operator()(T v) const { union { T iv; uint32_t sv; } u; u.iv = v; u.sv = endian_swap32(u.sv); return u.iv; } }; template struct EndianSwap { using Argument = T; using Result = T; T operator()(T v) const { union { T iv; uint64_t sv; } u; u.iv = v; u.sv = endian_swap64(u.sv); return u.iv; } }; template T endian_swap(T x) { return EndianSwap()(x); } namespace detail { template> struct EndianSame; template struct EndianSame { using Argument = T; using Result = T; T operator()(T v) const { return v; } }; template struct EndianSame { using Argument = T; using Result = T; T operator()(T v) const { return v; } }; template struct EndianSame { using Argument = T; using Result = T; T operator()(T v) const { return v; } }; } #if OSTD_BYTE_ORDER == OSTD_ENDIAN_LIL template struct FromLilEndian: detail::EndianSame {}; template struct FromBigEndian: EndianSwap {}; #else template struct FromLilEndian: EndianSwap {}; template struct FromBigEndian: detail::EndianSame {}; #endif template T from_lil_endian(T x) { return FromLilEndian()(x); } template T from_big_endian(T x) { return FromBigEndian()(x); } /* hash */ template struct ToHash { using Argument = T; using Result = Size; Size operator()(T const &v) const { return v.to_hash(); } }; namespace detail { template struct ToHashBase { using Argument = T; using Result = Size; Size operator()(T v) const { return Size(v); } }; } #define OSTD_HASH_BASIC(T) \ template<> \ struct ToHash: detail::ToHashBase {}; OSTD_HASH_BASIC(bool) OSTD_HASH_BASIC(char) OSTD_HASH_BASIC(short) OSTD_HASH_BASIC(int) OSTD_HASH_BASIC(long) OSTD_HASH_BASIC(sbyte) OSTD_HASH_BASIC(byte) OSTD_HASH_BASIC(ushort) OSTD_HASH_BASIC(uint) OSTD_HASH_BASIC(ulong) #ifndef OSTD_TYPES_CHAR_16_32_NO_BUILTINS OSTD_HASH_BASIC(Char16) OSTD_HASH_BASIC(Char32) #endif OSTD_HASH_BASIC(Wchar) #undef OSTD_HASH_BASIC namespace detail { template struct FnvConstants { static constexpr Size prime = 16777619u; static constexpr Size offset = 2166136261u; }; template<> struct FnvConstants<8> { /* conversion is necessary here because when compiling on * 32bit, compilers will complain, despite this template * not being instantiated... */ static constexpr Size prime = Size(1099511628211u); static constexpr Size offset = Size(14695981039346656037u); }; inline Size mem_hash(void const *p, Size l) { using Consts = FnvConstants; byte const *d = static_cast(p); Size h = Consts::offset; for (byte const *it = d, *end = d + l; it != end; ++it) { h ^= *it; h *= Consts::prime; } return h; } template struct ScalarHash; template struct ScalarHash { using Argument = T; using Result = Size; Size operator()(T v) const { union { T v; Size h; } u; u.h = 0; u.v = v; return u.h; } }; template struct ScalarHash { using Argument = T; using Result = Size; Size operator()(T v) const { union { T v; Size h; } u; u.v = v; return u.h; } }; template struct ScalarHash { using Argument = T; using Result = Size; Size operator()(T v) const { union { T v; struct { Size h1, h2; }; } u; u.v = v; return mem_hash(static_cast(&u), sizeof(u)); } }; template struct ScalarHash { using Argument = T; using Result = Size; Size operator()(T v) const { union { T v; struct { Size h1, h2, h3; }; } u; u.v = v; return mem_hash(static_cast(&u), sizeof(u)); } }; template struct ScalarHash { using Argument = T; using Result = Size; Size operator()(T v) const { union { T v; struct { Size h1, h2, h3, h4; }; } u; u.v = v; return mem_hash(static_cast(&u), sizeof(u)); } }; } /* namespace detail */ template<> struct ToHash: detail::ScalarHash {}; template<> struct ToHash: detail::ScalarHash {}; template<> struct ToHash: detail::ScalarHash { Size operator()(float v) const { if (v == 0) return 0; return detail::ScalarHash::operator()(v); } }; template<> struct ToHash: detail::ScalarHash { Size operator()(double v) const { if (v == 0) return 0; return detail::ScalarHash::operator()(v); } }; template<> struct ToHash: detail::ScalarHash { Size operator()(ldouble v) const { if (v == 0) { return 0; } #ifdef __i386__ union { ldouble v; struct { Size h1, h2, h3, h4; }; } u; u.h1 = u.h2 = u.h3 = u.h4 = 0; u.v = v; return (u.h1 ^ u.h2 ^ u.h3 ^ u.h4); #else #ifdef __x86_64__ union { ldouble v; struct { Size h1, h2; }; } u; u.h1 = u.h2 = 0; u.v = v; return (u.h1 ^ u.h2); #else return detail::ScalarHash::operator()(v); #endif #endif } }; namespace detail { template, char>> struct ToHashPtr { using Argument = T *; using Result = Size; Size operator()(T *v) const { union { T *v; Size h; } u; u.v = v; return detail::mem_hash(static_cast(&u), sizeof(u)); } }; template struct ToHashPtr { using Argument = T *; using Result = Size; Size operator()(T *v) const { return detail::mem_hash(v, strlen(v)); } }; } template struct ToHash: detail::ToHashPtr {}; template typename ToHash::Result to_hash(T const &v) { return ToHash()(v); } /* reference wrapper */ template struct ReferenceWrapper { using Type = T; ReferenceWrapper(T &v): p_ptr(address_of(v)) {} ReferenceWrapper(ReferenceWrapper const &) = default; ReferenceWrapper(T &&) = delete; ReferenceWrapper &operator=(ReferenceWrapper const &) = default; operator T &() const { return *p_ptr; } T &get() const { return *p_ptr; } private: T *p_ptr; }; template ReferenceWrapper ref(T &v) { return ReferenceWrapper(v); } template ReferenceWrapper ref(ReferenceWrapper v) { return ReferenceWrapper(v); } template void ref(T const &&) = delete; template ReferenceWrapper cref(T const &v) { return ReferenceWrapper(v); } template ReferenceWrapper cref(ReferenceWrapper v) { return ReferenceWrapper(v); } template void cref(T const &&) = delete; /* mem_fn */ namespace detail { template struct MemTypes; template struct MemTypes { using Result = R; using Argument = T; }; template struct MemTypes { using Result = R; using FirstArgument = T; using SecondArgument = A; }; template struct MemTypes { using Result = R; using Argument = T const; }; template struct MemTypes { using Result = R; using FirstArgument = T const; using SecondArgument = A; }; template class MemFn: MemTypes { R T::*p_ptr; public: MemFn(R T::*ptr): p_ptr(ptr) {} template auto operator()(T &obj, A &&...args) -> decltype(((obj).*(p_ptr))(std::forward(args)...)) { return ((obj).*(p_ptr))(std::forward(args)...); } template auto operator()(T const &obj, A &&...args) -> decltype(((obj).*(p_ptr))(std::forward(args)...)) const { return ((obj).*(p_ptr))(std::forward(args)...); } template auto operator()(T *obj, A &&...args) -> decltype(((obj)->*(p_ptr))(std::forward(args)...)) { return ((obj)->*(p_ptr))(std::forward(args)...); } template auto operator()(T const *obj, A &&...args) -> decltype(((obj)->*(p_ptr))(std::forward(args)...)) const { return ((obj)->*(p_ptr))(std::forward(args)...); } }; } /* namespace detail */ template detail::MemFn mem_fn(R T:: *ptr) { return detail::MemFn(ptr); } /* 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( piecewise_construct, forward_as_tuple(std::move(f)), forward_as_tuple() ) {} explicit FuncCore(F const &f, A const &a): f_stor( piecewise_construct, forward_as_tuple(f), forward_as_tuple(a) ) {} explicit FuncCore(F const &f, A &&a): f_stor( piecewise_construct, forward_as_tuple(f), forward_as_tuple(std::move(a)) ) {} explicit FuncCore(F &&f, A &&a): f_stor( piecewise_construct, forward_as_tuple(std::move(f)), 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 detail { template struct DcLambdaTypes: DcLambdaTypes {}; template struct DcLambdaTypes { using Ptr = R (*)(A...); using Obj = Function; }; template static char dc_func_test(typename DcLambdaTypes::Ptr); template static int dc_func_test(...); template constexpr bool DcFuncTest = (sizeof(dc_func_test(declval())) == 1); template> struct DcFuncTypeObjBase { using Type = typename DcLambdaTypes::Obj; }; template struct DcFuncTypeObjBase { using Type = typename DcLambdaTypes::Ptr; }; template && IsMoveConstructible > struct DcFuncTypeObj { using Type = typename DcFuncTypeObjBase::Type; }; template struct DcFuncTypeObj { using Type = F; }; template> struct DcFuncType { using Type = F; }; template struct DcFuncType { using Type = typename DcFuncTypeObj::Type; }; } template using FunctionMakeDefaultConstructible = typename detail::DcFuncType::Type; } /* namespace ostd */ #endif