/* Function objects for OctaSTD. * * This file is part of OctaSTD. See COPYING.md for futher information. */ #ifndef OSTD_FUNCTIONAL_HH #define OSTD_FUNCTIONAL_HH #include #include "ostd/platform.hh" #include "ostd/new.hh" #include "ostd/memory.hh" #include "ostd/utility.hh" #include "ostd/type_traits.hh" namespace ostd { /* basic function objects */ #define OSTD_DEFINE_BINARY_OP(name, op, RT) \ template struct name { \ RT operator()(const T &x, const T &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()(const T &x, const T &y) const { return x == y; } }; template struct EqualWithCstr: detail::CharEqual {}; template struct LogicalNot { bool operator()(const T &x) const { return !x; } using Argument = T; using Result = bool; }; template struct Negate { bool operator()(const T &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(const T &f): p_fn(f) {} bool operator()(const FirstArgument &x, const SecondArgument &y) { return !p_fn(x, y); } private: T p_fn; }; template struct UnaryNegate { using Argument = typename T::Argument; using Result = bool; explicit UnaryNegate(const T &f): p_fn(f) {} bool operator()(const Argument &x) { return !p_fn(x); } private: T p_fn; }; template UnaryNegate not1(const T &fn) { return UnaryNegate(fn); } template BinaryNegate not2(const T &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()(const T &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) OSTD_HASH_BASIC(Char16) OSTD_HASH_BASIC(Char32) 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(const void *p, Size l) { using Consts = FnvConstants; const byte *d = (const byte *)p; Size h = Consts::offset; for (const byte *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((const void *)&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((const void *)&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((const void *)&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((const void *)&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(const T &v) { return ToHash()(v); } /* reference wrapper */ template struct ReferenceWrapper { using Type = T; ReferenceWrapper(T &v): p_ptr(address_of(v)) {} ReferenceWrapper(const ReferenceWrapper &) = default; ReferenceWrapper(T &&) = delete; ReferenceWrapper &operator=(const ReferenceWrapper &) = 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(const T &&) = delete; template ReferenceWrapper cref(const T &v) { return ReferenceWrapper(v); } template ReferenceWrapper cref(ReferenceWrapper v) { return ReferenceWrapper(v); } template void cref(const T &&) = 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 = const T; }; template struct MemTypes { using Result = R; using FirstArgument = const T; 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))(forward(args)...)) { return ((obj).*(p_ptr))(forward(args)...); } template auto operator()(const T &obj, A &&...args) -> decltype(((obj).*(p_ptr))(forward(args)...)) const { return ((obj).*(p_ptr))(forward(args)...); } template auto operator()(T *obj, A &&...args) -> decltype(((obj)->*(p_ptr))(forward(args)...)) { return ((obj)->*(p_ptr))(forward(args)...); } template auto operator()(const T *obj, A &&...args) -> decltype(((obj)->*(p_ptr))(forward(args)...)) const { return ((obj)->*(p_ptr))(forward(args)...); } }; } /* namespace detail */ template detail::MemFn mem_fn(R T:: *ptr) { return detail::MemFn(ptr); } /* function impl * reference: http://probablydance.com/2013/01/13/a-faster-implementation-of-stdfunction */ template struct Function; namespace detail { struct FunctorData { void *p1, *p2; }; template constexpr bool FunctorInPlace = sizeof(T) <= sizeof(FunctorData) && (alignof(FunctorData) % alignof(T)) == 0 && IsMoveConstructible; struct FunctionManager; struct FmStorage { FunctorData data; const FunctionManager *manager; template A &get_alloc() { union { const FunctionManager **m; A *alloc; } u; u.m = &manager; return *u.alloc; } template const A &get_alloc() const { union { const FunctionManager * const *m; const A *alloc; } u; u.m = &manager; return *u.alloc; } }; template struct FunctorDataManager { template static R call(const FunctorData &s, Args ...args) { return ((T &)s)(forward(args)...); } static void store_f(FmStorage &s, T v) { new (&get_ref(s)) T(forward(v)); } static void move_f(FmStorage &lhs, FmStorage &&rhs) { new (&get_ref(lhs)) T(move(get_ref(rhs))); } static void destroy_f(A &, FmStorage &s) { get_ref(s).~T(); } static T &get_ref(const FmStorage &s) { union { const FunctorData *data; T *ret; } u; u.data = &s.data; return *u.ret; } }; template struct FunctorDataManager>> { template static R call(const FunctorData &s, Args ...args) { return (*(AllocatorPointer &)s)(forward(args)...); } static void store_f(FmStorage &s, T v) { A &a = s.get_alloc(); AllocatorPointer *ptr = new (&get_ptr_ref(s)) AllocatorPointer(allocator_allocate(a, 1)); allocator_construct(a, *ptr, forward(v)); } static void move_f(FmStorage &lhs, FmStorage &&rhs) { new (&get_ptr_ref(lhs)) AllocatorPointer(move(get_ptr_ref(rhs))); get_ptr_ref(rhs) = nullptr; } static void destroy_f(A &a, FmStorage &s) { AllocatorPointer &ptr = get_ptr_ref(s); if (!ptr) return; allocator_destroy(a, ptr); allocator_deallocate(a, ptr, 1); ptr = nullptr; } static T &get_ref(const FmStorage &s) { return *get_ptr_ref(s); } static AllocatorPointer &get_ptr_ref(FmStorage &s) { return (AllocatorPointer &)(s.data); } static AllocatorPointer &get_ptr_ref(const FmStorage &s) { return (AllocatorPointer &)(s.data); } }; template static const FunctionManager &get_default_fm(); template static void create_fm(FmStorage &s, A &&a) { new (&s.get_alloc()) A(move(a)); s.manager = &get_default_fm(); } struct FunctionManager { template inline static constexpr FunctionManager create_default_manager() { return FunctionManager { &call_move_and_destroy, &call_copy, &call_copy_fo, &call_destroy }; } void (* const call_move_and_destroyf)(FmStorage &lhs, FmStorage &&rhs); void (* const call_copyf)(FmStorage &lhs, const FmStorage &rhs); void (* const call_copyf_fo)(FmStorage &lhs, const FmStorage &rhs); void (* const call_destroyf)(FmStorage &s); template static void call_move_and_destroy(FmStorage &lhs, FmStorage &&rhs) { using Spec = FunctorDataManager; Spec::move_f(lhs, move(rhs)); Spec::destroy_f(rhs.get_alloc(), rhs); create_fm(lhs, move(rhs.get_alloc())); rhs.get_alloc().~A(); } template static void call_copy(FmStorage &lhs, const FmStorage &rhs) { using Spec = FunctorDataManager; create_fm(lhs, A(rhs.get_alloc())); Spec::store_f(lhs, Spec::get_ref(rhs)); } template static void call_copy_fo(FmStorage &lhs, const FmStorage &rhs) { using Spec = FunctorDataManager; Spec::store_f(lhs, Spec::get_ref(rhs)); } template static void call_destroy(FmStorage &s) { using Spec = FunctorDataManager; Spec::destroy_f(s.get_alloc(), s); s.get_alloc().~A(); } }; template inline static const FunctionManager &get_default_fm() { static const FunctionManager def_manager = FunctionManager::create_default_manager(); return def_manager; } template struct FunctionBase { using Result = R; }; template struct FunctionBase { using Result = R; using Argument = T; }; template struct FunctionBase { using Result = R; using FirstArgument = T; using SecondArgument = U; }; template constexpr bool IsValidFunctor = false; template constexpr bool IsValidFunctor, R(A...)> = false; template T func_to_functor(T &&f) { return forward(f); } template auto func_to_functor(RR (T::*f)(AA...)) -> decltype(mem_fn(f)) { return mem_fn(f); } template auto func_to_functor(RR (T::*f)(AA...) const) -> decltype(mem_fn(f)) { return mem_fn(f); } struct ValidFunctorNat {}; template static decltype(func_to_functor(declval()) (declval()...)) valid_functor_test(U *); template static ValidFunctorNat valid_functor_test(...); template constexpr bool IsValidFunctor = IsConvertible(nullptr)), R>; template using FunctorType = decltype(func_to_functor(declval())); } /* namespace detail */ template struct Function: detail::FunctionBase { Function( ) { init_empty(); } Function(Nullptr) { init_empty(); } Function(Function &&f) { init_empty(); swap(f); } Function(const Function &f): p_call(f.p_call) { f.p_stor.manager->call_copyf(p_stor, f.p_stor); } template >> Function(T f) { if (func_is_null(f)) { init_empty(); return; } initialize(detail::func_to_functor(forward(f)), Allocator>()); } template Function(AllocatorArg, const A &) { init_empty(); } template Function(AllocatorArg, const A &, Nullptr) { init_empty(); } template Function(AllocatorArg, const A &, Function &&f) { init_empty(); swap(f); } template Function(AllocatorArg, const A &a, const Function &f): p_call(f.p_call) { const detail::FunctionManager *mfa = &detail::get_default_fm, A>(); if (f.p_stor.manager == mfa) { detail::create_fm, A>(p_stor, A(a)); mfa->call_copyf_fo(p_stor, f.p_stor); return; } using AA = AllocatorRebind; const detail::FunctionManager *mff = &detail::get_default_fm(); if (f.p_stor.manager == mff) { detail::create_fm(p_stor, AA(a)); mff->call_copyf_fo(p_stor, f.P_stor); return; } initialize(f, AA(a)); } template >> Function(AllocatorArg, const A &a, T f) { if (func_is_null(f)) { init_empty(); return; } initialize(detail::func_to_functor(forward(f)), A(a)); } ~Function() { p_stor.manager->call_destroyf(p_stor); } Function &operator=(Function &&f) { p_stor.manager->call_destroyf(p_stor); swap(f); return *this; } Function &operator=(const Function &f) { p_stor.manager->call_destroyf(p_stor); swap(Function(f)); return *this; }; R operator()(Args ...args) const { return p_call(p_stor.data, forward(args)...); } template void assign(F &&f, const A &a) { Function(allocator_arg, a, forward(f)).swap(*this); } void swap(Function &f) { detail::FmStorage tmp; f.p_stor.manager->call_move_and_destroyf(tmp, move(f.p_stor)); p_stor.manager->call_move_and_destroyf(f.p_stor, move(p_stor)); tmp.manager->call_move_and_destroyf(p_stor, move(tmp)); ostd::swap(p_call, f.p_call); } explicit operator bool() const { return p_call != nullptr; } private: detail::FmStorage p_stor; R (*p_call)(const detail::FunctorData &, Args...); template void initialize(T &&f, A &&a) { p_call = &detail::FunctorDataManager::template call; detail::create_fm(p_stor, forward(a)); detail::FunctorDataManager::store_f(p_stor, forward(f)); } void init_empty() { using emptyf = R(*)(Args...); using emptya = Allocator; p_call = nullptr; detail::create_fm(p_stor, emptya()); detail::FunctorDataManager::store_f(p_stor, nullptr); } template static bool func_is_null(const T &) { return false; } static bool func_is_null(R (* const &fptr)(Args...)) { return fptr == nullptr; } template static bool func_is_null(RR (T::* const &fptr)(AArgs...)) { return fptr == nullptr; } template static bool func_is_null(RR (T::* const &fptr)(AArgs...) const) { return fptr == nullptr; } }; template bool operator==(Nullptr, const Function &rhs) { return !rhs; } template bool operator==(const Function &lhs, Nullptr) { return !lhs; } template bool operator!=(Nullptr, const Function &rhs) { return rhs; } template bool operator!=(const Function &lhs, Nullptr) { return lhs; } 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