/* Function objects for OctaSTD. * * This file is part of OctaSTD. See COPYING.md for futher information. */ #ifndef OCTA_FUNCTIONAL_H #define OCTA_FUNCTIONAL_H #include "octa/new.h" #include "octa/memory.h" #include "octa/utility.h" #include "octa/type_traits.h" namespace octa { /* basic function objects */ #define __OCTA_DEFINE_BINARY_OP(name, op, rettype) \ template struct name { \ bool operator()(const T &x, const T &y) const noexcept( \ noexcept(x op y) \ ) { return x op y; } \ typedef T FirstArgType; \ typedef T SecondArgType; \ typedef rettype ResultType; \ }; __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(Modulus, %, T) __OCTA_DEFINE_BINARY_OP(Multiplies, *, T) __OCTA_DEFINE_BINARY_OP(Divides, /, T) __OCTA_DEFINE_BINARY_OP(Plus, +, T) __OCTA_DEFINE_BINARY_OP(Minus, -, 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 noexcept(noexcept(!x)) { return !x; } typedef T ArgType; typedef bool ResultType; }; template struct Negate { bool operator()(const T &x) const noexcept(noexcept(-x)) { return -x; } typedef T ArgType; typedef T ResultType; }; template struct BinaryNegate { typedef typename T::FirstArgType FirstArgType; typedef typename T::SecondArgType SecondArgType; typedef bool ResultType; explicit BinaryNegate(const T &f) noexcept( IsNothrowCopyConstructible::value ): p_fn(f) {} bool operator()(const FirstArgType &x, const SecondArgType &y) noexcept( noexcept(p_fn(x, y)) ) { return !p_fn(x, y); } private: T p_fn; }; template struct UnaryNegate { typedef typename T::ArgType ArgType; typedef bool ResultType; explicit UnaryNegate(const T &f) noexcept( IsNothrowCopyConstructible::value ): p_fn(f) {} bool operator()(const ArgType &x) noexcept(noexcept(p_fn(x))) { return !p_fn(x); } private: T p_fn; }; template UnaryNegate not1(const T &fn) noexcept( IsNothrowCopyConstructible>::value ) { return UnaryNegate(fn); } template BinaryNegate not2(const T &fn) noexcept( IsNothrowCopyConstructible>::value ) { return BinaryNegate(fn); } /* hash */ template struct Hash; template struct __OctaHashBase { typedef T ArgType; typedef size_t ResultType; size_t operator()(T v) const noexcept { return (size_t)v; } }; #define __OCTA_HASH_BASIC(T) template<> struct Hash: __OctaHashBase {}; __OCTA_HASH_BASIC(bool) __OCTA_HASH_BASIC(char) __OCTA_HASH_BASIC(schar) __OCTA_HASH_BASIC(uchar) __OCTA_HASH_BASIC(char16_t) __OCTA_HASH_BASIC(char32_t) __OCTA_HASH_BASIC(wchar_t) __OCTA_HASH_BASIC(short) __OCTA_HASH_BASIC(ushort) __OCTA_HASH_BASIC(int) __OCTA_HASH_BASIC(uint) __OCTA_HASH_BASIC(long) __OCTA_HASH_BASIC(ulong) #undef __OCTA_HASH_BASIC static inline size_t __octa_mem_hash(const void *p, size_t l) { const uchar *d = (const uchar *)p; size_t h = 5381; for (size_t i = 0; i < l; ++i) h = ((h << 5) + h) ^ d[i]; return h; } template struct __OctaScalarHash; template struct __OctaScalarHash { typedef T ArgType; typedef size_t ResultType; size_t operator()(T v) const noexcept { union { T v; size_t h; } u; u.h = 0; u.v = v; return u.h; } }; template struct __OctaScalarHash { typedef T ArgType; typedef size_t ResultType; size_t operator()(T v) const noexcept { union { T v; size_t h; } u; u.v = v; return u.h; } }; template struct __OctaScalarHash { typedef T ArgType; typedef size_t ResultType; size_t operator()(T v) const noexcept { union { T v; struct { size_t h1, h2; }; } u; u.v = v; return __octa_mem_hash((const void *)&u, sizeof(u)); } }; template struct __OctaScalarHash { typedef T ArgType; typedef size_t ResultType; size_t operator()(T v) const noexcept { union { T v; struct { size_t h1, h2, h3; }; } u; u.v = v; return __octa_mem_hash((const void *)&u, sizeof(u)); } }; template struct __OctaScalarHash { typedef T ArgType; typedef size_t ResultType; size_t operator()(T v) const noexcept { union { T v; struct { size_t h1, h2, h3, h4; }; } u; u.v = v; return __octa_mem_hash((const void *)&u, sizeof(u)); } }; template<> struct Hash: __OctaScalarHash {}; template<> struct Hash: __OctaScalarHash {}; template<> struct Hash: __OctaScalarHash { size_t operator()(float v) const noexcept { if (v == 0) return 0; return __OctaScalarHash::operator()(v); } }; template<> struct Hash: __OctaScalarHash { size_t operator()(double v) const noexcept { if (v == 0) return 0; return __OctaScalarHash::operator()(v); } }; template<> struct Hash: __OctaScalarHash { size_t operator()(ldouble v) const noexcept { if (v == 0) return 0; #ifdef __i386__ union { ldouble v; struct { size_t 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_t h1, h2; }; } u; u.h1 = u.h2 = 0; u.v = v; return (u.h1 ^ u.h2); #else return __OctaScalarHash::operator()(v); #endif #endif } }; template struct Hash { typedef T *ArgType; typedef size_t ResultType; size_t operator()(T *v) const noexcept { union { T *v; size_t h; } u; u.v = v; return __octa_mem_hash((const void *)&u, sizeof(u)); } }; /* reference wrapper */ template struct ReferenceWrapper { typedef T type; ReferenceWrapper(T &v) noexcept: 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) noexcept { return ReferenceWrapper(v); } template ReferenceWrapper ref(ReferenceWrapper v) noexcept { return ReferenceWrapper(v); } template void ref(const T &&) = delete; template ReferenceWrapper cref(const T &v) noexcept { return ReferenceWrapper(v); } template ReferenceWrapper cref(ReferenceWrapper v) noexcept { return ReferenceWrapper(v); } template void cref(const T &&) = delete; /* mem_fn */ template struct __OctaMemTypes; template struct __OctaMemTypes { typedef R ResultType; typedef T ArgType; }; template struct __OctaMemTypes { typedef R ResultType; typedef T FirstArgType; typedef A SecondArgType; }; template struct __OctaMemTypes { typedef R ResultType; typedef const T ArgType; }; template struct __OctaMemTypes { typedef R ResultType; typedef const T FirstArgType; typedef A SecondArgType; }; template class __OctaMemFn: __OctaMemTypes { R T::*p_ptr; public: __OctaMemFn(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)...); } }; template __OctaMemFn mem_fn(R T:: *ptr) noexcept { return __OctaMemFn(ptr); } /* function impl * reference: http://probablydance.com/2013/01/13/a-faster-implementation-of-stdfunction */ template struct Function; struct __OctaFunctorData { void *p1, *p2; }; template struct __OctaFunctorInPlace { static constexpr bool value = sizeof(T) <= sizeof(__OctaFunctorData) && (alignof(__OctaFunctorData) % alignof(T)) == 0 && octa::IsNothrowMoveConstructible(); }; template struct __OctaFunctorDataManager { template static R call(const __OctaFunctorData &s, A ...args) { return ((T &)s)(forward(args)...); } static void store_f(__OctaFunctorData &s, T v) { new (&get_ref(s)) T(forward(v)); } static void move_f(__OctaFunctorData &lhs, __OctaFunctorData &&rhs) noexcept { new (&get_ref(lhs)) T(move(get_ref(rhs))); } static void destroy_f(__OctaFunctorData &s) noexcept { get_ref(s).~T(); } static T &get_ref(const __OctaFunctorData &s) noexcept { return (T &)s; } }; template struct __OctaFunctorDataManager::value>> { template static R call(const __OctaFunctorData &s, A ...args) { return (*(T *&)s)(forward(args)...); } static void store_f(__OctaFunctorData &s, T v) { new (&get_ptr_ref(s)) T *(new T(forward(v))); } static void move_f(__OctaFunctorData &lhs, __OctaFunctorData &&rhs) noexcept { new (&get_ptr_ref(lhs)) T *(get_ptr_ref(rhs)); get_ptr_ref(rhs) = nullptr; } static void destroy_f(__OctaFunctorData &s) noexcept { T *&ptr = get_ptr_ref(s); if (!ptr) return; delete ptr; ptr = nullptr; } static T &get_ref(const __OctaFunctorData &s) noexcept { return *get_ptr_ref(s); } static T *&get_ptr_ref(__OctaFunctorData &s) noexcept { return (T *&)s; } static T *&get_ptr_ref(const __OctaFunctorData &s) noexcept { return (T *&)s; } }; struct __OctaFunctionManager; struct __OctaFmStorage { __OctaFunctorData data; const __OctaFunctionManager *manager; }; template static const __OctaFunctionManager &__octa_get_default_fm(); struct __OctaFunctionManager { template inline static const __OctaFunctionManager create_default_manager() { return __OctaFunctionManager { &t_call_move_and_destroy, &t_call_copy, &t_call_destroy }; } void (* const call_move_and_destroy)(__OctaFmStorage &lhs, __OctaFmStorage &&rhs); void (* const call_copy)(__OctaFmStorage &lhs, const __OctaFmStorage &rhs); void (* const call_destroy)(__OctaFmStorage &s); template static void t_call_move_and_destroy(__OctaFmStorage &lhs, __OctaFmStorage &&rhs) { typedef __OctaFunctorDataManager spec; spec::move_f(lhs.data, move(rhs.data)); spec::destroy_f(rhs.data); lhs.manager = &__octa_get_default_fm(); } template static void t_call_copy(__OctaFmStorage &lhs, const __OctaFmStorage &rhs) { typedef __OctaFunctorDataManager spec; lhs.manager = &__octa_get_default_fm(); spec::store_f(lhs.data, spec::get_ref(rhs.data)); } template static void t_call_destroy(__OctaFmStorage &s) { typedef __OctaFunctorDataManager spec; spec::destroy_f(s.data); } }; template inline static const __OctaFunctionManager &__octa_get_default_fm() { static const __OctaFunctionManager def_manager = __OctaFunctionManager::create_default_manager(); return def_manager; } template struct __OctaFunction { typedef R ResultType; }; template struct __OctaFunction { typedef R ResultType; typedef T ArgType; }; template struct __OctaFunction { typedef R ResultType; typedef T FirstArgType; typedef U SecondArgType; }; template struct IsValidFunctor { static constexpr bool value = false; }; template struct IsValidFunctor, R(A...)> { static constexpr bool value = false; }; struct __OctaEmpty { }; 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); } template struct IsValidFunctor { template static decltype(func_to_functor(declval())(declval()...)) __octa_test(U *); template static __OctaEmpty __octa_test(...); static constexpr bool value = IsConvertible< decltype(__octa_test(nullptr)), R >::value; }; template struct Function: __OctaFunction { Function( ) noexcept { initialize_empty(); } Function(nullptr_t) noexcept { initialize_empty(); } Function(Function &&f) noexcept { initialize_empty(); swap(f); } Function(const Function &f) noexcept: p_call(f.p_call) { f.p_stor.manager->call_copy(p_stor, f.p_stor); } template Function(T f, EnableIf::value, __OctaEmpty> = __OctaEmpty()) noexcept(__OctaFunctorInPlace::value) { if (func_is_null(f)) { initialize_empty(); return; } initialize(func_to_functor(forward(f))); } ~Function() { p_stor.manager->call_destroy(p_stor); } Function &operator=(Function &&f) noexcept { p_stor.manager->call_destroy(p_stor); swap(f); return *this; } Function &operator=(const Function &f) noexcept { p_stor.manager->call_destroy(p_stor); f.p_stor.manager->call_copy(p_stor, f.p_stor); return *this; }; R operator()(A ...args) const { return p_call(p_stor.data, forward(args)...); } template void assign(F &&f) noexcept(__OctaFunctorInPlace::value) { Function(forward(f)).swap(*this); } void swap(Function &f) noexcept { __OctaFmStorage tmp; f.p_stor.manager->call_move_and_destroy(tmp, move(f.p_stor)); p_stor.manager->call_move_and_destroy(f.p_stor, move(p_stor)); tmp.manager->call_move_and_destroy(p_stor, move(tmp)); octa::swap(p_call, f.p_call); } operator bool() const noexcept { return p_call != nullptr; } private: __OctaFmStorage p_stor; R (*p_call)(const __OctaFunctorData &, A...); template void initialize(T f) { p_call = &__OctaFunctorDataManager::template call; p_stor.manager = &__octa_get_default_fm(); __OctaFunctorDataManager::store_f(p_stor.data, forward(f)); } void initialize_empty() noexcept { typedef R(*emptyf)(A...); p_call = nullptr; p_stor.manager = &__octa_get_default_fm(); __OctaFunctorDataManager::store_f(p_stor.data, nullptr); } template static bool func_is_null(const T &) { return false; } static bool func_is_null(R (* const &fptr)(A...)) { return fptr == nullptr; } template static bool func_is_null(RR (T::* const &fptr)(AA...)) { return fptr == nullptr; } template static bool func_is_null(RR (T::* const &fptr)(AA...) const) { return fptr == nullptr; } }; template void swap(Function &a, Function &b) noexcept { a.swap(b); } template bool operator==(nullptr_t, const Function &rhs) noexcept { return !rhs; } template bool operator==(const Function &lhs, nullptr_t) noexcept { return !lhs; } template bool operator!=(nullptr_t, const Function &rhs) noexcept { return rhs; } template bool operator!=(const Function &lhs, nullptr_t) noexcept { return lhs; } } #endif