diff options
34 files changed, 1397 insertions, 93 deletions
diff --git a/.clang-tidy b/.clang-tidy index 9c93a1f..9c19cf2 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -7,7 +7,8 @@ Checks: ' modernize-*, bugprone-*, performance-*, - readability-*' + readability-*, + -cppcoreguidelines-pro-type-union-access' WarningsAsErrors: '*' HeaderFilterRegex: '\/src\/' AnalyzeTemporaryDtors: false 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); +} diff --git a/src/bootstrap.cpp b/src/bootstrap.cpp index 276b73a..22fe472 100644 --- a/src/bootstrap.cpp +++ b/src/bootstrap.cpp @@ -31,22 +31,12 @@ #include "randomization/generator.hpp" #include "randomization/seed_generator.hpp" -#include "DI/concepts.hpp" -#include "util/function.hpp" +#include "DI/factory.hpp" -#include <concepts> #include <memory> #include <random> #include <string_view> -template <typename Interface, typename Impl, typename... Params> -requires Abstract<Interface> && std::derived_from<Impl, Interface> && - std::constructible_from<Impl, Params...> -auto construct_as_interface(Params &&...parameters) -> std::shared_ptr<Interface> -{ - return std::dynamic_pointer_cast<Interface>(std::make_shared<Impl>(parameters...)); -} - auto bootstrap() noexcept -> Container { auto container = Container(); @@ -58,69 +48,63 @@ auto bootstrap() noexcept -> Container container.bind<IWindow>().to<Window>(); container.bind<ISeedGenerator>().to<SeedGenerator>(); - container.bind<IGameFactory>().to_factory(normalize_lambda( + container.bind<IGameFactory>().to_factory( [&container](const std::shared_ptr<IWindow> &window, const std::shared_ptr<IScene> &scene, const std::shared_ptr<ICursorController> &cursor_controller) { - auto statusline = + std::shared_ptr<IStatusLine> statusline = container.get<IStatusLineFactory>()(cursor_controller, window); - auto generation_tracker = container.get<IGenerationTrackerFactory>()(true); + std::shared_ptr<IGenerationTracker> generation_tracker = + container.get<IGenerationTrackerFactory>()(true); - auto status_updater = + std::shared_ptr<IStatusUpdater> status_updater = container.get<IStatusUpdaterFactory>()(statusline, generation_tracker); - return construct_as_interface<IGame, Game>(window, scene, cursor_controller, - statusline, generation_tracker, - status_updater); - })); + return std::make_unique<Game>(window, scene, cursor_controller, statusline, + generation_tracker, status_updater); + }); container.bind<IRandomNumberGeneratorFactory>().to_factory( [](const uint32_t &seed) { - return construct_as_interface<IRandomNumberGenerator, RandomNumberGenerator>( - seed); + return std::make_unique<RandomNumberGenerator>(seed); }); container.bind<IMatrixFactory<std::string_view>>().to_factory( [](const Bounds &bounds) { - return construct_as_interface<IMatrix<std::string_view>, - Matrix<std::string_view>>(bounds); + return std::make_unique<Matrix<std::string_view>>(bounds); }); - container.bind<ISceneFactory>().to_factory(normalize_lambda( + container.bind<ISceneFactory>().to_factory( [&container](const std::shared_ptr<ICursorController> &cursor_controller, const std::shared_ptr<IWindow> &window) { auto matrix_factory = container.get<IMatrixFactory<std::string_view>>(); - return construct_as_interface<IScene, Scene>(matrix_factory, - cursor_controller, window); - })); + return std::make_unique<Scene>(matrix_factory, cursor_controller, window); + }); container.bind<IStatusLineFactory>().to_factory( [](const std::shared_ptr<ICursorController> &cursor_controller, const std::shared_ptr<IWindow> &window) { - return construct_as_interface<IStatusLine, StatusLine>(cursor_controller, - window); + return std::make_unique<StatusLine>(cursor_controller, window); }); container.bind<IStatusUpdaterFactory>().to_factory( [](const std::shared_ptr<IStatusLine> &statusline, const std::shared_ptr<IGenerationTracker> &generation_tracker) { - return construct_as_interface<IStatusUpdater, StatusUpdater>( - statusline, generation_tracker); + return std::make_unique<StatusUpdater>(statusline, generation_tracker); }); container.bind<IGenerationTrackerFactory>().to_factory( [](bool is_paused) { - return construct_as_interface<IGenerationTracker, GenerationTracker>( - is_paused); + return std::make_unique<GenerationTracker>(is_paused); }); return container; diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index c988c33..a05fed9 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -9,8 +9,8 @@ CLIGameEngine::CLIGameEngine(IGameFactory game_factory, ISceneFactory scene_fact std::shared_ptr<IInputHandler> input_handler, std::shared_ptr<ICursorController> cursor_controller, std::shared_ptr<IWindow> window) noexcept - : _game_factory(game_factory), - _scene_factory(scene_factory), + : _game_factory(std::move(game_factory)), + _scene_factory(std::move(scene_factory)), _input_handler(std::move(input_handler)), _cursor_controller(std::move(cursor_controller)), _window(std::move(window)) @@ -19,7 +19,7 @@ CLIGameEngine::CLIGameEngine(IGameFactory game_factory, ISceneFactory scene_fact void CLIGameEngine::start() noexcept { - auto scene = _scene_factory(_cursor_controller, _window); + std::shared_ptr<IScene> scene = _scene_factory(_cursor_controller, _window); scene->enter(); _input_handler->enter_raw_mode(); @@ -29,7 +29,7 @@ void CLIGameEngine::start() noexcept game->on_start(); std::atexit(normalize_lambda( - [this, scene, game]() + [this, scene, &game]() { scene->leave(); _input_handler->leave_raw_mode(); diff --git a/src/interfaces/game.hpp b/src/interfaces/game.hpp index 46d79e4..0f5c895 100644 --- a/src/interfaces/game.hpp +++ b/src/interfaces/game.hpp @@ -5,6 +5,8 @@ #include "interfaces/scene.hpp" #include "interfaces/window.hpp" +#include "DI/factory.hpp" + #include <memory> #include <unordered_map> @@ -24,6 +26,12 @@ public: -> std::unordered_map<char, std::shared_ptr<ICommand>> = 0; }; -using IGameFactory = std::shared_ptr<IGame> (*)( +/* +using IGameFactory = std::unique_ptr<IGame> (*)( const std::shared_ptr<IWindow> &window, const std::shared_ptr<IScene> &scene, const std::shared_ptr<ICursorController> &cursor_controller); + */ + +using IGameFactory = Factory<std::unique_ptr<IGame>( + const std::shared_ptr<IWindow> &window, const std::shared_ptr<IScene> &scene, + const std::shared_ptr<ICursorController> &cursor_controller)>; diff --git a/src/interfaces/generation_tracker.hpp b/src/interfaces/generation_tracker.hpp index fcd2426..fd2c130 100644 --- a/src/interfaces/generation_tracker.hpp +++ b/src/interfaces/generation_tracker.hpp @@ -1,5 +1,7 @@ #pragma once +#include "DI/factory.hpp" + #include <cstdint> #include <memory> @@ -16,4 +18,5 @@ public: virtual void set_is_paused(bool is_paused) noexcept = 0; }; -using IGenerationTrackerFactory = std::shared_ptr<IGenerationTracker> (*)(bool is_paused); +using IGenerationTrackerFactory = + Factory<std::unique_ptr<IGenerationTracker>(bool is_paused)>; diff --git a/src/interfaces/matrix.hpp b/src/interfaces/matrix.hpp index edcf935..7fea003 100644 --- a/src/interfaces/matrix.hpp +++ b/src/interfaces/matrix.hpp @@ -1,8 +1,8 @@ #pragma once +#include "DI/factory.hpp" #include "engine/data/bounds.hpp" #include "engine/data/vector2.hpp" - #include "engine/matrix_iterator.hpp" #include <memory> @@ -39,4 +39,4 @@ public: }; template <typename Element> -using IMatrixFactory = std::shared_ptr<IMatrix<Element>> (*)(const Bounds &bounds); +using IMatrixFactory = Factory<std::unique_ptr<IMatrix<Element>>(const Bounds &bounds)>; diff --git a/src/interfaces/randomization.hpp b/src/interfaces/randomization.hpp index 8b1631c..4abad1d 100644 --- a/src/interfaces/randomization.hpp +++ b/src/interfaces/randomization.hpp @@ -1,5 +1,7 @@ #pragma once +#include "DI/factory.hpp" + #include <memory> // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions) @@ -28,4 +30,4 @@ public: }; using IRandomNumberGeneratorFactory = - std::shared_ptr<IRandomNumberGenerator> (*)(const uint32_t &seed); + Factory<std::unique_ptr<IRandomNumberGenerator>(const uint32_t &seed)>; diff --git a/src/interfaces/scene.hpp b/src/interfaces/scene.hpp index 3960985..0a51b6e 100644 --- a/src/interfaces/scene.hpp +++ b/src/interfaces/scene.hpp @@ -4,6 +4,8 @@ #include "interfaces/matrix.hpp" #include "interfaces/window.hpp" +#include "DI/factory.hpp" + #include <memory> // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions) @@ -20,6 +22,6 @@ public: -> const std::shared_ptr<IMatrix<std::string_view>> & = 0; }; -using ISceneFactory = std::shared_ptr<IScene> (*)( +using ISceneFactory = Factory<std::unique_ptr<IScene>( const std::shared_ptr<ICursorController> &cursor_controller, - const std::shared_ptr<IWindow> &window); + const std::shared_ptr<IWindow> &window)>; diff --git a/src/interfaces/status_updater.hpp b/src/interfaces/status_updater.hpp index 2770d5e..e5081ef 100644 --- a/src/interfaces/status_updater.hpp +++ b/src/interfaces/status_updater.hpp @@ -4,6 +4,7 @@ #include "interfaces/statusline.hpp" #include "interfaces/subscriber.hpp" +#include "DI/factory.hpp" #include "engine/data/vector2.hpp" #include <memory> @@ -15,6 +16,6 @@ public: void update(const Vector2 &context) noexcept override = 0; }; -using IStatusUpdaterFactory = std::shared_ptr<IStatusUpdater> (*)( +using IStatusUpdaterFactory = Factory<std::unique_ptr<IStatusUpdater>( const std::shared_ptr<IStatusLine> &statusline, - const std::shared_ptr<IGenerationTracker> &generation_tracker); + const std::shared_ptr<IGenerationTracker> &generation_tracker)>; diff --git a/src/interfaces/statusline.hpp b/src/interfaces/statusline.hpp index f07c1a0..bbd8e4a 100644 --- a/src/interfaces/statusline.hpp +++ b/src/interfaces/statusline.hpp @@ -3,6 +3,8 @@ #include "interfaces/cursor.hpp" #include "interfaces/window.hpp" +#include "DI/factory.hpp" + #include <string_view> enum StatusLineSection @@ -23,6 +25,6 @@ public: const std::string_view &str) noexcept = 0; }; -using IStatusLineFactory = std::shared_ptr<IStatusLine> (*)( +using IStatusLineFactory = Factory<std::unique_ptr<IStatusLine>( const std::shared_ptr<ICursorController> &cursor_controller, - const std::shared_ptr<IWindow> &window); + const std::shared_ptr<IWindow> &window)>; |