/* Function objects for OctaSTD. * * This file is part of OctaSTD. See COPYING.md for futher information. */ #ifndef OCTA_FUNCTIONAL_HH #define OCTA_FUNCTIONAL_HH #include #include "octa/platform.hh" #include "octa/new.hh" #include "octa/memory.hh" #include "octa/utility.hh" #include "octa/type_traits.hh" namespace octa { /* basic function objects */ #define OCTA_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; \ }; OCTA_DEFINE_BINARY_OP(Less, <, bool) OCTA_DEFINE_BINARY_OP(LessEqual, <=, bool) OCTA_DEFINE_BINARY_OP(Greater, >, bool) OCTA_DEFINE_BINARY_OP(GreaterEqual, >=, bool) OCTA_DEFINE_BINARY_OP(Equal, ==, bool) OCTA_DEFINE_BINARY_OP(NotEqual, !=, bool) OCTA_DEFINE_BINARY_OP(LogicalAnd, &&, bool) OCTA_DEFINE_BINARY_OP(LogicalOr, ||, bool) OCTA_DEFINE_BINARY_OP(Modulo, %, T) OCTA_DEFINE_BINARY_OP(Multiply, *, T) OCTA_DEFINE_BINARY_OP(Divide, /, T) OCTA_DEFINE_BINARY_OP(Add, +, T) OCTA_DEFINE_BINARY_OP(Subtract, -, T) OCTA_DEFINE_BINARY_OP(BitAnd, &, T) OCTA_DEFINE_BINARY_OP(BitOr, |, T) OCTA_DEFINE_BINARY_OP(BitXor, ^, T) #undef OCTA_DEFINE_BINARY_OP 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::value > 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 = octa::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 = octa::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 = octa::endian_swap64(u.sv); return u.iv; } }; template T endian_swap(T x) { return EndianSwap()(x); } namespace detail { template::value > 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 OCTA_BYTE_ORDER == OCTA_ENDIAN_LIL template struct FromLilEndian: octa::detail::EndianSame {}; template struct FromBigEndian: EndianSwap {}; #else template struct FromLilEndian: EndianSwap {}; template struct FromBigEndian: octa::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 = octa::Size; octa::Size operator()(const T &v) const { return v.to_hash(); } }; namespace detail { template struct ToHashBase { using Argument = T; using Result = octa::Size; octa::Size operator()(T v) const { return octa::Size(v); } }; } #define OCTA_HASH_BASIC(T) template<> struct ToHash: octa::detail::ToHashBase {}; OCTA_HASH_BASIC(bool) OCTA_HASH_BASIC(char) OCTA_HASH_BASIC(short) OCTA_HASH_BASIC(int) OCTA_HASH_BASIC(long) OCTA_HASH_BASIC(octa::sbyte) OCTA_HASH_BASIC(octa::byte) OCTA_HASH_BASIC(octa::ushort) OCTA_HASH_BASIC(octa::uint) OCTA_HASH_BASIC(octa::ulong) OCTA_HASH_BASIC(octa::Char16) OCTA_HASH_BASIC(octa::Char32) OCTA_HASH_BASIC(octa::Wchar) #undef OCTA_HASH_BASIC namespace detail { static inline Size mem_hash(const void *p, octa::Size l) { const octa::byte *d = (const octa::byte *)p; octa::Size h = 5381; for (Size i = 0; i < l; ++i) h = ((h << 5) + h) ^ d[i]; return h; } template struct ScalarHash; template struct ScalarHash { using Argument = T; using Result = octa::Size; octa::Size operator()(T v) const { union { T v; octa::Size h; } u; u.h = 0; u.v = v; return u.h; } }; template struct ScalarHash { using Argument = T; using Result = octa::Size; octa::Size operator()(T v) const { union { T v; octa::Size h; } u; u.v = v; return u.h; } }; template struct ScalarHash { using Argument = T; using Result = octa::Size; octa::Size operator()(T v) const { union { T v; struct { octa::Size h1, h2; }; } u; u.v = v; return mem_hash((const void *)&u, sizeof(u)); } }; template struct ScalarHash { using Argument = T; using Result = octa::Size; octa::Size operator()(T v) const { union { T v; struct { octa::Size h1, h2, h3; }; } u; u.v = v; return mem_hash((const void *)&u, sizeof(u)); } }; template struct ScalarHash { using Argument = T; using Result = octa::Size; octa::Size operator()(T v) const { union { T v; struct { octa::Size h1, h2, h3, h4; }; } u; u.v = v; return mem_hash((const void *)&u, sizeof(u)); } }; } /* namespace detail */ template<> struct ToHash: octa::detail::ScalarHash {}; template<> struct ToHash: octa::detail::ScalarHash {}; template<> struct ToHash: octa::detail::ScalarHash { octa::Size operator()(float v) const { if (v == 0) return 0; return octa::detail::ScalarHash::operator()(v); } }; template<> struct ToHash: octa::detail::ScalarHash { octa::Size operator()(double v) const { if (v == 0) return 0; return octa::detail::ScalarHash::operator()(v); } }; template<> struct ToHash: octa::detail::ScalarHash { octa::Size operator()(octa::ldouble v) const { if (v == 0) return 0; #ifdef __i386__ union { octa::ldouble v; struct { octa::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 { octa::ldouble v; struct { octa::Size h1, h2; }; } u; u.h1 = u.h2 = 0; u.v = v; return (u.h1 ^ u.h2); #else return octa::detail::ScalarHash::operator()(v); #endif #endif } }; namespace detail { template, char>::value> struct ToHashPtr { using Argument = T *; using Result = octa::Size; octa::Size operator()(T *v) const { union { T *v; octa::Size h; } u; u.v = v; return octa::detail::mem_hash((const void *)&u, sizeof(u)); } }; template struct ToHashPtr { using Argument = T *; using Result = octa::Size; octa::Size operator()(T *v) const { return octa::detail::mem_hash(v, strlen(v)); } }; } template struct ToHash: octa::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 octa::detail::MemFn mem_fn(R T:: *ptr) { return octa::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 struct FunctorInPlace { static constexpr bool value = sizeof(T) <= sizeof(FunctorData) && (alignof(FunctorData) % alignof(T)) == 0 && octa::IsMoveConstructible::value; }; 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)(octa::forward(args)...); } static void store_f(FmStorage &s, T v) { new (&get_ref(s)) T(octa::forward(v)); } static void move_f(FmStorage &lhs, FmStorage &&rhs) { new (&get_ref(lhs)) T(octa::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::value> > { template static R call(const FunctorData &s, Args ...args) { return (*(octa::AllocatorPointer &)s) (octa::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, octa::forward(v)); } static void move_f(FmStorage &lhs, FmStorage &&rhs) { new (&get_ptr_ref(lhs)) AllocatorPointer(octa::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(octa::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, octa::move(rhs)); Spec::destroy_f(rhs.get_alloc(), rhs); create_fm(lhs, octa::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 struct IsValidFunctor { static constexpr bool value = false; }; template struct IsValidFunctor, R(A...)> { static constexpr bool value = false; }; template T func_to_functor(T &&f) { return octa::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); } template struct IsValidFunctor { struct Nat {}; template static decltype(func_to_functor(octa::declval()) (octa::declval()...)) test(U *); template static Nat test(...); static constexpr bool value = octa::IsConvertible< decltype(test(nullptr)), R >::value; }; template using FunctorType = decltype(func_to_functor(octa::declval())); } /* namespace detail */ template struct Function: octa::detail::FunctionBase { Function( ) { init_empty(); } Function(octa::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::value >> Function(T f) { if (func_is_null(f)) { init_empty(); return; } initialize(octa::detail::func_to_functor(octa::forward(f)), octa::Allocator>()); } template Function(octa::AllocatorArg, const A &) { init_empty(); } template Function(octa::AllocatorArg, const A &, octa::Nullptr) { init_empty(); } template Function(octa::AllocatorArg, const A &, Function &&f) { init_empty(); swap(f); } template Function(octa::AllocatorArg, const A &a, const Function &f): p_call(f.p_call) { const octa::detail::FunctionManager *mfa = &octa::detail::get_default_fm, A>(); if (f.p_stor.manager == mfa) { octa::detail::create_fm, A>(p_stor, A(a)); mfa->call_copyf_fo(p_stor, f.p_stor); return; } using AA = AllocatorRebind; const octa::detail::FunctionManager *mff = &octa::detail::get_default_fm(); if (f.p_stor.manager == mff) { octa::detail::create_fm(p_stor, AA(a)); mff->call_copyf_fo(p_stor, f.P_stor); return; } initialize(f, AA(a)); } template::value >> Function(octa::AllocatorArg, const A &a, T f) { if (func_is_null(f)) { init_empty(); return; } initialize(octa::detail::func_to_functor(octa::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, octa::forward(args)...); } template void assign(F &&f, const A &a) { Function(octa::allocator_arg, a, octa::forward(f)).swap(*this); } void swap(Function &f) { octa::detail::FmStorage tmp; f.p_stor.manager->call_move_and_destroyf(tmp, octa::move(f.p_stor)); p_stor.manager->call_move_and_destroyf(f.p_stor, octa::move(p_stor)); tmp.manager->call_move_and_destroyf(p_stor, octa::move(tmp)); octa::swap(p_call, f.p_call); } operator bool() const { return p_call != nullptr; } private: octa::detail::FmStorage p_stor; R (*p_call)(const octa::detail::FunctorData &, Args...); template void initialize(T &&f, A &&a) { p_call = &octa::detail::FunctorDataManager::template call; octa::detail::create_fm(p_stor, octa::forward(a)); octa::detail::FunctorDataManager::store_f(p_stor, octa::forward(f)); } void init_empty() { using emptyf = R(*)(Args...); using emptya = octa::Allocator; p_call = nullptr; octa::detail::create_fm(p_stor, emptya()); octa::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==(octa::Nullptr, const Function &rhs) { return !rhs; } template bool operator==(const Function &lhs, octa::Nullptr) { return !lhs; } template bool operator!=(octa::Nullptr, const Function &rhs) { return rhs; } template bool operator!=(const Function &lhs, octa::Nullptr) { return lhs; } namespace detail { template struct DcLambdaTypes: DcLambdaTypes {}; template struct DcLambdaTypes { using Ptr = R (*)(A...); using Obj = octa::Function; }; template struct DcFuncTest { template static char test(typename DcLambdaTypes::Ptr); template static int test(...); static constexpr bool value = (sizeof(test(octa::declval())) == 1); }; template::value> struct DcFuncTypeObjBase { using Type = typename DcLambdaTypes::Obj; }; template struct DcFuncTypeObjBase { using Type = typename DcLambdaTypes::Ptr; }; template::value && octa::IsMoveConstructible::value > struct DcFuncTypeObj { using Type = typename DcFuncTypeObjBase::Type; }; template struct DcFuncTypeObj { using Type = F; }; template::value> struct DcFuncType { using Type = F; }; template struct DcFuncType { using Type = typename DcFuncTypeObj::Type; }; } template using FunctionMakeDefaultConstructible = typename octa::detail::DcFuncType::Type; } /* namespace octa */ #endif