aboutsummaryrefslogtreecommitdiff
path: root/src/DI
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2022-04-10 17:20:49 +0200
committerHampusM <hampus@hampusmat.com>2022-06-13 17:56:58 +0200
commitb36d072ad7a7b9c6e30fcb25d6bbb001a8393468 (patch)
tree7749fc877652bb2cf7bbd2b7c1fed5e3abe397fc /src/DI
parentb828d4799a84beb1afbd889e5c4cb939a8b3df78 (diff)
refactor: add factory class & make DI container return unique ptrs
Diffstat (limited to 'src/DI')
-rw-r--r--src/DI/alloc_functor.hpp84
-rw-r--r--src/DI/alloc_functor.tpp165
-rw-r--r--src/DI/allocation.hpp32
-rw-r--r--src/DI/allocation.tpp16
-rw-r--r--src/DI/auto_wirable.hpp12
-rw-r--r--src/DI/auto_wirable.tpp4
-rw-r--r--src/DI/compressed_pair.hpp188
-rw-r--r--src/DI/concepts.hpp4
-rw-r--r--src/DI/container.cpp2
-rw-r--r--src/DI/container.hpp23
-rw-r--r--src/DI/container.tpp26
-rw-r--r--src/DI/copyable_functor.hpp47
-rw-r--r--src/DI/copyable_functor.tpp105
-rw-r--r--src/DI/extra_concepts.hpp6
-rw-r--r--src/DI/factory.hpp189
-rw-r--r--src/DI/factory.tpp65
-rw-r--r--src/DI/function_wrapper.tpp4
-rw-r--r--src/DI/interfaces/copyable_functor.hpp41
-rw-r--r--src/DI/object_wrapper.hpp4
-rw-r--r--src/DI/object_wrapper.tpp3
-rw-r--r--src/DI/strip_signature.hpp103
-rw-r--r--src/DI/type_traits.hpp12
-rw-r--r--src/DI/value_functor.hpp65
-rw-r--r--src/DI/value_functor.tpp189
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);
+}