1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792 |
-
- // Copyright 2015-2020 Denis Blank <denis.blank at outlook dot com>
- // Distributed under the Boost Software License, Version 1.0
- // (See accompanying file LICENSE_1_0.txt or copy at
- // http://www.boost.org/LICENSE_1_0.txt)
-
- #ifndef FU2_INCLUDED_FUNCTION2_HPP_
- #define FU2_INCLUDED_FUNCTION2_HPP_
-
- #include <cassert>
- #include <cstddef>
- #include <cstdlib>
- #include <memory>
- #include <tuple>
- #include <type_traits>
- #include <utility>
-
- // Defines:
- // - FU2_HAS_DISABLED_EXCEPTIONS
- #if defined(FU2_WITH_DISABLED_EXCEPTIONS) || \
- defined(FU2_MACRO_DISABLE_EXCEPTIONS)
- #define FU2_HAS_DISABLED_EXCEPTIONS
- #else // FU2_WITH_DISABLED_EXCEPTIONS
- #if defined(_MSC_VER)
- #if !defined(_HAS_EXCEPTIONS) || (_HAS_EXCEPTIONS == 0)
- #define FU2_HAS_DISABLED_EXCEPTIONS
- #endif
- #elif defined(__clang__)
- #if !(__EXCEPTIONS && __has_feature(cxx_exceptions))
- #define FU2_HAS_DISABLED_EXCEPTIONS
- #endif
- #elif defined(__GNUC__)
- #if !__EXCEPTIONS
- #define FU2_HAS_DISABLED_EXCEPTIONS
- #endif
- #endif
- #endif // FU2_WITH_DISABLED_EXCEPTIONS
- // - FU2_HAS_NO_FUNCTIONAL_HEADER
- #if !defined(FU2_WITH_NO_FUNCTIONAL_HEADER) && \
- !defined(FU2_NO_FUNCTIONAL_HEADER) && \
- !defined(FU2_HAS_DISABLED_EXCEPTIONS)
- #include <functional>
- #else
- #define FU2_HAS_NO_FUNCTIONAL_HEADER
- #endif
- // - FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
- #if defined(FU2_WITH_CXX17_NOEXCEPT_FUNCTION_TYPE)
- #define FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
- #else // FU2_WITH_CXX17_NOEXCEPT_FUNCTION_TYPE
- #if defined(_MSC_VER)
- #if defined(_HAS_CXX17) && _HAS_CXX17
- #define FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
- #endif
- #elif defined(__cpp_noexcept_function_type)
- #define FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
- #elif defined(__cplusplus) && (__cplusplus >= 201703L)
- #define FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
- #endif
- #endif // FU2_WITH_CXX17_NOEXCEPT_FUNCTION_TYPE
-
- // - FU2_HAS_NO_EMPTY_PROPAGATION
- #if defined(FU2_WITH_NO_EMPTY_PROPAGATION)
- #define FU2_HAS_NO_EMPTY_PROPAGATION
- #endif // FU2_WITH_NO_EMPTY_PROPAGATION
-
- #if !defined(FU2_HAS_DISABLED_EXCEPTIONS)
- #include <exception>
- #endif
-
- #if defined(__cpp_constexpr) && (__cpp_constexpr >= 201304)
- #define FU2_DETAIL_CXX14_CONSTEXPR constexpr
- #elif defined(__clang__) && defined(__has_feature)
- #if __has_feature(__cxx_generic_lambdas__) && \
- __has_feature(__cxx_relaxed_constexpr__)
- #define FU2_DETAIL_CXX14_CONSTEXPR constexpr
- #endif
- #elif defined(_MSC_VER) && (_MSC_VER >= 1915) && (_MSVC_LANG >= 201402)
- #define FU2_DETAIL_CXX14_CONSTEXPR constexpr
- #endif
- #ifndef FU2_DETAIL_CXX14_CONSTEXPR
- #define FU2_DETAIL_CXX14_CONSTEXPR
- #endif
-
- /// Hint for the compiler that this point should be unreachable
- #if defined(_MSC_VER)
- // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
- #define FU2_DETAIL_UNREACHABLE_INTRINSIC() __assume(false)
- #elif defined(__GNUC__)
- // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
- #define FU2_DETAIL_UNREACHABLE_INTRINSIC() __builtin_unreachable()
- #elif defined(__has_builtin)
- #if __has_builtin(__builtin_unreachable)
- // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
- #define FU2_DETAIL_UNREACHABLE_INTRINSIC() __builtin_unreachable()
- #endif
- #endif
- #ifndef FU2_DETAIL_UNREACHABLE_INTRINSIC
- // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
- #define FU2_DETAIL_UNREACHABLE_INTRINSIC() abort()
- #endif
-
- /// Causes the application to exit abnormally
- #if defined(_MSC_VER)
- // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
- #define FU2_DETAIL_TRAP() __debugbreak()
- #elif defined(__GNUC__)
- // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
- #define FU2_DETAIL_TRAP() __builtin_trap()
- #elif defined(__has_builtin)
- #if __has_builtin(__builtin_trap)
- // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
- #define FU2_DETAIL_TRAP() __builtin_trap()
- #endif
- #endif
- #ifndef FU2_DETAIL_TRAP
- // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
- #define FU2_DETAIL_TRAP() *(volatile int*)0x11 = 0
- #endif
-
- #ifndef NDEBUG
- // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
- #define FU2_DETAIL_UNREACHABLE() ::fu2::detail::unreachable_debug()
- #else
- // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
- #define FU2_DETAIL_UNREACHABLE() FU2_DETAIL_UNREACHABLE_INTRINSIC()
- #endif
-
- namespace fu2 {
- inline namespace abi_400 {
- namespace detail {
- template <typename Config, typename Property>
- class function;
-
- template <typename...>
- struct identity {};
-
- // Equivalent to C++17's std::void_t which targets a bug in GCC,
- // that prevents correct SFINAE behavior.
- // See http://stackoverflow.com/questions/35753920 for details.
- template <typename...>
- struct deduce_to_void : std::common_type<void> {};
-
- template <typename... T>
- using void_t = typename deduce_to_void<T...>::type;
-
- template <typename T>
- using unrefcv_t = std::remove_cv_t<std::remove_reference_t<T>>;
-
- // Copy enabler helper class
- template <bool /*Copyable*/>
- struct copyable {};
- template <>
- struct copyable<false> {
- copyable() = default;
- ~copyable() = default;
- copyable(copyable const&) = delete;
- copyable(copyable&&) = default;
- copyable& operator=(copyable const&) = delete;
- copyable& operator=(copyable&&) = default;
- };
-
- /// Configuration trait to configure the function_base class.
- template <bool Owning, bool Copyable, typename Capacity>
- struct config {
- // Is true if the function is owning.
- static constexpr auto const is_owning = Owning;
-
- // Is true if the function is copyable.
- static constexpr auto const is_copyable = Copyable;
-
- // The internal capacity of the function
- // used in small functor optimization.
- // The object shall expose the real capacity through Capacity::capacity
- // and the intended alignment through Capacity::alignment.
- using capacity = Capacity;
- };
-
- /// A config which isn't compatible to other configs
- template <bool Throws, bool HasStrongExceptGuarantee, typename... Args>
- struct property {
- // Is true when the function throws an exception on empty invocation.
- static constexpr auto const is_throwing = Throws;
-
- // Is true when the function throws an exception on empty invocation.
- static constexpr auto const is_strong_exception_guaranteed =
- HasStrongExceptGuarantee;
- };
-
- #ifndef NDEBUG
- [[noreturn]] inline void unreachable_debug() {
- FU2_DETAIL_TRAP();
- std::abort();
- }
- #endif
-
- /// Provides utilities for invocing callable objects
- namespace invocation {
- /// Invokes the given callable object with the given arguments
- template <typename Callable, typename... Args>
- constexpr auto invoke(Callable&& callable, Args&&... args) noexcept(
- noexcept(std::forward<Callable>(callable)(std::forward<Args>(args)...)))
- -> decltype(std::forward<Callable>(callable)(std::forward<Args>(args)...)) {
-
- return std::forward<Callable>(callable)(std::forward<Args>(args)...);
- }
- /// Invokes the given member function pointer by reference
- template <typename T, typename Type, typename Self, typename... Args>
- constexpr auto invoke(Type T::*member, Self&& self, Args&&... args) noexcept(
- noexcept((std::forward<Self>(self).*member)(std::forward<Args>(args)...)))
- -> decltype((std::forward<Self>(self).*
- member)(std::forward<Args>(args)...)) {
- return (std::forward<Self>(self).*member)(std::forward<Args>(args)...);
- }
- /// Invokes the given member function pointer by pointer
- template <typename T, typename Type, typename Self, typename... Args>
- constexpr auto invoke(Type T::*member, Self&& self, Args&&... args) noexcept(
- noexcept((std::forward<Self>(self)->*member)(std::forward<Args>(args)...)))
- -> decltype(
- (std::forward<Self>(self)->*member)(std::forward<Args>(args)...)) {
- return (std::forward<Self>(self)->*member)(std::forward<Args>(args)...);
- }
- /// Invokes the given pointer to a scalar member by reference
- template <typename T, typename Type, typename Self>
- constexpr auto
- invoke(Type T::*member,
- Self&& self) noexcept(noexcept(std::forward<Self>(self).*member))
- -> decltype(std::forward<Self>(self).*member) {
- return (std::forward<Self>(self).*member);
- }
- /// Invokes the given pointer to a scalar member by pointer
- template <typename T, typename Type, typename Self>
- constexpr auto
- invoke(Type T::*member,
- Self&& self) noexcept(noexcept(std::forward<Self>(self)->*member))
- -> decltype(std::forward<Self>(self)->*member) {
- return std::forward<Self>(self)->*member;
- }
-
- /// Deduces to a true type if the callable object can be invoked with
- /// the given arguments.
- /// We don't use invoke here because MSVC can't evaluate the nested expression
- /// SFINAE here.
- template <typename T, typename Args, typename = void>
- struct can_invoke : std::false_type {};
- template <typename T, typename... Args>
- struct can_invoke<T, identity<Args...>,
- decltype((void)std::declval<T>()(std::declval<Args>()...))>
- : std::true_type {};
- template <typename Pointer, typename T, typename... Args>
- struct can_invoke<Pointer, identity<T&, Args...>,
- decltype((void)((std::declval<T&>().*std::declval<Pointer>())(
- std::declval<Args>()...)))> : std::true_type {};
- template <typename Pointer, typename T, typename... Args>
- struct can_invoke<Pointer, identity<T&&, Args...>,
- decltype(
- (void)((std::declval<T&&>().*std::declval<Pointer>())(
- std::declval<Args>()...)))> : std::true_type {};
- template <typename Pointer, typename T, typename... Args>
- struct can_invoke<Pointer, identity<T*, Args...>,
- decltype(
- (void)((std::declval<T*>()->*std::declval<Pointer>())(
- std::declval<Args>()...)))> : std::true_type {};
- template <typename Pointer, typename T>
- struct can_invoke<Pointer, identity<T&>,
- decltype((void)(std::declval<T&>().*std::declval<Pointer>()))>
- : std::true_type {};
- template <typename Pointer, typename T>
- struct can_invoke<Pointer, identity<T&&>,
- decltype(
- (void)(std::declval<T&&>().*std::declval<Pointer>()))>
- : std::true_type {};
- template <typename Pointer, typename T>
- struct can_invoke<Pointer, identity<T*>,
- decltype(
- (void)(std::declval<T*>()->*std::declval<Pointer>()))>
- : std::true_type {};
-
- template <bool RequiresNoexcept, typename T, typename Args>
- struct is_noexcept_correct : std::true_type {};
- template <typename T, typename... Args>
- struct is_noexcept_correct<true, T, identity<Args...>>
- : std::integral_constant<bool,
- noexcept(::fu2::detail::invocation::invoke(
- std::declval<T>(), std::declval<Args>()...))> {
- };
- } // end namespace invocation
-
- namespace overloading {
- template <typename... Args>
- struct overload_impl;
- template <typename Current, typename Next, typename... Rest>
- struct overload_impl<Current, Next, Rest...> : Current,
- overload_impl<Next, Rest...> {
- explicit overload_impl(Current current, Next next, Rest... rest)
- : Current(std::move(current)), overload_impl<Next, Rest...>(
- std::move(next), std::move(rest)...) {
- }
-
- using Current::operator();
- using overload_impl<Next, Rest...>::operator();
- };
- template <typename Current>
- struct overload_impl<Current> : Current {
- explicit overload_impl(Current current) : Current(std::move(current)) {
- }
-
- using Current::operator();
- };
-
- template <typename... T>
- constexpr auto overload(T&&... callables) {
- return overload_impl<std::decay_t<T>...>{std::forward<T>(callables)...};
- }
- } // namespace overloading
-
- /// Declares the namespace which provides the functionality to work with a
- /// type-erased object.
- namespace type_erasure {
- /// Specialization to work with addresses of callable objects
- template <typename T, typename = void>
- struct address_taker {
- template <typename O>
- static void* take(O&& obj) {
- return std::addressof(obj);
- }
- static T& restore(void* ptr) {
- return *static_cast<T*>(ptr);
- }
- static T const& restore(void const* ptr) {
- return *static_cast<T const*>(ptr);
- }
- static T volatile& restore(void volatile* ptr) {
- return *static_cast<T volatile*>(ptr);
- }
- static T const volatile& restore(void const volatile* ptr) {
- return *static_cast<T const volatile*>(ptr);
- }
- };
- /// Specialization to work with addresses of raw function pointers
- template <typename T>
- struct address_taker<T, std::enable_if_t<std::is_pointer<T>::value>> {
- template <typename O>
- static void* take(O&& obj) {
- return reinterpret_cast<void*>(obj);
- }
- template <typename O>
- static T restore(O ptr) {
- return reinterpret_cast<T>(const_cast<void*>(ptr));
- }
- };
-
- template <typename Box>
- struct box_factory;
- /// Store the allocator inside the box
- template <bool IsCopyable, typename T, typename Allocator>
- struct box : private Allocator {
- friend box_factory<box>;
-
- T value_;
-
- explicit box(T value, Allocator allocator)
- : Allocator(std::move(allocator)), value_(std::move(value)) {
- }
-
- box(box&&) = default;
- box(box const&) = default;
- box& operator=(box&&) = default;
- box& operator=(box const&) = default;
- ~box() = default;
- };
- template <typename T, typename Allocator>
- struct box<false, T, Allocator> : private Allocator {
- friend box_factory<box>;
-
- T value_;
-
- explicit box(T value, Allocator allocator)
- : Allocator(std::move(allocator)), value_(std::move(value)) {
- }
-
- box(box&&) = default;
- box(box const&) = delete;
- box& operator=(box&&) = default;
- box& operator=(box const&) = delete;
- ~box() = default;
- };
-
- template <bool IsCopyable, typename T, typename Allocator>
- struct box_factory<box<IsCopyable, T, Allocator>> {
- using real_allocator =
- typename std::allocator_traits<std::decay_t<Allocator>>::
- template rebind_alloc<box<IsCopyable, T, Allocator>>;
-
- /// Allocates space through the boxed allocator
- static box<IsCopyable, T, Allocator>*
- box_allocate(box<IsCopyable, T, Allocator> const* me) {
- real_allocator allocator(*static_cast<Allocator const*>(me));
-
- return static_cast<box<IsCopyable, T, Allocator>*>(
- std::allocator_traits<real_allocator>::allocate(allocator, 1U));
- }
-
- /// Destroys the box through the given allocator
- static void box_deallocate(box<IsCopyable, T, Allocator>* me) {
- real_allocator allocator(*static_cast<Allocator const*>(me));
-
- me->~box();
- std::allocator_traits<real_allocator>::deallocate(allocator, me, 1U);
- }
- };
-
- /// Creates a box containing the given value and allocator
- template <bool IsCopyable, typename T, typename Allocator>
- auto make_box(std::integral_constant<bool, IsCopyable>, T&& value,
- Allocator&& allocator) {
- return box<IsCopyable, std::decay_t<T>, std::decay_t<Allocator>>(
- std::forward<T>(value), std::forward<Allocator>(allocator));
- }
-
- template <typename T>
- struct is_box : std::false_type {};
- template <bool IsCopyable, typename T, typename Allocator>
- struct is_box<box<IsCopyable, T, Allocator>> : std::true_type {};
-
- /// Provides access to the pointer to a heal allocated erased object
- /// as well to the inplace storage.
- union data_accessor {
- data_accessor() = default;
- explicit constexpr data_accessor(std::nullptr_t) noexcept : ptr_(nullptr) {
- }
- explicit constexpr data_accessor(void* ptr) noexcept : ptr_(ptr) {
- }
-
- /// The pointer we use if the object is on the heap
- void* ptr_;
- /// The first field of the inplace storage
- std::size_t inplace_storage_;
- };
-
- /// See opcode::op_fetch_empty
- static FU2_DETAIL_CXX14_CONSTEXPR void write_empty(data_accessor* accessor,
- bool empty) noexcept {
- accessor->inplace_storage_ = std::size_t(empty);
- }
-
- template <typename From, typename To>
- using transfer_const_t =
- std::conditional_t<std::is_const<std::remove_pointer_t<From>>::value,
- std::add_const_t<To>, To>;
- template <typename From, typename To>
- using transfer_volatile_t =
- std::conditional_t<std::is_volatile<std::remove_pointer_t<From>>::value,
- std::add_volatile_t<To>, To>;
-
- /// The retriever when the object is allocated inplace
- template <typename T, typename Accessor>
- FU2_DETAIL_CXX14_CONSTEXPR auto retrieve(std::true_type /*is_inplace*/,
- Accessor from,
- std::size_t from_capacity) {
- using type = transfer_const_t<Accessor, transfer_volatile_t<Accessor, void>>*;
-
- /// Process the command by using the data inside the internal capacity
- auto storage = &(from->inplace_storage_);
- auto inplace = const_cast<void*>(static_cast<type>(storage));
- return type(std::align(alignof(T), sizeof(T), inplace, from_capacity));
- }
-
- /// The retriever which is used when the object is allocated
- /// through the allocator
- template <typename T, typename Accessor>
- constexpr auto retrieve(std::false_type /*is_inplace*/, Accessor from,
- std::size_t /*from_capacity*/) {
-
- return from->ptr_;
- }
-
- namespace invocation_table {
- #if !defined(FU2_HAS_DISABLED_EXCEPTIONS)
- #if defined(FU2_HAS_NO_FUNCTIONAL_HEADER)
- struct bad_function_call : std::exception {
- bad_function_call() noexcept {
- }
-
- char const* what() const noexcept override {
- return "bad function call";
- }
- };
- #else
- using std::bad_function_call;
- #endif
- #endif
-
- #ifdef FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
- #define FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT(F) \
- F(, , noexcept, , &) \
- F(const, , noexcept, , &) \
- F(, volatile, noexcept, , &) \
- F(const, volatile, noexcept, , &) \
- F(, , noexcept, &, &) \
- F(const, , noexcept, &, &) \
- F(, volatile, noexcept, &, &) \
- F(const, volatile, noexcept, &, &) \
- F(, , noexcept, &&, &&) \
- F(const, , noexcept, &&, &&) \
- F(, volatile, noexcept, &&, &&) \
- F(const, volatile, noexcept, &&, &&)
- #define FU2_DETAIL_EXPAND_CV_NOEXCEPT(F) \
- F(, , noexcept) \
- F(const, , noexcept) \
- F(, volatile, noexcept) \
- F(const, volatile, noexcept)
- #else // FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
- #define FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT(F)
- #define FU2_DETAIL_EXPAND_CV_NOEXCEPT(F)
- #endif // FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE
-
- #define FU2_DETAIL_EXPAND_QUALIFIERS(F) \
- F(, , , , &) \
- F(const, , , , &) \
- F(, volatile, , , &) \
- F(const, volatile, , , &) \
- F(, , , &, &) \
- F(const, , , &, &) \
- F(, volatile, , &, &) \
- F(const, volatile, , &, &) \
- F(, , , &&, &&) \
- F(const, , , &&, &&) \
- F(, volatile, , &&, &&) \
- F(const, volatile, , &&, &&) \
- FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT(F)
- #define FU2_DETAIL_EXPAND_CV(F) \
- F(, , ) \
- F(const, , ) \
- F(, volatile, ) \
- F(const, volatile, ) \
- FU2_DETAIL_EXPAND_CV_NOEXCEPT(F)
-
- /// If the function is qualified as noexcept, the call will never throw
- template <bool IsNoexcept>
- [[noreturn]] void throw_or_abortnoexcept(
- std::integral_constant<bool, IsNoexcept> /*is_throwing*/) noexcept {
- std::abort();
- }
- /// Calls std::abort on empty function calls
- [[noreturn]] inline void
- throw_or_abort(std::false_type /*is_throwing*/) noexcept {
- std::abort();
- }
- /// Throws bad_function_call on empty funciton calls
- [[noreturn]] inline void throw_or_abort(std::true_type /*is_throwing*/) {
- #ifdef FU2_HAS_DISABLED_EXCEPTIONS
- throw_or_abort(std::false_type{});
- #else
- throw bad_function_call{};
- #endif
- }
-
- template <typename T>
- struct function_trait;
-
- using is_noexcept_ = std::false_type;
- using is_noexcept_noexcept = std::true_type;
-
- #define FU2_DEFINE_FUNCTION_TRAIT(CONST, VOLATILE, NOEXCEPT, OVL_REF, REF) \
- template <typename Ret, typename... Args> \
- struct function_trait<Ret(Args...) CONST VOLATILE OVL_REF NOEXCEPT> { \
- using pointer_type = Ret (*)(data_accessor CONST VOLATILE*, \
- std::size_t capacity, Args...); \
- template <typename T, bool IsInplace> \
- struct internal_invoker { \
- static Ret invoke(data_accessor CONST VOLATILE* data, \
- std::size_t capacity, Args... args) NOEXCEPT { \
- auto obj = retrieve<T>(std::integral_constant<bool, IsInplace>{}, \
- data, capacity); \
- auto box = static_cast<T CONST VOLATILE*>(obj); \
- return invocation::invoke( \
- static_cast<std::decay_t<decltype(box->value_)> CONST VOLATILE \
- REF>(box->value_), \
- std::forward<Args>(args)...); \
- } \
- }; \
- \
- template <typename T> \
- struct view_invoker { \
- static Ret invoke(data_accessor CONST VOLATILE* data, std::size_t, \
- Args... args) NOEXCEPT { \
- \
- auto ptr = static_cast<void CONST VOLATILE*>(data->ptr_); \
- return invocation::invoke(address_taker<T>::restore(ptr), \
- std::forward<Args>(args)...); \
- } \
- }; \
- \
- template <typename T> \
- using callable = T CONST VOLATILE REF; \
- \
- using arguments = identity<Args...>; \
- \
- using is_noexcept = is_noexcept_##NOEXCEPT; \
- \
- template <bool Throws> \
- struct empty_invoker { \
- static Ret invoke(data_accessor CONST VOLATILE* /*data*/, \
- std::size_t /*capacity*/, Args... /*args*/) NOEXCEPT { \
- throw_or_abort##NOEXCEPT(std::integral_constant<bool, Throws>{}); \
- } \
- }; \
- };
-
- FU2_DETAIL_EXPAND_QUALIFIERS(FU2_DEFINE_FUNCTION_TRAIT)
- #undef FU2_DEFINE_FUNCTION_TRAIT
-
- /// Deduces to the function pointer to the given signature
- template <typename Signature>
- using function_pointer_of = typename function_trait<Signature>::pointer_type;
-
- template <typename... Args>
- struct invoke_table;
-
- /// We optimize the vtable_t in case there is a single function overload
- template <typename First>
- struct invoke_table<First> {
- using type = function_pointer_of<First>;
-
- /// Return the function pointer itself
- template <std::size_t Index>
- static constexpr auto fetch(type pointer) noexcept {
- static_assert(Index == 0U, "The index should be 0 here!");
- return pointer;
- }
-
- /// Returns the thunk of an single overloaded callable
- template <typename T, bool IsInplace>
- static constexpr type get_invocation_table_of() noexcept {
- return &function_trait<First>::template internal_invoker<T,
- IsInplace>::invoke;
- }
- /// Returns the thunk of an single overloaded callable
- template <typename T>
- static constexpr type get_invocation_view_table_of() noexcept {
- return &function_trait<First>::template view_invoker<T>::invoke;
- }
- /// Returns the thunk of an empty single overloaded callable
- template <bool IsThrowing>
- static constexpr type get_empty_invocation_table() noexcept {
- return &function_trait<First>::template empty_invoker<IsThrowing>::invoke;
- }
- };
- /// We generate a table in case of multiple function overloads
- template <typename First, typename Second, typename... Args>
- struct invoke_table<First, Second, Args...> {
- using type =
- std::tuple<function_pointer_of<First>, function_pointer_of<Second>,
- function_pointer_of<Args>...> const*;
-
- /// Return the function pointer at the particular index
- template <std::size_t Index>
- static constexpr auto fetch(type table) noexcept {
- return std::get<Index>(*table);
- }
-
- /// The invocation vtable for a present object
- template <typename T, bool IsInplace>
- struct invocation_vtable : public std::tuple<function_pointer_of<First>,
- function_pointer_of<Second>,
- function_pointer_of<Args>...> {
- constexpr invocation_vtable() noexcept
- : std::tuple<function_pointer_of<First>, function_pointer_of<Second>,
- function_pointer_of<Args>...>(std::make_tuple(
- &function_trait<First>::template internal_invoker<
- T, IsInplace>::invoke,
- &function_trait<Second>::template internal_invoker<
- T, IsInplace>::invoke,
- &function_trait<Args>::template internal_invoker<
- T, IsInplace>::invoke...)) {
- }
- };
-
- /// Returns the thunk of an multi overloaded callable
- template <typename T, bool IsInplace>
- static type get_invocation_table_of() noexcept {
- static invocation_vtable<T, IsInplace> const table;
- return &table;
- }
-
- /// The invocation vtable for a present object
- template <typename T>
- struct invocation_view_vtable
- : public std::tuple<function_pointer_of<First>,
- function_pointer_of<Second>,
- function_pointer_of<Args>...> {
- constexpr invocation_view_vtable() noexcept
- : std::tuple<function_pointer_of<First>, function_pointer_of<Second>,
- function_pointer_of<Args>...>(std::make_tuple(
- &function_trait<First>::template view_invoker<T>::invoke,
- &function_trait<Second>::template view_invoker<T>::invoke,
- &function_trait<Args>::template view_invoker<T>::invoke...)) {
- }
- };
-
- /// Returns the thunk of an multi overloaded callable
- template <typename T>
- static type get_invocation_view_table_of() noexcept {
- static invocation_view_vtable<T> const table;
- return &table;
- }
-
- /// The invocation table for an empty wrapper
- template <bool IsThrowing>
- struct empty_vtable : public std::tuple<function_pointer_of<First>,
- function_pointer_of<Second>,
- function_pointer_of<Args>...> {
- constexpr empty_vtable() noexcept
- : std::tuple<function_pointer_of<First>, function_pointer_of<Second>,
- function_pointer_of<Args>...>(
- std::make_tuple(&function_trait<First>::template empty_invoker<
- IsThrowing>::invoke,
- &function_trait<Second>::template empty_invoker<
- IsThrowing>::invoke,
- &function_trait<Args>::template empty_invoker<
- IsThrowing>::invoke...)) {
- }
- };
-
- /// Returns the thunk of an multi single overloaded callable
- template <bool IsThrowing>
- static type get_empty_invocation_table() noexcept {
- static empty_vtable<IsThrowing> const table;
- return &table;
- }
- };
-
- template <std::size_t Index, typename Function, typename... Signatures>
- class operator_impl;
-
- #define FU2_DEFINE_FUNCTION_TRAIT(CONST, VOLATILE, NOEXCEPT, OVL_REF, REF) \
- template <std::size_t Index, typename Function, typename Ret, \
- typename... Args, typename Next, typename... Signatures> \
- class operator_impl<Index, Function, \
- Ret(Args...) CONST VOLATILE OVL_REF NOEXCEPT, Next, \
- Signatures...> \
- : operator_impl<Index + 1, Function, Next, Signatures...> { \
- \
- template <std::size_t, typename, typename...> \
- friend class operator_impl; \
- \
- protected: \
- operator_impl() = default; \
- ~operator_impl() = default; \
- operator_impl(operator_impl const&) = default; \
- operator_impl(operator_impl&&) = default; \
- operator_impl& operator=(operator_impl const&) = default; \
- operator_impl& operator=(operator_impl&&) = default; \
- \
- using operator_impl<Index + 1, Function, Next, Signatures...>::operator(); \
- \
- Ret operator()(Args... args) CONST VOLATILE OVL_REF NOEXCEPT { \
- auto parent = static_cast<Function CONST VOLATILE*>(this); \
- using erasure_t = std::decay_t<decltype(parent->erasure_)>; \
- \
- /* `std::decay_t<decltype(parent->erasure_)>` is a workaround for a */ \
- /* compiler regression of MSVC 16.3.1, see #29 for details. */ \
- return std::decay_t<decltype(parent->erasure_)>::template invoke<Index>( \
- static_cast<erasure_t CONST VOLATILE REF>(parent->erasure_), \
- std::forward<Args>(args)...); \
- } \
- }; \
- template <std::size_t Index, typename Config, typename Property, \
- typename Ret, typename... Args> \
- class operator_impl<Index, function<Config, Property>, \
- Ret(Args...) CONST VOLATILE OVL_REF NOEXCEPT> \
- : copyable<!Config::is_owning || Config::is_copyable> { \
- \
- template <std::size_t, typename, typename...> \
- friend class operator_impl; \
- \
- protected: \
- operator_impl() = default; \
- ~operator_impl() = default; \
- operator_impl(operator_impl const&) = default; \
- operator_impl(operator_impl&&) = default; \
- operator_impl& operator=(operator_impl const&) = default; \
- operator_impl& operator=(operator_impl&&) = default; \
- \
- Ret operator()(Args... args) CONST VOLATILE OVL_REF NOEXCEPT { \
- auto parent = \
- static_cast<function<Config, Property> CONST VOLATILE*>(this); \
- using erasure_t = std::decay_t<decltype(parent->erasure_)>; \
- \
- /* `std::decay_t<decltype(parent->erasure_)>` is a workaround for a */ \
- /* compiler regression of MSVC 16.3.1, see #29 for details. */ \
- return std::decay_t<decltype(parent->erasure_)>::template invoke<Index>( \
- static_cast<erasure_t CONST VOLATILE REF>(parent->erasure_), \
- std::forward<Args>(args)...); \
- } \
- };
-
- FU2_DETAIL_EXPAND_QUALIFIERS(FU2_DEFINE_FUNCTION_TRAIT)
- #undef FU2_DEFINE_FUNCTION_TRAIT
- } // namespace invocation_table
-
- namespace tables {
- /// Identifies the action which is dispatched on the erased object
- enum class opcode {
- op_move, ///< Move the object and set the vtable
- op_copy, ///< Copy the object and set the vtable
- op_destroy, ///< Destroy the object and reset the vtable
- op_weak_destroy, ///< Destroy the object without resetting the vtable
- op_fetch_empty, ///< Stores true or false into the to storage
- ///< to indicate emptiness
- };
-
- /// Abstraction for a vtable together with a command table
- /// TODO Add optimization for a single formal argument
- /// TODO Add optimization to merge both tables if the function is size
- /// optimized
- template <typename Property>
- class vtable;
- template <bool IsThrowing, bool HasStrongExceptGuarantee,
- typename... FormalArgs>
- class vtable<property<IsThrowing, HasStrongExceptGuarantee, FormalArgs...>> {
- using command_function_t = void (*)(vtable* /*this*/, opcode /*op*/,
- data_accessor* /*from*/,
- std::size_t /*from_capacity*/,
- data_accessor* /*to*/,
- std::size_t /*to_capacity*/);
-
- using invoke_table_t = invocation_table::invoke_table<FormalArgs...>;
-
- command_function_t cmd_;
- typename invoke_table_t::type vtable_;
-
- template <typename T>
- struct trait {
- static_assert(is_box<T>::value,
- "The trait must be specialized with a box!");
-
- /// The command table
- template <bool IsInplace>
- static void process_cmd(vtable* to_table, opcode op, data_accessor* from,
- std::size_t from_capacity, data_accessor* to,
- std::size_t to_capacity) {
-
- switch (op) {
- case opcode::op_move: {
- /// Retrieve the pointer to the object
- auto box = static_cast<T*>(retrieve<T>(
- std::integral_constant<bool, IsInplace>{}, from, from_capacity));
- assert(box && "The object must not be over aligned or null!");
-
- if (!IsInplace) {
- // Just swap both pointers if we allocated on the heap
- to->ptr_ = from->ptr_;
-
- #ifndef NDEBUG
- // We don't need to null the pointer since we know that
- // we don't own the data anymore through the vtable
- // which is set to empty.
- from->ptr_ = nullptr;
- #endif
-
- to_table->template set_allocated<T>();
-
- }
- // The object is allocated inplace
- else {
- construct(std::true_type{}, std::move(*box), to_table, to,
- to_capacity);
- box->~T();
- }
- return;
- }
- case opcode::op_copy: {
- auto box = static_cast<T const*>(retrieve<T>(
- std::integral_constant<bool, IsInplace>{}, from, from_capacity));
- assert(box && "The object must not be over aligned or null!");
-
- assert(std::is_copy_constructible<T>::value &&
- "The box is required to be copyable here!");
-
- // Try to allocate the object inplace
- construct(std::is_copy_constructible<T>{}, *box, to_table, to,
- to_capacity);
- return;
- }
- case opcode::op_destroy:
- case opcode::op_weak_destroy: {
-
- assert(!to && !to_capacity && "Arg overflow!");
- auto box = static_cast<T*>(retrieve<T>(
- std::integral_constant<bool, IsInplace>{}, from, from_capacity));
-
- if (IsInplace) {
- box->~T();
- } else {
- box_factory<T>::box_deallocate(box);
- }
-
- if (op == opcode::op_destroy) {
- to_table->set_empty();
- }
- return;
- }
- case opcode::op_fetch_empty: {
- write_empty(to, false);
- return;
- }
- }
-
- FU2_DETAIL_UNREACHABLE();
- }
-
- template <typename Box>
- static void
- construct(std::true_type /*apply*/, Box&& box, vtable* to_table,
- data_accessor* to,
- std::size_t to_capacity) noexcept(HasStrongExceptGuarantee) {
- // Try to allocate the object inplace
- void* storage = retrieve<T>(std::true_type{}, to, to_capacity);
- if (storage) {
- to_table->template set_inplace<T>();
- } else {
- // Allocate the object through the allocator
- to->ptr_ = storage =
- box_factory<std::decay_t<Box>>::box_allocate(std::addressof(box));
- to_table->template set_allocated<T>();
- }
- new (storage) T(std::forward<Box>(box));
- }
-
- template <typename Box>
- static void
- construct(std::false_type /*apply*/, Box&& /*box*/, vtable* /*to_table*/,
- data_accessor* /*to*/,
- std::size_t /*to_capacity*/) noexcept(HasStrongExceptGuarantee) {
- }
- };
-
- /// The command table
- static void empty_cmd(vtable* to_table, opcode op, data_accessor* /*from*/,
- std::size_t /*from_capacity*/, data_accessor* to,
- std::size_t /*to_capacity*/) {
-
- switch (op) {
- case opcode::op_move:
- case opcode::op_copy: {
- to_table->set_empty();
- break;
- }
- case opcode::op_destroy:
- case opcode::op_weak_destroy: {
- // Do nothing
- break;
- }
- case opcode::op_fetch_empty: {
- write_empty(to, true);
- break;
- }
- default: {
- FU2_DETAIL_UNREACHABLE();
- }
- }
- }
-
- public:
- vtable() noexcept = default;
-
- /// Initialize an object at the given position
- template <typename T>
- static void init(vtable& table, T&& object, data_accessor* to,
- std::size_t to_capacity) {
-
- trait<std::decay_t<T>>::construct(std::true_type{}, std::forward<T>(object),
- &table, to, to_capacity);
- }
-
- /// Moves the object at the given position
- void move(vtable& to_table, data_accessor* from, std::size_t from_capacity,
- data_accessor* to,
- std::size_t to_capacity) noexcept(HasStrongExceptGuarantee) {
- cmd_(&to_table, opcode::op_move, from, from_capacity, to, to_capacity);
- set_empty();
- }
-
- /// Destroys the object at the given position
- void copy(vtable& to_table, data_accessor const* from,
- std::size_t from_capacity, data_accessor* to,
- std::size_t to_capacity) const {
- cmd_(&to_table, opcode::op_copy, const_cast<data_accessor*>(from),
- from_capacity, to, to_capacity);
- }
-
- /// Destroys the object at the given position
- void destroy(data_accessor* from,
- std::size_t from_capacity) noexcept(HasStrongExceptGuarantee) {
- cmd_(this, opcode::op_destroy, from, from_capacity, nullptr, 0U);
- }
-
- /// Destroys the object at the given position without invalidating the
- /// vtable
- void
- weak_destroy(data_accessor* from,
- std::size_t from_capacity) noexcept(HasStrongExceptGuarantee) {
- cmd_(this, opcode::op_weak_destroy, from, from_capacity, nullptr, 0U);
- }
-
- /// Returns true when the vtable doesn't hold any erased object
- bool empty() const noexcept {
- data_accessor data;
- cmd_(nullptr, opcode::op_fetch_empty, nullptr, 0U, &data, 0U);
- return bool(data.inplace_storage_);
- }
-
- /// Invoke the function at the given index
- template <std::size_t Index, typename... Args>
- constexpr decltype(auto) invoke(Args&&... args) const {
- auto thunk = invoke_table_t::template fetch<Index>(vtable_);
- return thunk(std::forward<Args>(args)...);
- }
- /// Invoke the function at the given index
- template <std::size_t Index, typename... Args>
- constexpr decltype(auto) invoke(Args&&... args) const volatile {
- auto thunk = invoke_table_t::template fetch<Index>(vtable_);
- return thunk(std::forward<Args>(args)...);
- }
-
- template <typename T>
- void set_inplace() noexcept {
- using type = std::decay_t<T>;
- vtable_ = invoke_table_t::template get_invocation_table_of<type, true>();
- cmd_ = &trait<type>::template process_cmd<true>;
- }
-
- template <typename T>
- void set_allocated() noexcept {
- using type = std::decay_t<T>;
- vtable_ = invoke_table_t::template get_invocation_table_of<type, false>();
- cmd_ = &trait<type>::template process_cmd<false>;
- }
-
- void set_empty() noexcept {
- vtable_ = invoke_table_t::template get_empty_invocation_table<IsThrowing>();
- cmd_ = &empty_cmd;
- }
- };
- } // namespace tables
-
- /// A union which makes the pointer to the heap object share the
- /// same space with the internal capacity.
- /// The storage type is distinguished by multiple versions of the
- /// control and vtable.
- template <typename Capacity, typename = void>
- struct internal_capacity {
- /// We extend the union through a technique similar to the tail object hack
- typedef union {
- /// Tag to access the structure in a type-safe way
- data_accessor accessor_;
- /// The internal capacity we use to allocate in-place
- std::aligned_storage_t<Capacity::capacity, Capacity::alignment> capacity_;
- } type;
- };
- template <typename Capacity>
- struct internal_capacity<
- Capacity, std::enable_if_t<(Capacity::capacity < sizeof(void*))>> {
- typedef struct {
- /// Tag to access the structure in a type-safe way
- data_accessor accessor_;
- } type;
- };
-
- template <typename Capacity>
- class internal_capacity_holder {
- // Tag to access the structure in a type-safe way
- typename internal_capacity<Capacity>::type storage_;
-
- public:
- constexpr internal_capacity_holder() = default;
-
- FU2_DETAIL_CXX14_CONSTEXPR data_accessor* opaque_ptr() noexcept {
- return &storage_.accessor_;
- }
- constexpr data_accessor const* opaque_ptr() const noexcept {
- return &storage_.accessor_;
- }
- FU2_DETAIL_CXX14_CONSTEXPR data_accessor volatile*
- opaque_ptr() volatile noexcept {
- return &storage_.accessor_;
- }
- constexpr data_accessor const volatile* opaque_ptr() const volatile noexcept {
- return &storage_.accessor_;
- }
-
- static constexpr std::size_t capacity() noexcept {
- return sizeof(storage_);
- }
- };
-
- /// An owning erasure
- template <bool IsOwning /* = true*/, typename Config, typename Property>
- class erasure : internal_capacity_holder<typename Config::capacity> {
- template <bool, typename, typename>
- friend class erasure;
- template <std::size_t, typename, typename...>
- friend class operator_impl;
-
- using vtable_t = tables::vtable<Property>;
-
- vtable_t vtable_;
-
- public:
- /// Returns the capacity of this erasure
- static constexpr std::size_t capacity() noexcept {
- return internal_capacity_holder<typename Config::capacity>::capacity();
- }
-
- FU2_DETAIL_CXX14_CONSTEXPR erasure() noexcept {
- vtable_.set_empty();
- }
-
- FU2_DETAIL_CXX14_CONSTEXPR erasure(std::nullptr_t) noexcept {
- vtable_.set_empty();
- }
-
- FU2_DETAIL_CXX14_CONSTEXPR
- erasure(erasure&& right) noexcept(Property::is_strong_exception_guaranteed) {
- right.vtable_.move(vtable_, right.opaque_ptr(), right.capacity(),
- this->opaque_ptr(), capacity());
- }
-
- FU2_DETAIL_CXX14_CONSTEXPR erasure(erasure const& right) {
- right.vtable_.copy(vtable_, right.opaque_ptr(), right.capacity(),
- this->opaque_ptr(), capacity());
- }
-
- template <typename OtherConfig>
- FU2_DETAIL_CXX14_CONSTEXPR
- erasure(erasure<true, OtherConfig, Property> right) noexcept(
- Property::is_strong_exception_guaranteed) {
- right.vtable_.move(vtable_, right.opaque_ptr(), right.capacity(),
- this->opaque_ptr(), capacity());
- }
-
- template <typename T, typename Allocator = std::allocator<std::decay_t<T>>>
- FU2_DETAIL_CXX14_CONSTEXPR erasure(std::false_type /*use_bool_op*/,
- T&& callable,
- Allocator&& allocator = Allocator{}) {
- vtable_t::init(vtable_,
- type_erasure::make_box(
- std::integral_constant<bool, Config::is_copyable>{},
- std::forward<T>(callable),
- std::forward<Allocator>(allocator)),
- this->opaque_ptr(), capacity());
- }
- template <typename T, typename Allocator = std::allocator<std::decay_t<T>>>
- FU2_DETAIL_CXX14_CONSTEXPR erasure(std::true_type /*use_bool_op*/,
- T&& callable,
- Allocator&& allocator = Allocator{}) {
- if (bool(callable)) {
- vtable_t::init(vtable_,
- type_erasure::make_box(
- std::integral_constant<bool, Config::is_copyable>{},
- std::forward<T>(callable),
- std::forward<Allocator>(allocator)),
- this->opaque_ptr(), capacity());
- } else {
- vtable_.set_empty();
- }
- }
-
- ~erasure() {
- vtable_.weak_destroy(this->opaque_ptr(), capacity());
- }
-
- FU2_DETAIL_CXX14_CONSTEXPR erasure&
- operator=(std::nullptr_t) noexcept(Property::is_strong_exception_guaranteed) {
- vtable_.destroy(this->opaque_ptr(), capacity());
- return *this;
- }
-
- FU2_DETAIL_CXX14_CONSTEXPR erasure& operator=(erasure&& right) noexcept(
- Property::is_strong_exception_guaranteed) {
- vtable_.weak_destroy(this->opaque_ptr(), capacity());
- right.vtable_.move(vtable_, right.opaque_ptr(), right.capacity(),
- this->opaque_ptr(), capacity());
- return *this;
- }
-
- FU2_DETAIL_CXX14_CONSTEXPR erasure& operator=(erasure const& right) {
- vtable_.weak_destroy(this->opaque_ptr(), capacity());
- right.vtable_.copy(vtable_, right.opaque_ptr(), right.capacity(),
- this->opaque_ptr(), capacity());
- return *this;
- }
-
- template <typename OtherConfig>
- FU2_DETAIL_CXX14_CONSTEXPR erasure&
- operator=(erasure<true, OtherConfig, Property> right) noexcept(
- Property::is_strong_exception_guaranteed) {
- vtable_.weak_destroy(this->opaque_ptr(), capacity());
- right.vtable_.move(vtable_, right.opaque_ptr(), right.capacity(),
- this->opaque_ptr(), capacity());
- return *this;
- }
-
- template <typename T, typename Allocator = std::allocator<std::decay_t<T>>>
- void assign(std::false_type /*use_bool_op*/, T&& callable,
- Allocator&& allocator = {}) {
- vtable_.weak_destroy(this->opaque_ptr(), capacity());
- vtable_t::init(vtable_,
- type_erasure::make_box(
- std::integral_constant<bool, Config::is_copyable>{},
- std::forward<T>(callable),
- std::forward<Allocator>(allocator)),
- this->opaque_ptr(), capacity());
- }
-
- template <typename T, typename Allocator = std::allocator<std::decay_t<T>>>
- void assign(std::true_type /*use_bool_op*/, T&& callable,
- Allocator&& allocator = {}) {
- if (bool(callable)) {
- assign(std::false_type{}, std::forward<T>(callable),
- std::forward<Allocator>(allocator));
- } else {
- operator=(nullptr);
- }
- }
-
- /// Returns true when the erasure doesn't hold any erased object
- constexpr bool empty() const noexcept {
- return vtable_.empty();
- }
-
- /// Invoke the function of the erasure at the given index
- ///
- /// We define this out of class to be able to forward the qualified
- /// erasure correctly.
- template <std::size_t Index, typename Erasure, typename... Args>
- static constexpr decltype(auto) invoke(Erasure&& erasure, Args&&... args) {
- auto const capacity = erasure.capacity();
- return erasure.vtable_.template invoke<Index>(
- std::forward<Erasure>(erasure).opaque_ptr(), capacity,
- std::forward<Args>(args)...);
- }
- };
-
- // A non owning erasure
- template </*bool IsOwning = false, */ typename Config, bool IsThrowing,
- bool HasStrongExceptGuarantee, typename... Args>
- class erasure<false, Config,
- property<IsThrowing, HasStrongExceptGuarantee, Args...>> {
- template <bool, typename, typename>
- friend class erasure;
- template <std::size_t, typename, typename...>
- friend class operator_impl;
-
- using property_t = property<IsThrowing, HasStrongExceptGuarantee, Args...>;
-
- using invoke_table_t = invocation_table::invoke_table<Args...>;
- typename invoke_table_t::type invoke_table_;
-
- /// The internal pointer to the non owned object
- data_accessor view_;
-
- public:
- // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init)
- constexpr erasure() noexcept
- : invoke_table_(
- invoke_table_t::template get_empty_invocation_table<IsThrowing>()),
- view_(nullptr) {
- }
-
- // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init)
- constexpr erasure(std::nullptr_t) noexcept
- : invoke_table_(
- invoke_table_t::template get_empty_invocation_table<IsThrowing>()),
- view_(nullptr) {
- }
-
- // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init)
- constexpr erasure(erasure&& right) noexcept
- : invoke_table_(right.invoke_table_), view_(right.view_) {
- }
-
- constexpr erasure(erasure const& /*right*/) = default;
-
- template <typename OtherConfig>
- // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init)
- constexpr erasure(erasure<false, OtherConfig, property_t> right) noexcept
- : invoke_table_(right.invoke_table_), view_(right.view_) {
- }
-
- template <typename T>
- // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init)
- constexpr erasure(std::false_type /*use_bool_op*/, T&& object)
- : invoke_table_(invoke_table_t::template get_invocation_view_table_of<
- std::decay_t<T>>()),
- view_(address_taker<std::decay_t<T>>::take(std::forward<T>(object))) {
- }
- template <typename T>
- // NOLINTNEXTLINE(cppcoreguidlines-pro-type-member-init)
- FU2_DETAIL_CXX14_CONSTEXPR erasure(std::true_type use_bool_op, T&& object) {
- this->assign(use_bool_op, std::forward<T>(object));
- }
-
- ~erasure() = default;
-
- constexpr erasure&
- operator=(std::nullptr_t) noexcept(HasStrongExceptGuarantee) {
- invoke_table_ =
- invoke_table_t::template get_empty_invocation_table<IsThrowing>();
- view_.ptr_ = nullptr;
- return *this;
- }
-
- constexpr erasure& operator=(erasure&& right) noexcept {
- invoke_table_ = right.invoke_table_;
- view_ = right.view_;
- right = nullptr;
- return *this;
- }
-
- constexpr erasure& operator=(erasure const& /*right*/) = default;
-
- template <typename OtherConfig>
- constexpr erasure&
- operator=(erasure<true, OtherConfig, property_t> right) noexcept {
- invoke_table_ = right.invoke_table_;
- view_ = right.view_;
- return *this;
- }
-
- template <typename T>
- constexpr void assign(std::false_type /*use_bool_op*/, T&& callable) {
- invoke_table_ = invoke_table_t::template get_invocation_view_table_of<
- std::decay_t<T>>();
- view_.ptr_ =
- address_taker<std::decay_t<T>>::take(std::forward<T>(callable));
- }
- template <typename T>
- constexpr void assign(std::true_type /*use_bool_op*/, T&& callable) {
- if (bool(callable)) {
- assign(std::false_type{}, std::forward<T>(callable));
- } else {
- operator=(nullptr);
- }
- }
-
- /// Returns true when the erasure doesn't hold any erased object
- constexpr bool empty() const noexcept {
- return view_.ptr_ == nullptr;
- }
-
- template <std::size_t Index, typename Erasure, typename... T>
- static constexpr decltype(auto) invoke(Erasure&& erasure, T&&... args) {
- auto thunk = invoke_table_t::template fetch<Index>(erasure.invoke_table_);
- return thunk(&(erasure.view_), 0UL, std::forward<T>(args)...);
- }
- };
- } // namespace type_erasure
-
- /// Deduces to a true_type if the type T provides the given signature and the
- /// signature is noexcept correct callable.
- template <typename T, typename Signature,
- typename Trait =
- type_erasure::invocation_table::function_trait<Signature>>
- struct accepts_one
- : std::integral_constant<
- bool, invocation::can_invoke<typename Trait::template callable<T>,
- typename Trait::arguments>::value &&
- invocation::is_noexcept_correct<
- Trait::is_noexcept::value,
- typename Trait::template callable<T>,
- typename Trait::arguments>::value> {};
-
- /// Deduces to a true_type if the type T provides all signatures
- template <typename T, typename Signatures, typename = void>
- struct accepts_all : std::false_type {};
- template <typename T, typename... Signatures>
- struct accepts_all<
- T, identity<Signatures...>,
- void_t<std::enable_if_t<accepts_one<T, Signatures>::value>...>>
- : std::true_type {};
-
- /// Deduces to a true_type if the type T is implementing operator bool()
- /// or if the type is convertible to bool directly, this also implements an
- /// optimizations for function references `void(&)()` which are can never
- /// be null and for such a conversion to bool would never return false.
- #if defined(FU2_HAS_NO_EMPTY_PROPAGATION)
- template <typename T>
- struct use_bool_op : std::false_type {};
- #else
- template <typename T, typename = void>
- struct has_bool_op : std::false_type {};
- template <typename T>
- struct has_bool_op<T, void_t<decltype(bool(std::declval<T>()))>>
- : std::true_type {
- #ifndef NDEBUG
- static_assert(!std::is_pointer<T>::value,
- "Missing deduction for function pointer!");
- #endif
- };
-
- template <typename T>
- struct use_bool_op : has_bool_op<T> {};
-
- #define FU2_DEFINE_USE_OP_TRAIT(CONST, VOLATILE, NOEXCEPT) \
- template <typename Ret, typename... Args> \
- struct use_bool_op<Ret (*CONST VOLATILE)(Args...) NOEXCEPT> \
- : std::true_type {};
-
- FU2_DETAIL_EXPAND_CV(FU2_DEFINE_USE_OP_TRAIT)
- #undef FU2_DEFINE_USE_OP_TRAIT
-
- template <typename Ret, typename... Args>
- struct use_bool_op<Ret(Args...)> : std::false_type {};
-
- #if defined(FU2_HAS_CXX17_NOEXCEPT_FUNCTION_TYPE)
- template <typename Ret, typename... Args>
- struct use_bool_op<Ret(Args...) noexcept> : std::false_type {};
- #endif
- #endif // FU2_HAS_NO_EMPTY_PROPAGATION
-
- template <typename Config, typename T>
- struct assert_wrong_copy_assign {
- static_assert(!Config::is_owning || !Config::is_copyable ||
- std::is_copy_constructible<std::decay_t<T>>::value,
- "Can't wrap a non copyable object into a unique function!");
-
- using type = void;
- };
-
- template <bool IsStrongExceptGuaranteed, typename T>
- struct assert_no_strong_except_guarantee {
- static_assert(
- !IsStrongExceptGuaranteed ||
- (std::is_nothrow_move_constructible<T>::value &&
- std::is_nothrow_destructible<T>::value),
- "Can't wrap a object an object that has no strong exception guarantees "
- "if this is required by the wrapper!");
-
- using type = void;
- };
-
- /// SFINAES out if the given callable is not copyable correct to the left one.
- template <typename LeftConfig, typename RightConfig>
- using enable_if_copyable_correct_t =
- std::enable_if_t<(!LeftConfig::is_copyable || RightConfig::is_copyable)>;
-
- template <typename LeftConfig, typename RightConfig>
- using is_owning_correct =
- std::integral_constant<bool,
- (LeftConfig::is_owning == RightConfig::is_owning)>;
-
- /// SFINAES out if the given function2 is not owning correct to this one
- template <typename LeftConfig, typename RightConfig>
- using enable_if_owning_correct_t =
- std::enable_if_t<is_owning_correct<LeftConfig, RightConfig>::value>;
-
- template <typename Config, bool IsThrowing, bool HasStrongExceptGuarantee,
- typename... Args>
- class function<Config, property<IsThrowing, HasStrongExceptGuarantee, Args...>>
- : type_erasure::invocation_table::operator_impl<
- 0U,
- function<Config,
- property<IsThrowing, HasStrongExceptGuarantee, Args...>>,
- Args...> {
-
- template <typename, typename>
- friend class function;
-
- template <std::size_t, typename, typename...>
- friend class type_erasure::invocation_table::operator_impl;
-
- using property_t = property<IsThrowing, HasStrongExceptGuarantee, Args...>;
- using erasure_t =
- type_erasure::erasure<Config::is_owning, Config, property_t>;
-
- template <typename T>
- using enable_if_can_accept_all_t =
- std::enable_if_t<accepts_all<std::decay_t<T>, identity<Args...>>::value>;
-
- template <typename Function, typename = void>
- struct is_convertible_to_this : std::false_type {};
- template <typename RightConfig>
- struct is_convertible_to_this<
- function<RightConfig, property_t>,
- void_t<enable_if_copyable_correct_t<Config, RightConfig>,
- enable_if_owning_correct_t<Config, RightConfig>>>
- : std::true_type {};
-
- template <typename T>
- using enable_if_not_convertible_to_this =
- std::enable_if_t<!is_convertible_to_this<std::decay_t<T>>::value>;
-
- template <typename T>
- using enable_if_owning_t =
- std::enable_if_t<std::is_same<T, T>::value && Config::is_owning>;
-
- template <typename T>
- using assert_wrong_copy_assign_t =
- typename assert_wrong_copy_assign<Config, std::decay_t<T>>::type;
-
- template <typename T>
- using assert_no_strong_except_guarantee_t =
- typename assert_no_strong_except_guarantee<HasStrongExceptGuarantee,
- std::decay_t<T>>::type;
-
- erasure_t erasure_;
-
- public:
- /// Default constructor which empty constructs the function
- function() = default;
- ~function() = default;
-
- explicit FU2_DETAIL_CXX14_CONSTEXPR
- function(function const& /*right*/) = default;
- explicit FU2_DETAIL_CXX14_CONSTEXPR function(function&& /*right*/) = default;
-
- /// Copy construction from another copyable function
- template <typename RightConfig,
- std::enable_if_t<RightConfig::is_copyable>* = nullptr,
- enable_if_copyable_correct_t<Config, RightConfig>* = nullptr,
- enable_if_owning_correct_t<Config, RightConfig>* = nullptr>
- FU2_DETAIL_CXX14_CONSTEXPR
- function(function<RightConfig, property_t> const& right)
- : erasure_(right.erasure_) {
- }
-
- /// Move construction from another function
- template <typename RightConfig,
- enable_if_copyable_correct_t<Config, RightConfig>* = nullptr,
- enable_if_owning_correct_t<Config, RightConfig>* = nullptr>
- FU2_DETAIL_CXX14_CONSTEXPR function(function<RightConfig, property_t>&& right)
- : erasure_(std::move(right.erasure_)) {
- }
-
- /// Construction from a callable object which overloads the `()` operator
- template <typename T, //
- enable_if_not_convertible_to_this<T>* = nullptr,
- enable_if_can_accept_all_t<T>* = nullptr,
- assert_wrong_copy_assign_t<T>* = nullptr,
- assert_no_strong_except_guarantee_t<T>* = nullptr>
- FU2_DETAIL_CXX14_CONSTEXPR function(T&& callable)
- : erasure_(use_bool_op<unrefcv_t<T>>{}, std::forward<T>(callable)) {
- }
- template <typename T, typename Allocator, //
- enable_if_not_convertible_to_this<T>* = nullptr,
- enable_if_can_accept_all_t<T>* = nullptr,
- enable_if_owning_t<T>* = nullptr,
- assert_wrong_copy_assign_t<T>* = nullptr,
- assert_no_strong_except_guarantee_t<T>* = nullptr>
- FU2_DETAIL_CXX14_CONSTEXPR function(T&& callable, Allocator&& allocator)
- : erasure_(use_bool_op<unrefcv_t<T>>{}, std::forward<T>(callable),
- std::forward<Allocator>(allocator)) {
- }
-
- /// Empty constructs the function
- FU2_DETAIL_CXX14_CONSTEXPR function(std::nullptr_t np) : erasure_(np) {
- }
-
- function& operator=(function const& /*right*/) = default;
- function& operator=(function&& /*right*/) = default;
-
- /// Copy assigning from another copyable function
- template <typename RightConfig,
- std::enable_if_t<RightConfig::is_copyable>* = nullptr,
- enable_if_copyable_correct_t<Config, RightConfig>* = nullptr,
- enable_if_owning_correct_t<Config, RightConfig>* = nullptr>
- function& operator=(function<RightConfig, property_t> const& right) {
- erasure_ = right.erasure_;
- return *this;
- }
-
- /// Move assigning from another function
- template <typename RightConfig,
- enable_if_copyable_correct_t<Config, RightConfig>* = nullptr,
- enable_if_owning_correct_t<Config, RightConfig>* = nullptr>
- function& operator=(function<RightConfig, property_t>&& right) {
- erasure_ = std::move(right.erasure_);
- return *this;
- }
-
- /// Move assigning from a callable object
- template <typename T, // ...
- enable_if_not_convertible_to_this<T>* = nullptr,
- enable_if_can_accept_all_t<T>* = nullptr,
- assert_wrong_copy_assign_t<T>* = nullptr,
- assert_no_strong_except_guarantee_t<T>* = nullptr>
- function& operator=(T&& callable) {
- erasure_.assign(use_bool_op<unrefcv_t<T>>{}, std::forward<T>(callable));
- return *this;
- }
-
- /// Clears the function
- function& operator=(std::nullptr_t np) {
- erasure_ = np;
- return *this;
- }
-
- /// Returns true when the function is empty
- bool empty() const noexcept {
- return erasure_.empty();
- }
-
- /// Returns true when the function isn't empty
- explicit operator bool() const noexcept {
- return !empty();
- }
-
- /// Assigns a new target with an optional allocator
- template <typename T, typename Allocator = std::allocator<std::decay_t<T>>,
- enable_if_not_convertible_to_this<T>* = nullptr,
- enable_if_can_accept_all_t<T>* = nullptr,
- assert_wrong_copy_assign_t<T>* = nullptr,
- assert_no_strong_except_guarantee_t<T>* = nullptr>
- void assign(T&& callable, Allocator&& allocator = Allocator{}) {
- erasure_.assign(use_bool_op<unrefcv_t<T>>{}, std::forward<T>(callable),
- std::forward<Allocator>(allocator));
- }
-
- /// Swaps this function with the given function
- void swap(function& other) noexcept(HasStrongExceptGuarantee) {
- if (&other == this) {
- return;
- }
-
- function cache = std::move(other);
- other = std::move(*this);
- *this = std::move(cache);
- }
-
- /// Swaps the left function with the right one
- friend void swap(function& left,
- function& right) noexcept(HasStrongExceptGuarantee) {
- left.swap(right);
- }
-
- /// Calls the wrapped callable object
- using type_erasure::invocation_table::operator_impl<
- 0U, function<Config, property_t>, Args...>::operator();
- };
-
- template <typename Config, typename Property>
- bool operator==(function<Config, Property> const& f, std::nullptr_t) {
- return !bool(f);
- }
-
- template <typename Config, typename Property>
- bool operator!=(function<Config, Property> const& f, std::nullptr_t) {
- return bool(f);
- }
-
- template <typename Config, typename Property>
- bool operator==(std::nullptr_t, function<Config, Property> const& f) {
- return !bool(f);
- }
-
- template <typename Config, typename Property>
- bool operator!=(std::nullptr_t, function<Config, Property> const& f) {
- return bool(f);
- }
-
- // Default intended object size of the function
- using object_size = std::integral_constant<std::size_t, 32U>;
- } // namespace detail
- } // namespace abi_400
-
- /// Can be passed to function_base as template argument which causes
- /// the internal small buffer to be sized according to the given size,
- /// and aligned with the given alignment.
- template <std::size_t Capacity,
- std::size_t Alignment = alignof(std::max_align_t)>
- struct capacity_fixed {
- static constexpr std::size_t capacity = Capacity;
- static constexpr std::size_t alignment = Alignment;
- };
-
- /// Default capacity for small functor optimization
- struct capacity_default
- : capacity_fixed<detail::object_size::value - (2 * sizeof(void*))> {};
-
- /// Can be passed to function_base as template argument which causes
- /// the internal small buffer to be removed from the callable wrapper.
- /// The owning function_base will then allocate memory for every object
- /// it applies a type erasure on.
- struct capacity_none : capacity_fixed<0UL> {};
-
- /// Can be passed to function_base as template argument which causes
- /// the internal small buffer to be sized such that it can hold
- /// the given object without allocating memory for an applied type erasure.
- template <typename T>
- struct capacity_can_hold {
- static constexpr std::size_t capacity = sizeof(T);
- static constexpr std::size_t alignment = alignof(T);
- };
-
- /// An adaptable function wrapper base for arbitrary functional types.
- ///
- /// \tparam IsOwning Is true when the type erasure shall be owning the object.
- ///
- /// \tparam IsCopyable Defines whether the function is copyable or not
- ///
- /// \tparam Capacity Defines the internal capacity of the function
- /// for small functor optimization.
- /// The size of the whole function object will be the capacity
- /// plus the size of two pointers. If the capacity is zero,
- /// the size will increase through one additional pointer
- /// so the whole object has the size of 3 * sizeof(void*).
- /// The type which is passed to the Capacity template parameter
- /// shall provide a capacity and alignment member which
- /// looks like the following example:
- /// ```cpp
- /// struct my_capacity {
- /// static constexpr std::size_t capacity = sizeof(my_type);
- /// static constexpr std::size_t alignment = alignof(my_type);
- /// };
- /// ```
- ///
- /// \tparam IsThrowing Defines whether the function throws an exception on
- /// empty function call, `std::abort` is called otherwise.
- ///
- /// \tparam HasStrongExceptGuarantee Defines whether all objects satisfy the
- /// strong exception guarantees,
- /// which means the function type will satisfy
- /// the strong exception guarantees too.
- ///
- /// \tparam Signatures Defines the signature of the callable wrapper
- ///
- template <bool IsOwning, bool IsCopyable, typename Capacity, bool IsThrowing,
- bool HasStrongExceptGuarantee, typename... Signatures>
- using function_base = detail::function<
- detail::config<IsOwning, IsCopyable, Capacity>,
- detail::property<IsThrowing, HasStrongExceptGuarantee, Signatures...>>;
-
- /// An owning copyable function wrapper for arbitrary callable types.
- template <typename... Signatures>
- using function = function_base<true, true, capacity_default, //
- true, false, Signatures...>;
-
- /// An owning non copyable function wrapper for arbitrary callable types.
- template <typename... Signatures>
- using unique_function = function_base<true, false, capacity_default, //
- true, false, Signatures...>;
-
- /// A non owning copyable function wrapper for arbitrary callable types.
- template <typename... Signatures>
- using function_view = function_base<false, true, capacity_default, //
- true, false, Signatures...>;
-
- #if !defined(FU2_HAS_DISABLED_EXCEPTIONS)
- /// Exception type that is thrown when invoking empty function objects
- /// and exception support isn't disabled.
- ///
- /// Exception support is enabled if
- /// the template parameter 'Throwing' is set to true (default).
- ///
- /// This type will default to std::bad_function_call if the
- /// functional header is used, otherwise the library provides its own type.
- ///
- /// You may disable the inclusion of the functional header
- /// through defining `FU2_WITH_NO_FUNCTIONAL_HEADER`.
- ///
- using detail::type_erasure::invocation_table::bad_function_call;
- #endif
-
- /// Returns a callable object, which unifies all callable objects
- /// that were passed to this function.
- ///
- /// ```cpp
- /// auto overloaded = fu2::overload([](std::true_type) { return true; },
- /// [](std::false_type) { return false; });
- /// ```
- ///
- /// \param callables A pack of callable objects with arbitrary signatures.
- ///
- /// \returns A callable object which exposes the
- ///
- template <typename... T>
- constexpr auto overload(T&&... callables) {
- return detail::overloading::overload(std::forward<T>(callables)...);
- }
- } // namespace fu2
-
- #undef FU2_DETAIL_EXPAND_QUALIFIERS
- #undef FU2_DETAIL_EXPAND_QUALIFIERS_NOEXCEPT
- #undef FU2_DETAIL_EXPAND_CV
- #undef FU2_DETAIL_EXPAND_CV_NOEXCEPT
- #undef FU2_DETAIL_UNREACHABLE_INTRINSIC
- #undef FU2_DETAIL_TRAP
- #undef FU2_DETAIL_CXX14_CONSTEXPR
-
- #endif // FU2_INCLUDED_FUNCTION2_HPP_
|