/* 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()(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))(forward(args)...)) { return ((obj).*(p_ptr))(forward(args)...); } template auto operator()(T const &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()(T const *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; FunctionManager const *manager; template A &get_alloc() { union { FunctionManager const **m; A *alloc; } u; u.m = &manager; return *u.alloc; } template A const &get_alloc() const { union { FunctionManager const * const *m; A const *alloc; } u; u.m = &manager; return *u.alloc; } }; template struct FunctorDataManager { template static R call(FunctorData const &s, Args ...args) { return (*reinterpret_cast(&const_cast(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(FmStorage const &s) { union { FunctorData const *data; T *ret; } u; u.data = &s.data; return *u.ret; } }; template struct FunctorDataManager>> { template static R call(FunctorData const &s, Args ...args) { return (*reinterpret_cast &>( const_cast(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(FmStorage const &s) { return *get_ptr_ref(s); } static AllocatorPointer &get_ptr_ref(FmStorage &s) { return reinterpret_cast &>(s.data); } static AllocatorPointer &get_ptr_ref(FmStorage const &s) { return reinterpret_cast &>( const_cast(s.data)); } }; template static FunctionManager const &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, FmStorage const &rhs); void (* const call_copyf_fo)(FmStorage &lhs, FmStorage const &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, FmStorage const &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, FmStorage const &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 FunctionManager const &get_default_fm() { static FunctionManager const 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(Function const &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, A const &) { init_empty(); } template Function(AllocatorArg, A const &, Nullptr) { init_empty(); } template Function(AllocatorArg, A const &, Function &&f) { init_empty(); swap(f); } template Function(AllocatorArg, A const &a, Function const &f): p_call(f.p_call) { detail::FunctionManager const *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; detail::FunctionManager const *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, A const &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=(Function const &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, A const &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)(detail::FunctorData const &, 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(T const &) { 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, Function const &rhs) { return !rhs; } template bool operator==(Function const &lhs, Nullptr) { return !lhs; } template bool operator!=(Nullptr, Function const &rhs) { return rhs; } template bool operator!=(Function const &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