diff options
Diffstat (limited to 'src/DI')
-rw-r--r-- | src/DI/alloc_functor.hpp | 84 | ||||
-rw-r--r-- | src/DI/alloc_functor.tpp | 165 | ||||
-rw-r--r-- | src/DI/allocation.hpp | 32 | ||||
-rw-r--r-- | src/DI/allocation.tpp | 16 | ||||
-rw-r--r-- | src/DI/auto_wirable.hpp | 12 | ||||
-rw-r--r-- | src/DI/auto_wirable.tpp | 4 | ||||
-rw-r--r-- | src/DI/compressed_pair.hpp | 188 | ||||
-rw-r--r-- | src/DI/concepts.hpp | 4 | ||||
-rw-r--r-- | src/DI/container.cpp | 2 | ||||
-rw-r--r-- | src/DI/container.hpp | 23 | ||||
-rw-r--r-- | src/DI/container.tpp | 26 | ||||
-rw-r--r-- | src/DI/copyable_functor.hpp | 47 | ||||
-rw-r--r-- | src/DI/copyable_functor.tpp | 105 | ||||
-rw-r--r-- | src/DI/extra_concepts.hpp | 6 | ||||
-rw-r--r-- | src/DI/factory.hpp | 189 | ||||
-rw-r--r-- | src/DI/factory.tpp | 65 | ||||
-rw-r--r-- | src/DI/function_wrapper.tpp | 4 | ||||
-rw-r--r-- | src/DI/interfaces/copyable_functor.hpp | 41 | ||||
-rw-r--r-- | src/DI/object_wrapper.hpp | 4 | ||||
-rw-r--r-- | src/DI/object_wrapper.tpp | 3 | ||||
-rw-r--r-- | src/DI/strip_signature.hpp | 103 | ||||
-rw-r--r-- | src/DI/type_traits.hpp | 12 | ||||
-rw-r--r-- | src/DI/value_functor.hpp | 65 | ||||
-rw-r--r-- | src/DI/value_functor.tpp | 189 |
24 files changed, 1345 insertions, 44 deletions
diff --git a/src/DI/alloc_functor.hpp b/src/DI/alloc_functor.hpp new file mode 100644 index 0000000..ea40b22 --- /dev/null +++ b/src/DI/alloc_functor.hpp @@ -0,0 +1,84 @@ +#pragma once + +#include "DI/allocation.hpp" +#include "DI/compressed_pair.hpp" + +#include <memory> +#include <type_traits> +#include <utility> + +template <typename Return> +struct InvokeReturnWrapper +{ + template <typename... Args> + static auto call(Args &&...args) -> Return; +}; + +/** + * Holds a functor and a allocator. + */ +template <class Function, class Allocator, class FunctionSignature> +class AllocFunctor; + +template <class Function, class Allocator, class Return, class... Args> +class AllocFunctor<Function, Allocator, Return(Args...)> +{ +public: + using Target = Function; + using Alloc = Allocator; + + explicit AllocFunctor(Target &&function); + + explicit AllocFunctor(const Target &function, const Alloc &allocator); + + explicit AllocFunctor(const Target &function, Alloc &&allocator); + + explicit AllocFunctor(Target &&function, Alloc &&allocator); + + auto operator()(Args &&...args) -> Return; + + [[nodiscard]] auto target() const -> const Target &; + + [[nodiscard]] auto get_allocator() const -> const Alloc &; + + [[nodiscard]] auto clone() const -> AllocFunctor *; + + void destroy() noexcept; + + static void destroy_and_delete(AllocFunctor *functor); + +private: + CompressedPair<Function, Allocator> _function; +}; + +/** + * Holds a functor and a allocator. + */ +template <class Function, class FB> +class DefaultAllocFunctor; + +template <class Function, class Return, class... Args> +class DefaultAllocFunctor<Function, Return(Args...)> +{ +public: + using Target = Function; + + explicit DefaultAllocFunctor(Target &&function); + + explicit DefaultAllocFunctor(const Target &function); + + auto operator()(Args &&...args) -> Return; + + auto target() const -> const Target &; + + auto clone() const -> DefaultAllocFunctor *; + + void destroy() noexcept; + + static void destroy_and_delete(DefaultAllocFunctor *function); + +private: + Function _function; +}; + +#include "alloc_functor.tpp" diff --git a/src/DI/alloc_functor.tpp b/src/DI/alloc_functor.tpp new file mode 100644 index 0000000..d3f6946 --- /dev/null +++ b/src/DI/alloc_functor.tpp @@ -0,0 +1,165 @@ +#pragma once + +#include "alloc_functor.hpp" + +template <class Return> +template <class... Args> +auto InvokeReturnWrapper<Return>::call(Args &&...args) -> Return +{ + return std::invoke(std::forward<Args>(args)...); +} + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ALLOC_FUNCTOR_TEMPLATE \ + template <class Function, class Allocator, class Return, class... Args> + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define ALLOC_FUNCTOR AllocFunctor<Function, Allocator, Return(Args...)> + +ALLOC_FUNCTOR_TEMPLATE +ALLOC_FUNCTOR::AllocFunctor(Target &&function) + : _function(std::piecewise_construct, std::forward_as_tuple(std::move(function)), + std::forward_as_tuple()) +{ +} + +ALLOC_FUNCTOR_TEMPLATE +ALLOC_FUNCTOR::AllocFunctor(const Target &function, const Alloc &allocator) + : _function(std::piecewise_construct, std::forward_as_tuple(function), + std::forward_as_tuple(allocator)) +{ +} + +ALLOC_FUNCTOR_TEMPLATE +ALLOC_FUNCTOR::AllocFunctor(const Target &function, Alloc &&allocator) + : _function(std::piecewise_construct, std::forward_as_tuple(function), + std::forward_as_tuple(std::move(allocator))) +{ +} + +ALLOC_FUNCTOR_TEMPLATE +ALLOC_FUNCTOR::AllocFunctor(Target &&function, Alloc &&allocator) + : _function(std::piecewise_construct, std::forward_as_tuple(std::move(function)), + std::forward_as_tuple(std::move(allocator))) +{ +} + +ALLOC_FUNCTOR_TEMPLATE +auto ALLOC_FUNCTOR::operator()(Args &&...args) -> Return +{ + using Invoker = InvokeReturnWrapper<Return>; + + return Invoker::call(_function.first(), std::forward<Args>(args)...); +} + +ALLOC_FUNCTOR_TEMPLATE +auto ALLOC_FUNCTOR::target() const -> const Target & +{ + return _function.first(); +} + +ALLOC_FUNCTOR_TEMPLATE +auto ALLOC_FUNCTOR::get_allocator() const -> const Alloc & +{ + return _function.second(); +} + +ALLOC_FUNCTOR_TEMPLATE +auto ALLOC_FUNCTOR::clone() const -> AllocFunctor * +{ + using AllocTraits = std::allocator_traits<Alloc>; + + using AllocHelper = typename RebindAllocHelper<AllocTraits, AllocFunctor>::type; + + auto alloc_helper = AllocHelper(_function.second()); + + using Destructor = AllocDestructor<AllocHelper>; + + auto hold = std::unique_ptr<AllocFunctor, Destructor>(alloc_helper.allocate(1), + _Dp(alloc_helper, 1)); + + ::new (static_cast<void *>(hold.get())) + AllocFunctor(_function.first(), _Alloc(alloc_helper)); + + return hold.release(); +} + +ALLOC_FUNCTOR_TEMPLATE +void ALLOC_FUNCTOR::destroy() noexcept +{ + _function.~CompressedPair<Target, Alloc>(); +} + +ALLOC_FUNCTOR_TEMPLATE +void ALLOC_FUNCTOR::destroy_and_delete(AllocFunctor *functor) +{ + using AllocTraits = std::allocator_traits<Alloc>; + + using FunctorAlloc = typename RebindAllocHelper<AllocTraits, AllocFunctor>::type; + + auto functor_alloc = FunctorAlloc(functor->get_allocator()); + + functor->destroy(); + functor_alloc.deallocate(functor, 1); +} + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DEFAULT_ALLOC_FUNCTOR_TEMPLATE \ + template <class Function, class Return, class... Args> + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DEFAULT_ALLOC_FUNCTOR DefaultAllocFunctor<Function, Return(Args...)> + +DEFAULT_ALLOC_FUNCTOR_TEMPLATE +DEFAULT_ALLOC_FUNCTOR::DefaultAllocFunctor(Target &&function) + : _function(std::move(function)) +{ +} + +DEFAULT_ALLOC_FUNCTOR_TEMPLATE +DEFAULT_ALLOC_FUNCTOR::DefaultAllocFunctor(const Target &function) : _function(function) +{ +} + +DEFAULT_ALLOC_FUNCTOR_TEMPLATE +auto DEFAULT_ALLOC_FUNCTOR::operator()(Args &&...args) -> Return +{ + using Invoker = InvokeReturnWrapper<Return>; + + return Invoker::call(_function, std::forward<Args>(args)...); +} + +DEFAULT_ALLOC_FUNCTOR_TEMPLATE +auto DEFAULT_ALLOC_FUNCTOR::target() const -> const Target & +{ + return _function; +} + +DEFAULT_ALLOC_FUNCTOR_TEMPLATE +auto DEFAULT_ALLOC_FUNCTOR::clone() const -> DefaultAllocFunctor * +{ + std::allocator<DefaultAllocFunctor> allocator; + + DefaultAllocFunctor *functor_ptr = allocator.allocate(1); + + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + auto *res = ::new (static_cast<void *>(functor_ptr)) DefaultAllocFunctor(_function); + + return res; +} + +DEFAULT_ALLOC_FUNCTOR_TEMPLATE +void DEFAULT_ALLOC_FUNCTOR::destroy() noexcept +{ + _function.~_Target(); +} + +DEFAULT_ALLOC_FUNCTOR_TEMPLATE +void DEFAULT_ALLOC_FUNCTOR::destroy_and_delete(DefaultAllocFunctor *function) +{ + function->destroy(); + + std::allocator<DefaultAllocFunctor> allocator; + + allocator.deallocate(function, 1); +} diff --git a/src/DI/allocation.hpp b/src/DI/allocation.hpp new file mode 100644 index 0000000..ac0fa38 --- /dev/null +++ b/src/DI/allocation.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include <memory> + +template <class Traits, class Type> +struct RebindAllocHelper +{ + using type = typename Traits::template rebind_alloc<Type>; +}; + +template <class Allocator> +class AllocDestructor +{ + using _alloc_traits = std::allocator_traits<Allocator>; + +public: + using Pointer = typename _alloc_traits::pointer; + using Size = typename _alloc_traits::size_type; + + using pointer = Pointer; + using size = Size; + + AllocDestructor(Allocator &allocator, Size alloc_size) noexcept; + + void operator()(Pointer ptr) noexcept; + +private: + Allocator &_allocator; + Size _size; +}; + +#include "allocation.tpp" diff --git a/src/DI/allocation.tpp b/src/DI/allocation.tpp new file mode 100644 index 0000000..245ce99 --- /dev/null +++ b/src/DI/allocation.tpp @@ -0,0 +1,16 @@ +#pragma once + +#include "allocation.hpp" + +template <class Allocator> +AllocDestructor<Allocator>::AllocDestructor(Allocator &allocator, + Size alloc_size) noexcept + : _allocator(allocator), _size(alloc_size) +{ +} + +template <class Allocator> +void AllocDestructor<Allocator>::operator()(Pointer ptr) noexcept +{ + _alloc_traits::deallocate(_allocator, ptr, _size); +} diff --git a/src/DI/auto_wirable.hpp b/src/DI/auto_wirable.hpp index 54a291e..7c94074 100644 --- a/src/DI/auto_wirable.hpp +++ b/src/DI/auto_wirable.hpp @@ -4,18 +4,12 @@ #include <memory> -template <class Interface> -class IAutoWirable -{ -public: - static auto resolve() noexcept -> Interface; -}; - template <class Interface, class ObjectImpl, class... Dependencies> -class AutoWirable : public IAutoWirable<Interface> +class AutoWirable { public: - static auto resolve(const Container &container) noexcept -> std::shared_ptr<Interface>; + static auto resolve(const Container &container) noexcept + -> std::unique_ptr<Interface>; }; #include "auto_wirable.tpp" diff --git a/src/DI/auto_wirable.tpp b/src/DI/auto_wirable.tpp index 3b42cc4..0a9ca93 100644 --- a/src/DI/auto_wirable.tpp +++ b/src/DI/auto_wirable.tpp @@ -4,7 +4,7 @@ template <class Interface, class ObjectImpl, class... Dependencies> auto AutoWirable<Interface, ObjectImpl, Dependencies...>::resolve( - const Container &container) noexcept -> std::shared_ptr<Interface> + const Container &container) noexcept -> std::unique_ptr<Interface> { - return std::make_shared<ObjectImpl>(container.get<Dependencies>()...); + return std::make_unique<ObjectImpl>(container.get<Dependencies>()...); } diff --git a/src/DI/compressed_pair.hpp b/src/DI/compressed_pair.hpp new file mode 100644 index 0000000..6e05e29 --- /dev/null +++ b/src/DI/compressed_pair.hpp @@ -0,0 +1,188 @@ +#pragma once + +#include "DI/extra_concepts.hpp" + +#include <concepts> +#include <tuple> +#include <type_traits> +#include <utility> + +// Tag used to default initialize one or both of the pair's elements. +struct DefaultInitTag +{ +}; + +struct ValueInitTag +{ +}; + +template <size_t...> +struct TupleIndices +{ +}; + +template <class IdxType, IdxType... Values> +struct IntegerSequence +{ + template <template <class OIdxType, OIdxType...> class ToIndexSeq, class ToIndexType> + using Convert = ToIndexSeq<ToIndexType, Values...>; + + template <size_t Sp> + using ToTupleIndices = TupleIndices<(Values + Sp)...>; +}; + +template <size_t SizeOne, size_t SizeTwo> +using MakeIndices = + typename __make_integer_seq<IntegerSequence, size_t, + SizeOne - SizeTwo>::template ToTupleIndices<SizeTwo>; + +template <size_t SizeOne, size_t SizeTwo = 0> +requires(SizeTwo <= SizeOne) struct MakeTupleIndices +{ + using type = MakeIndices<SizeOne, SizeTwo>; +}; + +template <typename Value> +concept ValueCanBeEmptyBase = std::is_empty_v<Value> && !std::is_final_v<Value>; + +template <class Value, int Idx> +struct CompressedPairElement +{ + using ValueReference = Value &; + using ConstValueReference = const Value &; + + constexpr explicit CompressedPairElement(DefaultInitTag /*unused*/) {} + + constexpr explicit CompressedPairElement(ValueInitTag /*unused*/) : _value() {} + + template <class ElementValue> + requires std::same_as<CompressedPairElement, typename std::decay<ElementValue>::type> + // NOLINTNEXTLINE(bugprone-forwarding-reference-overload) + constexpr explicit CompressedPairElement(ElementValue &&value) + : _value(std::forward<ElementValue>(value)) + { + } + + template <class... Args, size_t... Indexes> + constexpr CompressedPairElement(std::piecewise_construct_t /*unused*/, + std::tuple<Args...> args, + TupleIndices<Indexes...> /*unused*/) + : _value(std::forward<Args>(std::get<Indexes>(args))...) + { + } + + auto get() noexcept -> ValueReference + { + return _value; + } + + [[nodiscard]] auto get() const noexcept -> ConstValueReference + { + return _value; + } + +private: + Value _value; +}; + +template <class Value, int Idx> +requires ValueCanBeEmptyBase<Value> +struct CompressedPairElement<Value, Idx> : private Value +{ + using ValueReference = Value &; + using ConstValueReference = const Value &; + + constexpr CompressedPairElement() = default; + + constexpr explicit CompressedPairElement(DefaultInitTag /*unused*/) {} + + constexpr explicit CompressedPairElement(ValueInitTag /*unused*/) : Value() {} + + template <class ElementValue> + requires std::same_as<CompressedPairElement, typename std::decay<ElementValue>::type> + // NOLINTNEXTLINE(bugprone-forwarding-reference-overload) + constexpr explicit CompressedPairElement(ElementValue &&value) + : Value(std::forward<ElementValue>(value)) + { + } + + template <class... Args, size_t... Indexes> + constexpr CompressedPairElement(std::piecewise_construct_t /*unused*/, + std::tuple<Args...> args, + TupleIndices<Indexes...> /*unused*/) + : Value(std::forward<Args>(std::get<Indexes>(args))...) + { + } + + auto get() noexcept -> ValueReference + { + return *this; + } + + [[nodiscard]] auto get() const noexcept -> ConstValueReference + { + return *this; + } +}; + +template <class ValueOne, class ValueTwo> +requires NotSameAs<ValueOne, ValueTwo> +class CompressedPair : private CompressedPairElement<ValueOne, 0>, + private CompressedPairElement<ValueTwo, 1> +{ +public: + using BaseOne = CompressedPairElement<ValueOne, 0>; + using BaseTwo = CompressedPairElement<ValueTwo, 1>; + + template <typename> + requires std::default_initializable<ValueOne> && std::default_initializable<ValueTwo> + constexpr CompressedPair() : BaseOne(ValueInitTag()), BaseTwo(ValueInitTag()) {} + + template <class FirstValue, class SecondValue> + constexpr CompressedPair(FirstValue &&first_value, SecondValue &&second_value) + : BaseOne(std::forward<FirstValue>(first_value)), + BaseTwo(std::forward<SecondValue>(second_value)) + { + } + + template <class... ArgsOne, class... ArgsTwo> + constexpr CompressedPair(std::piecewise_construct_t piecewise_construct, + std::tuple<ArgsOne...> first_args, + std::tuple<ArgsTwo...> second_args) + : BaseOne(piecewise_construct, std::move(first_args), + typename MakeTupleIndices<sizeof...(ArgsOne)>::type()), + BaseTwo(piecewise_construct, std::move(second_args), + typename MakeTupleIndices<sizeof...(ArgsTwo)>::type()) + { + } + + auto first() noexcept -> typename BaseOne::ValueReference + { + return static_cast<BaseOne &>(*this).get(); + } + + [[nodiscard]] auto first() const noexcept -> typename BaseOne::ConstValueReference + { + return static_cast<BaseOne const &>(*this).get(); + } + + auto second() noexcept -> typename BaseTwo::ValueReference + { + return static_cast<BaseTwo &>(*this).get(); + } + + [[nodiscard]] auto second() const noexcept -> typename BaseTwo::ConstValueReference + { + return static_cast<BaseTwo const &>(*this).get(); + } + + constexpr static auto get_first_base(CompressedPair *pair) noexcept -> BaseOne * + { + return static_cast<BaseOne *>(pair); + } + + constexpr static auto get_second_base(CompressedPair *pair) noexcept -> BaseTwo * + { + return static_cast<BaseTwo *>(pair); + } +}; diff --git a/src/DI/concepts.hpp b/src/DI/concepts.hpp index 12be546..4b2c7f9 100644 --- a/src/DI/concepts.hpp +++ b/src/DI/concepts.hpp @@ -5,7 +5,7 @@ #include <type_traits> template <typename Type> -concept Function = is_func_v<Type>; +concept Abstract = std::is_abstract_v<Type>; template <typename Type> -concept Abstract = std::is_abstract_v<Type>; +concept IsFactory = is_factory_v<Type>; diff --git a/src/DI/container.cpp b/src/DI/container.cpp index 4b86186..1b5dc55 100644 --- a/src/DI/container.cpp +++ b/src/DI/container.cpp @@ -1,6 +1,6 @@ #include "container.hpp" -void Container::add(BaseObjectType type, const Ptr<IGenericWrapper> &wrapper) noexcept +void Container::add(BaseObjectType type, const WrapperPtr<IGenericWrapper> &wrapper) noexcept { _bindings.insert({type, wrapper}); } diff --git a/src/DI/container.hpp b/src/DI/container.hpp index 0c0155c..d5f52b8 100644 --- a/src/DI/container.hpp +++ b/src/DI/container.hpp @@ -3,9 +3,11 @@ #include "DI/interfaces/wrapper.hpp" #include "DI/concepts.hpp" +#include "DI/factory.hpp" #include "DI/object_type.hpp" #include <concepts> +#include <functional> #include <memory> #include <unordered_map> @@ -18,10 +20,12 @@ public: explicit BindingBuilder(Container *container) noexcept; template <typename Impl> - requires std::derived_from<Impl, Interface> + requires Abstract<Interface> && std::derived_from<Impl, Interface> void to() noexcept; - void to_factory(Interface func) noexcept; + template <typename FactoryFunc> + requires IsFactory<Interface> && std::constructible_from<Interface, FactoryFunc> + void to_factory(FactoryFunc factory) noexcept; private: Container *_container; @@ -33,23 +37,24 @@ public: Container() noexcept = default; template <typename Type> - using Ptr = std::shared_ptr<Type>; + using WrapperPtr = std::shared_ptr<Type>; template <class Interface> auto bind() noexcept -> BindingBuilder<Interface>; template <class Interface> requires Abstract<Interface> - auto get() const noexcept -> Ptr<Interface>; + auto get() const noexcept -> std::unique_ptr<Interface>; - template <typename Interface> - requires Function<Interface> - auto get() const noexcept -> Interface; + template <typename AFactory> + requires IsFactory<AFactory> + auto get() const noexcept -> AFactory; - void add(BaseObjectType type, const Ptr<IGenericWrapper> &wrapper) noexcept; + void add(BaseObjectType type, const WrapperPtr<IGenericWrapper> &wrapper) noexcept; private: - std::unordered_map<BaseObjectType, Ptr<IGenericWrapper>, ObjectTypeHasher> _bindings; + std::unordered_map<BaseObjectType, WrapperPtr<IGenericWrapper>, ObjectTypeHasher> + _bindings; }; #include "container.tpp" diff --git a/src/DI/container.tpp b/src/DI/container.tpp index 54b09c6..13397bc 100644 --- a/src/DI/container.tpp +++ b/src/DI/container.tpp @@ -15,23 +15,25 @@ BindingBuilder<Interface>::BindingBuilder(Container *container) noexcept template <typename Interface> template <typename Impl> -requires std::derived_from<Impl, Interface> +requires Abstract<Interface> && std::derived_from<Impl, Interface> void BindingBuilder<Interface>::to() noexcept { using Wrapper = ObjectWrapper<Interface, Impl>; - auto wrapper = Container::Ptr<Wrapper>(new Wrapper(*_container)); + auto wrapper = Container::WrapperPtr<Wrapper>(new Wrapper(*_container)); _container->add(ObjectType<Interface>(), std::dynamic_pointer_cast<IGenericWrapper>(wrapper)); } template <typename Interface> -void BindingBuilder<Interface>::to_factory(Interface func) noexcept +template <typename FactoryFunc> +requires IsFactory<Interface> && std::constructible_from<Interface, FactoryFunc> +void BindingBuilder<Interface>::to_factory(FactoryFunc factory) noexcept { using Wrapper = FunctionWrapper<Interface>; - auto wrapper = Container::Ptr<Wrapper>(new Wrapper(func)); + auto wrapper = Container::WrapperPtr<Wrapper>(new Wrapper(factory)); _container->add(ObjectType<Interface>(), std::dynamic_pointer_cast<IGenericWrapper>(wrapper)); @@ -45,7 +47,7 @@ auto Container::bind() noexcept -> BindingBuilder<Interface> template <typename Interface> requires Abstract<Interface> -auto Container::get() const noexcept -> Ptr<Interface> +auto Container::get() const noexcept -> std::unique_ptr<Interface> { ObjectType<Interface> interface_type; @@ -57,18 +59,18 @@ auto Container::get() const noexcept -> Ptr<Interface> exit(EXIT_FAILURE); } - auto wrapper = - std::dynamic_pointer_cast<IWrapper<Ptr<Interface>>>(_bindings.at(interface_type)); + auto wrapper = std::dynamic_pointer_cast<IWrapper<std::unique_ptr<Interface>>>( + _bindings.at(interface_type)); return wrapper->get(); } -template <typename Interface> -requires Function<Interface> -auto Container::get() const noexcept -> Interface +template <typename AFactory> +requires IsFactory<AFactory> +auto Container::get() const noexcept -> AFactory { - auto wrapper = std::dynamic_pointer_cast<IWrapper<Interface>>( - _bindings.at(ObjectType<Interface>())); + auto wrapper = std::dynamic_pointer_cast<IWrapper<AFactory>>( + _bindings.at(ObjectType<AFactory>())); return wrapper->get(); } diff --git a/src/DI/copyable_functor.hpp b/src/DI/copyable_functor.hpp new file mode 100644 index 0000000..905da03 --- /dev/null +++ b/src/DI/copyable_functor.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include "DI/interfaces/copyable_functor.hpp" + +#include "DI/alloc_functor.hpp" + +#include <typeinfo> + +/** + * A copyable function object. + */ +template <class Function, class Allocator, class FunctionSignature> +class CopyableFunctor; + +template <class Function, class Allocator, class Return, class... Args> +class CopyableFunctor<Function, Allocator, Return(Args...)> + : public ICopyableFunctor<Return(Args...)> +{ +public: + explicit CopyableFunctor(Function &&function); + + explicit CopyableFunctor(const Function &function, const Allocator &allocator); + + explicit CopyableFunctor(const Function &function, Allocator &&allocator); + + explicit CopyableFunctor(Function &&function, Allocator &&allocator); + + auto operator()(Args &&...args) -> Return override; + + auto clone() const -> ICopyableFunctor<Return(Args...)> * override; + + void clone(ICopyableFunctor<Return(Args...)> *functor) const override; + + void destroy() noexcept override; + + void destroy_deallocate() noexcept override; + + [[nodiscard]] auto target(const std::type_info &type_info) const noexcept -> const + void * override; + + [[nodiscard]] auto target_type() const noexcept -> const std::type_info & override; + +private: + AllocFunctor<Function, Allocator, Return(Args...)> _functor; +}; + +#include "copyable_functor.tpp" diff --git a/src/DI/copyable_functor.tpp b/src/DI/copyable_functor.tpp new file mode 100644 index 0000000..9895397 --- /dev/null +++ b/src/DI/copyable_functor.tpp @@ -0,0 +1,105 @@ +#pragma once + +#include "copyable_functor.hpp" + +#include <memory> +#include <utility> + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define COPYABLE_FUNCTOR_TEMPLATE \ + template <class Function, class Allocator, class Return, class... Args> + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define COPYABLE_FUNCTOR CopyableFunctor<Function, Allocator, Return(Args...)> + +COPYABLE_FUNCTOR_TEMPLATE +COPYABLE_FUNCTOR::CopyableFunctor(Function &&function) : _functor(std::move(function)) {} + +COPYABLE_FUNCTOR_TEMPLATE +COPYABLE_FUNCTOR::CopyableFunctor(const Function &function, const Allocator &allocator) + : _functor(function, allocator) +{ +} + +COPYABLE_FUNCTOR_TEMPLATE +COPYABLE_FUNCTOR::CopyableFunctor(const Function &function, Allocator &&allocator) + : _functor(function, std::move(allocator)) +{ +} + +COPYABLE_FUNCTOR_TEMPLATE +COPYABLE_FUNCTOR::CopyableFunctor(Function &&function, Allocator &&allocator) + : _functor(std::move(function), std::move(allocator)) +{ +} + +COPYABLE_FUNCTOR_TEMPLATE +auto COPYABLE_FUNCTOR::operator()(Args &&...args) -> Return +{ + return _functor(std::forward<Args>(args)...); +} + +COPYABLE_FUNCTOR_TEMPLATE +auto COPYABLE_FUNCTOR::clone() const -> ICopyableFunctor<Return(Args...)> * +{ + using AllocTraits = std::allocator_traits<Allocator>; + + using AllocHelper = typename RebindAllocHelper<AllocTraits, CopyableFunctor>::type; + + auto alloc_helper = AllocHelper(_functor.get_allocator()); + + using Destructor = AllocDestructor<AllocHelper>; + + auto hold = std::unique_ptr<CopyableFunctor, Destructor>(alloc_helper.allocate(1), + Destructor(alloc_helper, 1)); + + ::new (static_cast<void *>(hold.get())) + CopyableFunctor(_functor.target(), Allocator(alloc_helper)); + + return hold.release(); +} + +COPYABLE_FUNCTOR_TEMPLATE +void COPYABLE_FUNCTOR::clone(ICopyableFunctor<Return(Args...)> *functor) const +{ + ::new (static_cast<void *>(functor)) + CopyableFunctor(_functor.target(), _functor.get_allocator()); +} + +COPYABLE_FUNCTOR_TEMPLATE +void COPYABLE_FUNCTOR::destroy() noexcept +{ + _functor.destroy(); +} + +COPYABLE_FUNCTOR_TEMPLATE +void COPYABLE_FUNCTOR::destroy_deallocate() noexcept +{ + using AllocTraits = std::allocator_traits<Allocator>; + + using AllocHelper = typename RebindAllocHelper<AllocTraits, CopyableFunctor>::type; + + auto alloc_helper = AllocHelper(_functor.get_allocator()); + + _functor.destroy(); + + alloc_helper.deallocate(this, 1); +} + +COPYABLE_FUNCTOR_TEMPLATE +auto COPYABLE_FUNCTOR::target(const std::type_info &type_info) const noexcept -> const + void * +{ + if (type_info == typeid(Function)) + { + return std::addressof(_functor.target()); + } + + return nullptr; +} + +COPYABLE_FUNCTOR_TEMPLATE +auto COPYABLE_FUNCTOR::target_type() const noexcept -> const std::type_info & +{ + return typeid(Function); +} diff --git a/src/DI/extra_concepts.hpp b/src/DI/extra_concepts.hpp new file mode 100644 index 0000000..45180c6 --- /dev/null +++ b/src/DI/extra_concepts.hpp @@ -0,0 +1,6 @@ +#pragma once + +#include <type_traits> + +template <typename FirstType, typename SecondType> +concept NotSameAs = !std::is_same_v<FirstType, SecondType>; diff --git a/src/DI/factory.hpp b/src/DI/factory.hpp new file mode 100644 index 0000000..13d0eb5 --- /dev/null +++ b/src/DI/factory.hpp @@ -0,0 +1,189 @@ +#pragma once + +#include "DI/interfaces/copyable_functor.hpp" + +#include "DI/alloc_functor.hpp" +#include "DI/allocation.hpp" +#include "DI/copyable_functor.hpp" +#include "DI/extra_concepts.hpp" +#include "DI/strip_signature.hpp" +#include "DI/value_functor.hpp" + +#include <type_traits> +#include <utility> + +template <class> +class Factory; + +template <class Return> +// NOLINTNEXTLINE(readability-identifier-naming) +struct maybe_derive_from_unary_function +{ +}; + +template <class Return, class Arg> +struct maybe_derive_from_unary_function<Return(Arg)> + : public std::unary_function<Arg, Return> +{ +}; + +template <class Return> +// NOLINTNEXTLINE(readability-identifier-naming) +struct maybe_derive_from_binary_function +{ +}; + +template <class Return, class ArgOne, class ArgTwo> +struct maybe_derive_from_binary_function<Return(ArgOne, ArgTwo)> + : public std::binary_function<ArgOne, ArgTwo, Return> +{ +}; + +template <class Function> +auto not_null(Function const & /*unused*/) -> bool +{ + return true; +} + +template <class Function> +auto not_null(Function *function_ptr) -> bool +{ + return function_ptr; +} + +template <class Return, class Class> +auto not_null(Return Class::*method_ptr) -> bool +{ + return method_ptr; +} + +template <class Function> +auto not_null(Factory<Function> const &factory) -> bool +{ + return !!factory; +} + +template <class Type> +// NOLINTNEXTLINE(readability-identifier-naming) +struct uncvref +{ + using type = + typename std::remove_cv<typename std::remove_reference<Type>::type>::type; +}; + +template <class Type> +// NOLINTNEXTLINE(readability-identifier-naming) +using uncvref_t = typename uncvref<Type>::type; + +template <class Tp, class Up, class = void> +// NOLINTNEXTLINE(readability-identifier-naming) +struct is_core_convertible : public std::false_type +{ +}; + +template <class Tp, class Up> +struct is_core_convertible< + Tp, Up, decltype(static_cast<void (*)(Up)>(0)(static_cast<Tp (*)()>(0)()))> + : public std::true_type +{ +}; + +template <class Tp, class Up> +constexpr bool is_core_convertible_v = is_core_convertible<Tp, Up>::value; + +template <typename Function, typename Return, typename... Args> +concept Callable = !std::is_same_v<uncvref_t<Function>, Factory<Return(Args...)>> && + std::is_invocable_v<Function, Args...> && + is_core_convertible_v<std::invoke_result_t<Function, Args...>, Return>; + +template <class Return, class... Args> +class Factory<Return(Args...)> : public maybe_derive_from_unary_function<Return(Args...)>, + public maybe_derive_from_binary_function<Return(Args...)> +{ +public: + using Result = Return; + + Factory() noexcept = default; + + explicit Factory(std::nullptr_t) noexcept {} + + Factory(const Factory &factory); + + Factory(Factory &&factory) noexcept; + + template <class Function> + requires Callable<Function, Return, Args...> + // NOLINTNEXTLINE(google-explicit-constructor) + Factory(Function function) : _functor(std::move(function)) + { + } + + auto operator=(const Factory &factory) = delete; + + auto operator=(Factory &&factory) noexcept -> Factory &; + + auto operator=(std::nullptr_t) noexcept -> Factory &; + + ~Factory(); + + explicit operator bool() const noexcept; + + // deleted overloads close possible hole in the type system + template <class RhsReturn, class... RhsArgs> + auto operator==(const Factory<RhsReturn(RhsArgs...)> &) const -> bool = delete; + + template <class RhsReturn, class... RhsArgs> + auto operator!=(const Factory<RhsReturn(RhsArgs...)> &) const -> bool = delete; + + auto operator()(Args... args) const -> Return; + + [[nodiscard]] auto target_type() const noexcept -> const std::type_info &; + + template <typename Tp> + auto target() noexcept -> Tp *; + + template <typename Tp> + auto target() const noexcept -> const Tp *; + +private: + using ValFunctor = ValueFunctor<Return(Args...)>; + + ValFunctor _functor; +}; + +template <class Return, class... Args> +Factory(Return (*)(Args...)) -> Factory<Return(Args...)>; + +template <class Function, + class Stripped = strip_signature_t<decltype(&Function::operator())>> +Factory(Function) -> Factory<Stripped>; + +template <class Return, class... Args> +inline auto operator==(const Factory<Return(Args...)> &factory, std::nullptr_t) noexcept + -> bool +{ + return !factory; +} + +template <class Return, class... Args> +inline auto operator==(std::nullptr_t, const Factory<Return(Args...)> &factory) noexcept + -> bool +{ + return !factory; +} + +template <class Return, class... Args> +inline auto operator!=(const Factory<Return(Args...)> &factory, std::nullptr_t) noexcept + -> bool +{ + return static_cast<bool>(factory); +} + +template <class Return, class... Args> +inline auto operator!=(std::nullptr_t, const Factory<Return(Args...)> &factory) noexcept + -> bool +{ + return static_cast<bool>(factory); +} + +#include "factory.tpp" diff --git a/src/DI/factory.tpp b/src/DI/factory.tpp new file mode 100644 index 0000000..38b9c00 --- /dev/null +++ b/src/DI/factory.tpp @@ -0,0 +1,65 @@ +#pragma once + +#include "factory.hpp" + +template <class Return, class... Args> +Factory<Return(Args...)>::Factory(const Factory &factory) : _functor(factory._functor) +{ +} + +template <class Return, class... Args> +Factory<Return(Args...)>::Factory(Factory &&factory) noexcept + : _functor(std::move(factory._functor)) +{ +} + +template <class Return, class... Args> +auto Factory<Return(Args...)>::operator=(Factory &&factory) noexcept + -> Factory<Return(Args...)> & +{ + _functor = std::move(factory._functor); + return *this; +} + +template <class Return, class... Args> +auto Factory<Return(Args...)>::operator=(std::nullptr_t) noexcept + -> Factory<Return(Args...)> & +{ + _functor = nullptr; + return *this; +} + +template <class Return, class... Args> +Factory<Return(Args...)>::~Factory() = default; + +template <class Return, class... Args> +Factory<Return(Args...)>::operator bool() const noexcept +{ + return static_cast<bool>(_functor); +} + +template <class Return, class... Args> +auto Factory<Return(Args...)>::operator()(Args... args) const -> Return +{ + return _functor(std::forward<Args>(args)...); +} + +template <class Return, class... Args> +auto Factory<Return(Args...)>::target_type() const noexcept -> const std::type_info & +{ + return _functor.target_type(); +} + +template <class Return, class... Args> +template <typename Tp> +auto Factory<Return(Args...)>::target() noexcept -> Tp * +{ + return static_cast<Tp *>(_functor.template target<Tp>()); +} + +template <class Return, class... Args> +template <typename Tp> +auto Factory<Return(Args...)>::target() const noexcept -> const Tp * +{ + return _functor.template target<Tp>(); +} diff --git a/src/DI/function_wrapper.tpp b/src/DI/function_wrapper.tpp index 6a9c317..d672a49 100644 --- a/src/DI/function_wrapper.tpp +++ b/src/DI/function_wrapper.tpp @@ -1,9 +1,11 @@ #pragma once +#include <utility> + #include "function_wrapper.hpp" template <class Interface> -FunctionWrapper<Interface>::FunctionWrapper(Interface func) noexcept : _func(func) +FunctionWrapper<Interface>::FunctionWrapper(Interface func) noexcept : _func(std::move(func)) { } diff --git a/src/DI/interfaces/copyable_functor.hpp b/src/DI/interfaces/copyable_functor.hpp new file mode 100644 index 0000000..46b7588 --- /dev/null +++ b/src/DI/interfaces/copyable_functor.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include <typeinfo> + +/** + * A copyable function object. + */ +template <class Function> +class ICopyableFunctor; + +template <class Return, class... Args> +class ICopyableFunctor<Return(Args...)> +{ +public: + ICopyableFunctor() = default; + + ICopyableFunctor(const ICopyableFunctor &) = delete; + + ICopyableFunctor(ICopyableFunctor &&) = delete; + + virtual ~ICopyableFunctor() = default; + + [[nodiscard]] virtual auto clone() const -> ICopyableFunctor * = 0; + + virtual void clone(ICopyableFunctor *) const = 0; + + virtual void destroy() noexcept = 0; + + virtual void destroy_deallocate() noexcept = 0; + + virtual auto operator()(Args &&...) -> Return = 0; + + auto operator=(const ICopyableFunctor &) = delete; + + auto operator=(ICopyableFunctor &&) = delete; + + [[nodiscard]] virtual auto target(const std::type_info &) const noexcept -> const + void * = 0; + + [[nodiscard]] virtual auto target_type() const noexcept -> const std::type_info & = 0; +}; diff --git a/src/DI/object_wrapper.hpp b/src/DI/object_wrapper.hpp index 28121cb..6433bc2 100644 --- a/src/DI/object_wrapper.hpp +++ b/src/DI/object_wrapper.hpp @@ -6,12 +6,12 @@ #include <memory> template <class Interface, class ObjectImpl> -class ObjectWrapper : public IWrapper<std::shared_ptr<Interface>> +class ObjectWrapper : public IWrapper<std::unique_ptr<Interface>> { public: explicit ObjectWrapper(const Container &container) noexcept : _container(container) {} - [[nodiscard]] auto get() const noexcept -> std::shared_ptr<Interface> override; + [[nodiscard]] auto get() const noexcept -> std::unique_ptr<Interface> override; private: const Container &_container; diff --git a/src/DI/object_wrapper.tpp b/src/DI/object_wrapper.tpp index 5334247..1811db8 100644 --- a/src/DI/object_wrapper.tpp +++ b/src/DI/object_wrapper.tpp @@ -3,7 +3,8 @@ #include "object_wrapper.hpp" template <class Interface, class ObjectImpl> -auto ObjectWrapper<Interface, ObjectImpl>::get() const noexcept -> std::shared_ptr<Interface> +auto ObjectWrapper<Interface, ObjectImpl>::get() const noexcept + -> std::unique_ptr<Interface> { return ObjectImpl::resolve(_container); } diff --git a/src/DI/strip_signature.hpp b/src/DI/strip_signature.hpp new file mode 100644 index 0000000..9d20a87 --- /dev/null +++ b/src/DI/strip_signature.hpp @@ -0,0 +1,103 @@ +#pragma once + +template <class Function> +struct strip_signature; + +template <class Return, class Class, class... Args> +struct strip_signature<Return (Class::*)(Args...)> +{ + using type = Return(Args...); +}; + +template <class Return, class Class, class... Args> +struct strip_signature<Return (Class::*)(Args...) const> +{ + using type = Return(Args...); +}; + +template <class Return, class Class, class... Args> +struct strip_signature<Return (Class::*)(Args...) volatile> +{ + using type = Return(Args...); +}; + +template <class Return, class Class, class... Args> +struct strip_signature<Return (Class::*)(Args...) const volatile> +{ + using type = Return(Args...); +}; + +template <class Return, class Class, class... Args> +struct strip_signature<Return (Class::*)(Args...) &> +{ + using type = Return(Args...); +}; + +template <class Return, class Class, class... Args> +struct strip_signature<Return (Class::*)(Args...) const &> +{ + using type = Return(Args...); +}; + +template <class Return, class Class, class... Args> +struct strip_signature<Return (Class::*)(Args...) volatile &> +{ + using type = Return(Args...); +}; + +template <class Return, class Class, class... Args> +struct strip_signature<Return (Class::*)(Args...) const volatile &> +{ + using type = Return(Args...); +}; + +template <class Return, class Class, class... Args> +struct strip_signature<Return (Class::*)(Args...) noexcept> +{ + using type = Return(Args...); +}; + +template <class Return, class Class, class... Args> +struct strip_signature<Return (Class::*)(Args...) const noexcept> +{ + using type = Return(Args...); +}; + +template <class Return, class Class, class... Args> +struct strip_signature<Return (Class::*)(Args...) volatile noexcept> +{ + using type = Return(Args...); +}; + +template <class Return, class Class, class... Args> +struct strip_signature<Return (Class::*)(Args...) const volatile noexcept> +{ + using type = Return(Args...); +}; + +template <class Return, class Class, class... Args> +struct strip_signature<Return (Class::*)(Args...) &noexcept> +{ + using type = Return(Args...); +}; + +template <class Return, class Class, class... Args> +struct strip_signature<Return (Class::*)(Args...) const &noexcept> +{ + using type = Return(Args...); +}; + +template <class Return, class Class, class... Args> +struct strip_signature<Return (Class::*)(Args...) volatile &noexcept> +{ + using type = Return(Args...); +}; + +template <class Return, class Class, class... Args> +struct strip_signature<Return (Class::*)(Args...) const volatile &noexcept> +{ + using type = Return(Args...); +}; + +template <class Function> +using strip_signature_t = typename strip_signature<Function>::type; diff --git a/src/DI/type_traits.hpp b/src/DI/type_traits.hpp index e4b6640..6847c4f 100644 --- a/src/DI/type_traits.hpp +++ b/src/DI/type_traits.hpp @@ -1,16 +1,18 @@ #pragma once +#include "DI/factory.hpp" + #include <type_traits> -template <typename NotAFunc> -struct is_func : public std::false_type // NOLINT(readability-identifier-naming) +template <typename NotAFunction> +struct is_factory : public std::false_type // NOLINT(readability-identifier-naming) { }; template <typename Return, typename... Args> -struct is_func<Return (*)(Args...)> : public std::true_type +struct is_factory<Factory<Return(Args...)>> : public std::true_type { }; -template <typename PossiblyFunc> -inline constexpr bool is_func_v = is_func<PossiblyFunc>::value; +template <typename PossiblyFunction> +inline constexpr bool is_factory_v = is_factory<PossiblyFunction>::value; diff --git a/src/DI/value_functor.hpp b/src/DI/value_functor.hpp new file mode 100644 index 0000000..1553af9 --- /dev/null +++ b/src/DI/value_functor.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include "DI/interfaces/copyable_functor.hpp" + +#include "DI/copyable_functor.hpp" +#include "DI/extra_concepts.hpp" + +#include <memory> +#include <type_traits> + +/** + * Creates a value-type from a copyable functor. + */ +template <class Function> +class ValueFunctor; + +template <class Return, class... Args> +class ValueFunctor<Return(Args...)> +{ + using TargetFunctor = ICopyableFunctor<Return(Args...)>; + +public: + ValueFunctor() noexcept; + + template <class Function> + requires NotSameAs<std::decay_t<Function>, ValueFunctor> + // NOLINTNEXTLINE(bugprone-forwarding-reference-overload) + explicit ValueFunctor(Function &&function) + : ValueFunctor(std::forward<Function>(function), std::allocator<Function>()) + { + } + + template <class Function, class Allocator> + ValueFunctor(Function &&function, const Allocator &allocator); + + ValueFunctor(const ValueFunctor &val_functor); + + ValueFunctor(ValueFunctor &&val_functor) noexcept; + + ~ValueFunctor(); + + auto operator=(const ValueFunctor &val_functor) noexcept = delete; + + auto operator=(ValueFunctor &&val_functor) noexcept -> ValueFunctor &; + + auto operator=(std::nullptr_t) -> ValueFunctor &; + + auto operator()(Args &&...args) const -> Return; + + explicit operator bool() const noexcept; + + [[nodiscard]] auto target_type() const noexcept -> const std::type_info &; + + template <typename Target> + auto target() const noexcept -> const Target *; + +private: + typename std::aligned_storage<3 * sizeof(void *)>::type _buf{}; + + TargetFunctor *_functor; + + static auto _as_copyable_functor(void *function_ptr) -> TargetFunctor *; +}; + +#include "value_functor.tpp" diff --git a/src/DI/value_functor.tpp b/src/DI/value_functor.tpp new file mode 100644 index 0000000..3665f18 --- /dev/null +++ b/src/DI/value_functor.tpp @@ -0,0 +1,189 @@ +#pragma once + +#include "value_functor.hpp" + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define VALUE_FUNCTOR_TEMPLATE template <class Return, class... Args> + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define VALUE_FUNCTOR ValueFunctor<Return(Args...)> + +VALUE_FUNCTOR_TEMPLATE +VALUE_FUNCTOR::ValueFunctor() noexcept : _functor(nullptr) {} + +VALUE_FUNCTOR_TEMPLATE +template <class Function, class Allocator> +VALUE_FUNCTOR::ValueFunctor(Function &&function, const Allocator &allocator) + : _functor(nullptr) +{ + using AllocTraits = std::allocator_traits<Allocator>; + + using Functor = CopyableFunctor<Function, Allocator, Return(Args...)>; + + using FunctorAlloc = typename RebindAllocHelper<AllocTraits, Functor>::type; + + if (not_null(function)) + { + auto functor_alloc = FunctorAlloc(allocator); + + if (sizeof(Functor) <= sizeof(_buf) && + std::is_nothrow_copy_constructible<Function>::value && + std::is_nothrow_copy_constructible<FunctorAlloc>::value) + { + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + _functor = ::new (static_cast<void *>(&_buf)) + Functor(std::forward<Function>(function), Allocator(functor_alloc)); + } + else + { + using Destructor = AllocDestructor<FunctorAlloc>; + + auto hold = std::unique_ptr<TargetFunctor, Destructor>( + functor_alloc.allocate(1), Destructor(functor_alloc, 1)); + + ::new (static_cast<void *>(hold.get())) + Functor(std::forward<Function>(function), Allocator(allocator)); + + _functor = hold.release(); + } + } +} + +VALUE_FUNCTOR_TEMPLATE +VALUE_FUNCTOR::ValueFunctor(const ValueFunctor &val_functor) +{ + if (val_functor._functor == nullptr) + { + _functor = nullptr; + } + else if (static_cast<void *>(val_functor._functor) == &val_functor._buf) + { + _functor = _as_copyable_functor(&_buf); + val_functor._functor->clone(_functor); + } + else + { + _functor = val_functor._functor->clone(); + } +} + +VALUE_FUNCTOR_TEMPLATE +VALUE_FUNCTOR::ValueFunctor(ValueFunctor &&val_functor) noexcept +{ + if (val_functor._functor == nullptr) + { + _functor = nullptr; + } + else if (static_cast<void *>(val_functor._functor) == &val_functor._buf) + { + _functor = _as_copyable_functor(&_buf); + val_functor._functor->clone(_functor); + } + else + { + _functor = val_functor._functor; + val_functor._functor = nullptr; + } +} + +VALUE_FUNCTOR_TEMPLATE +VALUE_FUNCTOR::~ValueFunctor() +{ + if (static_cast<void *>(_functor) == &_buf) + { + _functor->destroy(); + } + else if (_functor) + { + _functor->destroy_deallocate(); + } +} + +VALUE_FUNCTOR_TEMPLATE +auto VALUE_FUNCTOR::operator=(ValueFunctor &&val_functor) noexcept -> ValueFunctor & +{ + *this = nullptr; + + if (val_functor._functor == nullptr) + { + _functor = nullptr; + } + else if (static_cast<void *>(val_functor._functor) == &val_functor._buf) + { + _functor = _as_copyable_functor(&_buf); + val_functor._functor->clone(_functor); + } + else + { + _functor = val_functor._functor; + val_functor._functor = nullptr; + } + + return *this; +} + +VALUE_FUNCTOR_TEMPLATE +auto VALUE_FUNCTOR::operator=(std::nullptr_t) -> ValueFunctor & +{ + TargetFunctor *old_functor = _functor; + + _functor = nullptr; + + if (static_cast<void *>(old_functor) == &_buf) + { + old_functor->destroy(); + } + else if (old_functor) + { + old_functor->destroy_deallocate(); + } + + return *this; +} + +VALUE_FUNCTOR_TEMPLATE +auto VALUE_FUNCTOR::operator()(Args &&...args) const -> Return +{ + if (_functor == nullptr) + { + std::abort(); + } + + return (*_functor)(std::forward<Args>(args)...); +} + +VALUE_FUNCTOR_TEMPLATE +VALUE_FUNCTOR::operator bool() const noexcept +{ + return _functor != nullptr; +} + +VALUE_FUNCTOR_TEMPLATE +auto VALUE_FUNCTOR::target_type() const noexcept -> const std::type_info & +{ + if (_functor == nullptr) + { + return typeid(void); + } + + return _functor->target_type(); +} + +VALUE_FUNCTOR_TEMPLATE +template <typename Target> +auto VALUE_FUNCTOR::target() const noexcept -> const Target * +{ + if (_functor == nullptr) + { + return nullptr; + } + + return static_cast<const Target *>(_functor->target(typeid(Target))); +} + +VALUE_FUNCTOR_TEMPLATE +auto VALUE_FUNCTOR::_as_copyable_functor(void *function_ptr) -> TargetFunctor * +{ + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + return reinterpret_cast<TargetFunctor *>(function_ptr); +} |