aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-format19
-rw-r--r--.clang-tidy44
-rw-r--r--.gitignore2
-rw-r--r--CMakeLists.txt15
-rw-r--r--README.md1
-rw-r--r--include/yacppdic/auto_wirable.hpp20
-rw-r--r--include/yacppdic/concepts.hpp16
-rw-r--r--include/yacppdic/container.hpp65
-rw-r--r--include/yacppdic/detail/auto_wirable-impl.hpp16
-rw-r--r--include/yacppdic/detail/container-impl.hpp94
-rw-r--r--include/yacppdic/detail/factory-impl.hpp70
-rw-r--r--include/yacppdic/detail/internal/alloc_destructor-impl.hpp23
-rw-r--r--include/yacppdic/detail/internal/alloc_destructor.hpp37
-rw-r--r--include/yacppdic/detail/internal/compressed_pair-impl.hpp46
-rw-r--r--include/yacppdic/detail/internal/compressed_pair.hpp199
-rw-r--r--include/yacppdic/detail/internal/functor/alloc_functor-impl.hpp186
-rw-r--r--include/yacppdic/detail/internal/functor/alloc_functor.hpp89
-rw-r--r--include/yacppdic/detail/internal/functor/copyable_functor-impl.hpp112
-rw-r--r--include/yacppdic/detail/internal/functor/copyable_functor.hpp52
-rw-r--r--include/yacppdic/detail/internal/functor/value_functor-impl.hpp196
-rw-r--r--include/yacppdic/detail/internal/functor/value_functor.hpp70
-rw-r--r--include/yacppdic/detail/internal/interfaces/copyable_functor.hpp46
-rw-r--r--include/yacppdic/detail/internal/misc_concepts.hpp11
-rw-r--r--include/yacppdic/detail/internal/strip_signature.hpp108
-rw-r--r--include/yacppdic/detail/internal/type_utils.hpp35
-rw-r--r--include/yacppdic/detail/internal/wrapper/function_wrapper-impl.hpp22
-rw-r--r--include/yacppdic/detail/internal/wrapper/function_wrapper.hpp26
-rw-r--r--include/yacppdic/detail/internal/wrapper/object_wrapper-impl.hpp15
-rw-r--r--include/yacppdic/detail/internal/wrapper/object_wrapper.hpp26
-rw-r--r--include/yacppdic/detail/object_type-impl.hpp29
-rw-r--r--include/yacppdic/factory.hpp171
-rw-r--r--include/yacppdic/interfaces/wrapper.hpp17
-rw-r--r--include/yacppdic/object_type.hpp34
-rw-r--r--include/yacppdic/type_traits.hpp23
-rw-r--r--test/CMakeLists.txt47
-rw-r--r--test/container.test.cpp41
-rw-r--r--test/lib/CMakeLists.txt3
-rw-r--r--test/lib/gtest/CMakeLists.txt9
38 files changed, 2035 insertions, 0 deletions
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..782b0a9
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,19 @@
+BasedOnStyle: LLVM
+UseTab: Always
+IndentWidth: 4
+TabWidth: 4
+BreakBeforeBraces: Allman
+AllowShortIfStatementsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: Empty
+AllowShortLambdasOnASingleLine: None
+IndentCaseLabels: false
+ColumnLimit: 90
+AccessModifierOffset: -4
+AlwaysBreakTemplateDeclarations: Yes
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+AllowAllArgumentsOnNextLine: false
+AllowAllParametersOfDeclarationOnNextLine: false
+BinPackArguments: false
+BinPackParameters: false
+AlignAfterOpenBracket: BlockIndent
+Cpp11BracedListStyle: false
diff --git a/.clang-tidy b/.clang-tidy
new file mode 100644
index 0000000..9c19cf2
--- /dev/null
+++ b/.clang-tidy
@@ -0,0 +1,44 @@
+---
+Checks: '
+ clang-analyzer-*,
+ cppcoreguidelines-*,
+ google-*,
+ misc-*,
+ modernize-*,
+ bugprone-*,
+ performance-*,
+ readability-*,
+ -cppcoreguidelines-pro-type-union-access'
+WarningsAsErrors: '*'
+HeaderFilterRegex: '\/src\/'
+AnalyzeTemporaryDtors: false
+CheckOptions:
+ - key: readability-function-cognitive-complexity.Threshold
+ value: 100
+ - key: readability-identifier-naming.ClassCase
+ value: CamelCase
+ - key: readability-identifier-naming.PrivateMemberPrefix
+ value: _
+ - key: readability-identifier-naming.StructCase
+ value: CamelCase
+ - key: google-readability-braces-around-statements.ShortStatementLines
+ value: '1'
+ - key: google-readability-function-size.StatementThreshold
+ value: '800'
+ - key: google-readability-namespace-comments.ShortNamespaceLines
+ value: '10'
+ - key: google-readability-namespace-comments.SpacesBeforeComments
+ value: '2'
+ - key: modernize-loop-convert.MaxCopySize
+ value: '16'
+ - key: modernize-loop-convert.MinConfidence
+ value: reasonable
+ - key: modernize-loop-convert.NamingStyle
+ value: CamelCase
+ - key: modernize-pass-by-value.IncludeStyle
+ value: llvm
+ - key: modernize-replace-auto-ptr.IncludeStyle
+ value: llvm
+ - key: modernize-use-nullptr.NullMacros
+ value: 'NULL'
+...
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..af96791
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+.vscode
+build
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..9810762
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 3.2.0)
+
+project(yacppdic CXX)
+
+add_library(${PROJECT_NAME} INTERFACE)
+
+target_include_directories(
+ ${PROJECT_NAME}
+ INTERFACE
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+)
+
+add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
+
+add_subdirectory(test)
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f996018
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+# Yet another C++ Dependency Injection Container
diff --git a/include/yacppdic/auto_wirable.hpp b/include/yacppdic/auto_wirable.hpp
new file mode 100644
index 0000000..09cef65
--- /dev/null
+++ b/include/yacppdic/auto_wirable.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "yacppdic/container.hpp"
+
+#include <memory>
+
+namespace yacppdic
+{
+
+template <class Interface, class ObjectImpl, class... Dependencies>
+class AutoWirable
+{
+public:
+ static auto resolve(const Container &container) noexcept
+ -> std::unique_ptr<Interface>;
+};
+
+} // namespace yacppdic
+
+#include "yacppdic/detail/auto_wirable-impl.hpp"
diff --git a/include/yacppdic/concepts.hpp b/include/yacppdic/concepts.hpp
new file mode 100644
index 0000000..d8097df
--- /dev/null
+++ b/include/yacppdic/concepts.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "yacppdic/type_traits.hpp"
+
+#include <type_traits>
+
+namespace yacppdic
+{
+
+template <typename Type>
+concept Abstract = std::is_abstract_v<Type>;
+
+template <typename Type>
+concept IsFactory = is_factory_v<Type>;
+
+}
diff --git a/include/yacppdic/container.hpp b/include/yacppdic/container.hpp
new file mode 100644
index 0000000..f8ffb90
--- /dev/null
+++ b/include/yacppdic/container.hpp
@@ -0,0 +1,65 @@
+#pragma once
+
+#include "yacppdic/interfaces/wrapper.hpp"
+
+#include "yacppdic/concepts.hpp"
+#include "yacppdic/factory.hpp"
+#include "yacppdic/object_type.hpp"
+
+#include <concepts>
+#include <functional>
+#include <memory>
+#include <unordered_map>
+
+namespace yacppdic
+{
+
+class Container;
+
+template <typename Interface>
+class BindingBuilder
+{
+public:
+ explicit BindingBuilder(Container *container) noexcept;
+
+ template <typename Impl>
+ requires Abstract<Interface> && std::derived_from<Impl, Interface>
+ void to() noexcept;
+
+ template <typename FactoryFunc>
+ requires IsFactory<Interface> && std::constructible_from<Interface, FactoryFunc>
+ void to_factory(FactoryFunc factory) noexcept;
+
+private:
+ Container *_container;
+};
+
+class Container
+{
+public:
+ Container() noexcept = default;
+
+ template <typename 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 -> std::unique_ptr<Interface>;
+
+ template <typename AFactory>
+ requires IsFactory<AFactory>
+ auto get() const noexcept -> AFactory;
+
+ void add(BaseObjectType type, const WrapperPtr<IGenericWrapper> &wrapper) noexcept;
+
+private:
+ std::unordered_map<BaseObjectType, WrapperPtr<IGenericWrapper>, ObjectTypeHasher>
+ _bindings;
+};
+
+} // namespace yacppdic
+
+#include "yacppdic/detail/container-impl.hpp"
diff --git a/include/yacppdic/detail/auto_wirable-impl.hpp b/include/yacppdic/detail/auto_wirable-impl.hpp
new file mode 100644
index 0000000..e2dafa9
--- /dev/null
+++ b/include/yacppdic/detail/auto_wirable-impl.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "yacppdic/auto_wirable.hpp"
+
+namespace yacppdic
+{
+
+template <class Interface, class ObjectImpl, class... Dependencies>
+auto AutoWirable<Interface, ObjectImpl, Dependencies...>::resolve(
+ const Container &container
+) noexcept -> std::unique_ptr<Interface>
+{
+ return std::make_unique<ObjectImpl>(container.get<Dependencies>()...);
+}
+
+} // namespace yacppdic
diff --git a/include/yacppdic/detail/container-impl.hpp b/include/yacppdic/detail/container-impl.hpp
new file mode 100644
index 0000000..897b8c3
--- /dev/null
+++ b/include/yacppdic/detail/container-impl.hpp
@@ -0,0 +1,94 @@
+#pragma once
+
+#include "yacppdic/container.hpp"
+#include "yacppdic/detail/internal/wrapper/function_wrapper.hpp"
+#include "yacppdic/detail/internal/wrapper/object_wrapper.hpp"
+
+#include <iostream>
+
+namespace yacppdic
+{
+
+template <typename Interface>
+BindingBuilder<Interface>::BindingBuilder(Container *container) noexcept
+ : _container(container)
+{
+}
+
+template <typename Interface>
+template <typename Impl>
+requires Abstract<Interface> && std::derived_from<Impl, Interface>
+void BindingBuilder<Interface>::to() noexcept
+{
+ using Wrapper = internal::ObjectWrapper<Interface, Impl>;
+
+ auto wrapper = Container::WrapperPtr<Wrapper>(new Wrapper(*_container));
+
+ _container->add(
+ ObjectType<Interface>(),
+ std::dynamic_pointer_cast<IGenericWrapper>(wrapper)
+ );
+}
+
+template <typename Interface>
+template <typename FactoryFunc>
+requires IsFactory<Interface> && std::constructible_from<Interface, FactoryFunc>
+void BindingBuilder<Interface>::to_factory(FactoryFunc factory) noexcept
+{
+ using Wrapper = internal::FunctionWrapper<Interface>;
+
+ auto wrapper = Container::WrapperPtr<Wrapper>(new Wrapper(factory));
+
+ _container->add(
+ ObjectType<Interface>(),
+ std::dynamic_pointer_cast<IGenericWrapper>(wrapper)
+ );
+}
+
+template <typename Interface>
+auto Container::bind() noexcept -> BindingBuilder<Interface>
+{
+ return BindingBuilder<Interface>(this);
+}
+
+template <typename Interface>
+requires Abstract<Interface>
+auto Container::get() const noexcept -> std::unique_ptr<Interface>
+{
+ ObjectType<Interface> interface_type;
+
+ if (_bindings.count(interface_type) == 0)
+ {
+ std::cerr
+ << "Error: Tried to get a item from the container using unbound interface '"
+ << interface_type.name() << "'" << std::endl;
+ exit(EXIT_FAILURE);
+ }
+
+ auto wrapper = std::dynamic_pointer_cast<IWrapper<std::unique_ptr<Interface>>>(
+ _bindings.at(interface_type)
+ );
+
+ return wrapper->get();
+}
+
+template <typename AFactory>
+requires IsFactory<AFactory>
+auto Container::get() const noexcept -> AFactory
+{
+ auto wrapper =
+ std::dynamic_pointer_cast<IWrapper<AFactory>>(_bindings.at(ObjectType<AFactory>())
+ );
+
+ return wrapper->get();
+}
+
+void Container::add(
+ BaseObjectType type,
+ const WrapperPtr<IGenericWrapper> &wrapper
+) noexcept
+{
+ _bindings.insert({ type, wrapper });
+}
+
+} // namespace yacppdic
diff --git a/include/yacppdic/detail/factory-impl.hpp b/include/yacppdic/detail/factory-impl.hpp
new file mode 100644
index 0000000..3deede2
--- /dev/null
+++ b/include/yacppdic/detail/factory-impl.hpp
@@ -0,0 +1,70 @@
+#pragma once
+
+#include "yacppdic/factory.hpp"
+
+namespace yacppdic
+{
+
+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/include/yacppdic/detail/internal/alloc_destructor-impl.hpp b/include/yacppdic/detail/internal/alloc_destructor-impl.hpp
new file mode 100644
index 0000000..abcabba
--- /dev/null
+++ b/include/yacppdic/detail/internal/alloc_destructor-impl.hpp
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "alloc_destructor.hpp"
+
+namespace yacppdic::internal
+{
+
+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/include/yacppdic/detail/internal/alloc_destructor.hpp b/include/yacppdic/detail/internal/alloc_destructor.hpp
new file mode 100644
index 0000000..9160b8b
--- /dev/null
+++ b/include/yacppdic/detail/internal/alloc_destructor.hpp
@@ -0,0 +1,37 @@
+#pragma once
+
+#include <memory>
+
+namespace yacppdic::internal
+{
+
+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;
+};
+
+} // namespace yacppdic::internal
+
+#include "alloc_destructor-impl.hpp"
diff --git a/include/yacppdic/detail/internal/compressed_pair-impl.hpp b/include/yacppdic/detail/internal/compressed_pair-impl.hpp
new file mode 100644
index 0000000..0e2a322
--- /dev/null
+++ b/include/yacppdic/detail/internal/compressed_pair-impl.hpp
@@ -0,0 +1,46 @@
+#pragma once
+
+#include "compressed_pair.hpp"
+
+namespace yacppdic::internal
+{
+
+template <class Value, int Idx>
+constexpr CompressedPairElement<Value, Idx>::CompressedPairElement(
+ DefaultInitTag /*default_init_tag*/
+)
+{
+}
+
+template <class Value, int Idx>
+constexpr CompressedPairElement<Value, Idx>::CompressedPairElement(
+ ValueInitTag /*value_init_tag*/
+)
+ : _value()
+{
+}
+
+template <class Value, int Idx>
+template <class... Args, size_t... Indices>
+constexpr CompressedPairElement<Value, Idx>::CompressedPairElement(
+ std::piecewise_construct_t /*tag_types*/,
+ std::tuple<Args...> args,
+ TupleIndices<Indices...> /*indices*/
+)
+ : _value(std::forward<Args>(std::get<Indices>(args))...)
+{
+}
+
+template <class Value, int Idx>
+auto CompressedPairElement<Value, Idx>::get() noexcept -> ValueReference
+{
+ return _value;
+}
+
+template <class Value, int Idx>
+auto CompressedPairElement<Value, Idx>::get() const noexcept -> ConstValueReference
+{
+ return _value;
+}
+
+} // namespace yacppdic::internal
diff --git a/include/yacppdic/detail/internal/compressed_pair.hpp b/include/yacppdic/detail/internal/compressed_pair.hpp
new file mode 100644
index 0000000..3c832c5
--- /dev/null
+++ b/include/yacppdic/detail/internal/compressed_pair.hpp
@@ -0,0 +1,199 @@
+#pragma once
+
+#include "yacppdic/detail/internal/misc_concepts.hpp"
+
+#include <concepts>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+namespace yacppdic::internal
+{
+
+// 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>
+class CompressedPairElement
+{
+public:
+ using ValueReference = Value &;
+ using ConstValueReference = const Value &;
+
+ constexpr explicit CompressedPairElement(DefaultInitTag default_init_tag);
+
+ constexpr explicit CompressedPairElement(ValueInitTag value_init_tag);
+
+ template <class ElementValue>
+ requires std::same_as<CompressedPairElement, std::decay_t<ElementValue>>
+ // NOLINTNEXTLINE(bugprone-forwarding-reference-overload)
+ constexpr explicit CompressedPairElement(ElementValue &&value)
+ : _value(std::forward<ElementValue>(value))
+ {
+ }
+
+ template <class... Args, size_t... Indices>
+ constexpr CompressedPairElement(
+ std::piecewise_construct_t tag_type,
+ std::tuple<Args...> args,
+ TupleIndices<Indices...> tuple_indices
+ );
+
+ auto get() noexcept -> ValueReference;
+
+ [[nodiscard]] auto get() const noexcept -> ConstValueReference;
+
+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... Indices>
+ constexpr CompressedPairElement(
+ std::piecewise_construct_t /*unused*/,
+ std::tuple<Args...> args,
+ TupleIndices<Indices...> /*unused*/
+ )
+ : Value(std::forward<Args>(std::get<Indices>(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);
+ }
+};
+
+} // namespace yacppdic::internal
+
+#include "compressed_pair-impl.hpp"
diff --git a/include/yacppdic/detail/internal/functor/alloc_functor-impl.hpp b/include/yacppdic/detail/internal/functor/alloc_functor-impl.hpp
new file mode 100644
index 0000000..55de548
--- /dev/null
+++ b/include/yacppdic/detail/internal/functor/alloc_functor-impl.hpp
@@ -0,0 +1,186 @@
+#pragma once
+
+#include "alloc_functor.hpp"
+
+#include <functional>
+
+namespace yacppdic::internal
+{
+
+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/include/yacppdic/detail/internal/functor/alloc_functor.hpp b/include/yacppdic/detail/internal/functor/alloc_functor.hpp
new file mode 100644
index 0000000..687f182
--- /dev/null
+++ b/include/yacppdic/detail/internal/functor/alloc_functor.hpp
@@ -0,0 +1,89 @@
+#pragma once
+
+#include "yacppdic/detail/internal/alloc_destructor.hpp"
+#include "yacppdic/detail/internal/compressed_pair.hpp"
+
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+namespace yacppdic::internal
+{
+
+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;
+};
+
+} // namespace yacppdic::internal
+
+#include "alloc_functor-impl.hpp"
diff --git a/include/yacppdic/detail/internal/functor/copyable_functor-impl.hpp b/include/yacppdic/detail/internal/functor/copyable_functor-impl.hpp
new file mode 100644
index 0000000..9c51492
--- /dev/null
+++ b/include/yacppdic/detail/internal/functor/copyable_functor-impl.hpp
@@ -0,0 +1,112 @@
+#pragma once
+
+#include "copyable_functor.hpp"
+
+#include <memory>
+#include <utility>
+
+namespace yacppdic::internal
+{
+
+// 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/include/yacppdic/detail/internal/functor/copyable_functor.hpp b/include/yacppdic/detail/internal/functor/copyable_functor.hpp
new file mode 100644
index 0000000..b305d38
--- /dev/null
+++ b/include/yacppdic/detail/internal/functor/copyable_functor.hpp
@@ -0,0 +1,52 @@
+#pragma once
+
+#include "yacppdic/detail/internal/interfaces/copyable_functor.hpp"
+
+#include "yacppdic/detail/internal/functor/alloc_functor.hpp"
+
+#include <typeinfo>
+
+namespace yacppdic::internal
+{
+
+/**
+ * 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;
+};
+
+} // namespace yacppdic::internal
+
+#include "copyable_functor-impl.hpp"
diff --git a/include/yacppdic/detail/internal/functor/value_functor-impl.hpp b/include/yacppdic/detail/internal/functor/value_functor-impl.hpp
new file mode 100644
index 0000000..c1030aa
--- /dev/null
+++ b/include/yacppdic/detail/internal/functor/value_functor-impl.hpp
@@ -0,0 +1,196 @@
+#pragma once
+
+#include "value_functor.hpp"
+
+namespace yacppdic::internal
+{
+
+// 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/include/yacppdic/detail/internal/functor/value_functor.hpp b/include/yacppdic/detail/internal/functor/value_functor.hpp
new file mode 100644
index 0000000..e5cde97
--- /dev/null
+++ b/include/yacppdic/detail/internal/functor/value_functor.hpp
@@ -0,0 +1,70 @@
+#pragma once
+
+#include "yacppdic/detail/internal/interfaces/copyable_functor.hpp"
+
+#include "yacppdic/detail/internal/functor/copyable_functor.hpp"
+#include "yacppdic/detail/internal/misc_concepts.hpp"
+
+#include <memory>
+#include <type_traits>
+
+namespace yacppdic::internal
+{
+
+/**
+ * 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 *;
+};
+
+} // namespace yacppdic::internal
+
+#include "value_functor-impl.hpp"
diff --git a/include/yacppdic/detail/internal/interfaces/copyable_functor.hpp b/include/yacppdic/detail/internal/interfaces/copyable_functor.hpp
new file mode 100644
index 0000000..dadb214
--- /dev/null
+++ b/include/yacppdic/detail/internal/interfaces/copyable_functor.hpp
@@ -0,0 +1,46 @@
+#pragma once
+
+#include <typeinfo>
+
+namespace yacppdic::internal
+{
+
+/**
+ * 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/include/yacppdic/detail/internal/misc_concepts.hpp b/include/yacppdic/detail/internal/misc_concepts.hpp
new file mode 100644
index 0000000..5d9ecb2
--- /dev/null
+++ b/include/yacppdic/detail/internal/misc_concepts.hpp
@@ -0,0 +1,11 @@
+#pragma once
+
+#include <type_traits>
+
+namespace yacppdic::internal
+{
+
+template <typename FirstType, typename SecondType>
+concept NotSameAs = !std::is_same_v<FirstType, SecondType>;
+
+}
diff --git a/include/yacppdic/detail/internal/strip_signature.hpp b/include/yacppdic/detail/internal/strip_signature.hpp
new file mode 100644
index 0000000..30a2ad0
--- /dev/null
+++ b/include/yacppdic/detail/internal/strip_signature.hpp
@@ -0,0 +1,108 @@
+#pragma once
+
+namespace yacppdic::internal
+{
+
+template <class Function>
+struct StripSignature;
+
+template <class Return, class Class, class... Args>
+struct StripSignature<Return (Class::*)(Args...)>
+{
+ using type = Return(Args...);
+};
+
+template <class Return, class Class, class... Args>
+struct StripSignature<Return (Class::*)(Args...) const>
+{
+ using type = Return(Args...);
+};
+
+template <class Return, class Class, class... Args>
+struct StripSignature<Return (Class::*)(Args...) volatile>
+{
+ using type = Return(Args...);
+};
+
+template <class Return, class Class, class... Args>
+struct StripSignature<Return (Class::*)(Args...) const volatile>
+{
+ using type = Return(Args...);
+};
+
+template <class Return, class Class, class... Args>
+struct StripSignature<Return (Class::*)(Args...) &>
+{
+ using type = Return(Args...);
+};
+
+template <class Return, class Class, class... Args>
+struct StripSignature<Return (Class::*)(Args...) const &>
+{
+ using type = Return(Args...);
+};
+
+template <class Return, class Class, class... Args>
+struct StripSignature<Return (Class::*)(Args...) volatile &>
+{
+ using type = Return(Args...);
+};
+
+template <class Return, class Class, class... Args>
+struct StripSignature<Return (Class::*)(Args...) const volatile &>
+{
+ using type = Return(Args...);
+};
+
+template <class Return, class Class, class... Args>
+struct StripSignature<Return (Class::*)(Args...) noexcept>
+{
+ using type = Return(Args...);
+};
+
+template <class Return, class Class, class... Args>
+struct StripSignature<Return (Class::*)(Args...) const noexcept>
+{
+ using type = Return(Args...);
+};
+
+template <class Return, class Class, class... Args>
+struct StripSignature<Return (Class::*)(Args...) volatile noexcept>
+{
+ using type = Return(Args...);
+};
+
+template <class Return, class Class, class... Args>
+struct StripSignature<Return (Class::*)(Args...) const volatile noexcept>
+{
+ using type = Return(Args...);
+};
+
+template <class Return, class Class, class... Args>
+struct StripSignature<Return (Class::*)(Args...) &noexcept>
+{
+ using type = Return(Args...);
+};
+
+template <class Return, class Class, class... Args>
+struct StripSignature<Return (Class::*)(Args...) const &noexcept>
+{
+ using type = Return(Args...);
+};
+
+template <class Return, class Class, class... Args>
+struct StripSignature<Return (Class::*)(Args...) volatile &noexcept>
+{
+ using type = Return(Args...);
+};
+
+template <class Return, class Class, class... Args>
+struct StripSignature<Return (Class::*)(Args...) const volatile &noexcept>
+{
+ using type = Return(Args...);
+};
+
+template <class Function>
+using StripSignatureT = typename StripSignature<Function>::type;
+
+} // namespace yacppdic::internal
diff --git a/include/yacppdic/detail/internal/type_utils.hpp b/include/yacppdic/detail/internal/type_utils.hpp
new file mode 100644
index 0000000..f008352
--- /dev/null
+++ b/include/yacppdic/detail/internal/type_utils.hpp
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <type_traits>
+
+namespace yacppdic::internal
+{
+
+template <class Type>
+struct UnConstVolatileReference
+{
+ using type =
+ typename std::remove_cv<typename std::remove_reference<Type>::type>::type;
+};
+
+template <class Type>
+using UnConstVolatileReferenceT = typename UnConstVolatileReference<Type>::type;
+
+template <class Tp, class Up, class = void>
+struct IsCoreConvertible : public std::false_type
+{
+};
+
+template <class Tp, class Up>
+struct IsCoreConvertible<
+ Tp,
+ Up,
+ decltype(static_cast<void (*)(Up)>(0)(static_cast<Tp (*)()>(0)()))>
+ : public std::true_type
+{
+};
+
+template <class Tp, class Up>
+constexpr bool IsCoreConvertibleV = IsCoreConvertible<Tp, Up>::value;
+
+} // namespace yacppdic::internal
diff --git a/include/yacppdic/detail/internal/wrapper/function_wrapper-impl.hpp b/include/yacppdic/detail/internal/wrapper/function_wrapper-impl.hpp
new file mode 100644
index 0000000..fc9492a
--- /dev/null
+++ b/include/yacppdic/detail/internal/wrapper/function_wrapper-impl.hpp
@@ -0,0 +1,22 @@
+#pragma once
+
+#include "function_wrapper.hpp"
+
+#include <utility>
+
+namespace yacppdic::internal
+{
+
+template <class Interface>
+FunctionWrapper<Interface>::FunctionWrapper(Interface func) noexcept
+ : _func(std::move(func))
+{
+}
+
+template <class Interface>
+auto FunctionWrapper<Interface>::get() const noexcept -> Interface
+{
+ return _func;
+}
+
+}
diff --git a/include/yacppdic/detail/internal/wrapper/function_wrapper.hpp b/include/yacppdic/detail/internal/wrapper/function_wrapper.hpp
new file mode 100644
index 0000000..bec873b
--- /dev/null
+++ b/include/yacppdic/detail/internal/wrapper/function_wrapper.hpp
@@ -0,0 +1,26 @@
+#pragma once
+
+#include "yacppdic/interfaces/wrapper.hpp"
+
+#include "yacppdic/container.hpp"
+
+#include <memory>
+
+namespace yacppdic::internal
+{
+
+template <class Interface>
+class FunctionWrapper : public IWrapper<Interface>
+{
+public:
+ explicit FunctionWrapper(Interface func) noexcept;
+
+ [[nodiscard]] auto get() const noexcept -> Interface override;
+
+private:
+ const Interface _func;
+};
+
+} // namespace yacppdic::internal
+
+#include "function_wrapper-impl.hpp"
diff --git a/include/yacppdic/detail/internal/wrapper/object_wrapper-impl.hpp b/include/yacppdic/detail/internal/wrapper/object_wrapper-impl.hpp
new file mode 100644
index 0000000..01a3dff
--- /dev/null
+++ b/include/yacppdic/detail/internal/wrapper/object_wrapper-impl.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+#include "object_wrapper.hpp"
+
+namespace yacppdic::internal
+{
+
+template <class Interface, class ObjectImpl>
+auto ObjectWrapper<Interface, ObjectImpl>::get() const noexcept
+ -> std::unique_ptr<Interface>
+{
+ return ObjectImpl::resolve(_container);
+}
+
+} // namespace yacppdic::internal
diff --git a/include/yacppdic/detail/internal/wrapper/object_wrapper.hpp b/include/yacppdic/detail/internal/wrapper/object_wrapper.hpp
new file mode 100644
index 0000000..f3c5e87
--- /dev/null
+++ b/include/yacppdic/detail/internal/wrapper/object_wrapper.hpp
@@ -0,0 +1,26 @@
+#pragma once
+
+#include "yacppdic/interfaces/wrapper.hpp"
+
+#include "yacppdic/container.hpp"
+
+#include <memory>
+
+namespace yacppdic::internal
+{
+
+template <class Interface, class ObjectImpl>
+class ObjectWrapper : public IWrapper<std::unique_ptr<Interface>>
+{
+public:
+ explicit ObjectWrapper(const Container &container) noexcept : _container(container) {}
+
+ [[nodiscard]] auto get() const noexcept -> std::unique_ptr<Interface> override;
+
+private:
+ const Container &_container;
+};
+
+} // namespace yacppdic::internal
+
+#include "object_wrapper-impl.hpp"
diff --git a/include/yacppdic/detail/object_type-impl.hpp b/include/yacppdic/detail/object_type-impl.hpp
new file mode 100644
index 0000000..f12f99e
--- /dev/null
+++ b/include/yacppdic/detail/object_type-impl.hpp
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "yacppdic/object_type.hpp"
+
+BaseObjectType::BaseObjectType(const std::type_info &type_info) noexcept
+ : _type_info(type_info)
+{
+}
+
+auto BaseObjectType::operator==(const BaseObjectType &object_type) const noexcept -> bool
+{
+ return hash() == object_type.hash();
+}
+
+auto BaseObjectType::hash() const noexcept -> std::size_t
+{
+ return _type_info.hash_code();
+}
+
+auto BaseObjectType::name() const noexcept -> std::string_view
+{
+ return { _type_info.name() };
+}
+
+auto ObjectTypeHasher::operator()(const BaseObjectType &object_type) const noexcept
+ -> std::size_t
+{
+ return object_type.hash();
+}
diff --git a/include/yacppdic/factory.hpp b/include/yacppdic/factory.hpp
new file mode 100644
index 0000000..368175a
--- /dev/null
+++ b/include/yacppdic/factory.hpp
@@ -0,0 +1,171 @@
+#pragma once
+
+#include "yacppdic/detail/internal/interfaces/copyable_functor.hpp"
+
+#include "yacppdic/detail/internal/alloc_destructor.hpp"
+#include "yacppdic/detail/internal/functor/alloc_functor.hpp"
+#include "yacppdic/detail/internal/functor/copyable_functor.hpp"
+#include "yacppdic/detail/internal/functor/value_functor.hpp"
+#include "yacppdic/detail/internal/misc_concepts.hpp"
+#include "yacppdic/detail/internal/strip_signature.hpp"
+#include "yacppdic/detail/internal/type_utils.hpp"
+
+#include <type_traits>
+#include <utility>
+
+namespace yacppdic
+{
+
+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 <typename Function, typename Return, typename... Args>
+concept Callable =
+ !std::is_same_v<
+ internal::UnConstVolatileReferenceT<Function>,
+ Factory<Return(Args...)>> &&
+ std::is_invocable_v<Function, Args...> &&
+ internal::IsCoreConvertibleV<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 = internal::ValueFunctor<Return(Args...)>;
+
+ ValFunctor _functor;
+};
+
+template <class Return, class... Args>
+Factory(Return (*)(Args...)) -> Factory<Return(Args...)>;
+
+template <
+ class Function,
+ class Stripped = internal::StripSignatureT<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);
+}
+
+} // namespace yacppdic
+
+#include "yacppdic/detail/factory-impl.hpp"
diff --git a/include/yacppdic/interfaces/wrapper.hpp b/include/yacppdic/interfaces/wrapper.hpp
new file mode 100644
index 0000000..e22ea7f
--- /dev/null
+++ b/include/yacppdic/interfaces/wrapper.hpp
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <memory>
+
+// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions)
+class IGenericWrapper
+{
+public:
+ virtual ~IGenericWrapper() noexcept = default;
+};
+
+template <class Interface>
+class IWrapper : public IGenericWrapper
+{
+public:
+ [[nodiscard]] virtual auto get() const noexcept -> Interface = 0;
+};
diff --git a/include/yacppdic/object_type.hpp b/include/yacppdic/object_type.hpp
new file mode 100644
index 0000000..eb4ce0c
--- /dev/null
+++ b/include/yacppdic/object_type.hpp
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <string_view>
+#include <typeinfo>
+
+class BaseObjectType
+{
+public:
+ explicit BaseObjectType(const std::type_info &type_info) noexcept;
+
+ auto operator==(const BaseObjectType &object_type) const noexcept -> bool;
+
+ [[nodiscard]] auto hash() const noexcept -> std::size_t;
+
+ [[nodiscard]] auto name() const noexcept -> std::string_view;
+
+private:
+ const std::type_info &_type_info;
+};
+
+template <class Object>
+class ObjectType : public BaseObjectType
+{
+public:
+ ObjectType() noexcept : BaseObjectType(typeid(Object)) {}
+};
+
+class ObjectTypeHasher
+{
+public:
+ auto operator()(const BaseObjectType &object_type) const noexcept -> std::size_t;
+};
+
+#include "yacppdic/detail/object_type-impl.hpp"
diff --git a/include/yacppdic/type_traits.hpp b/include/yacppdic/type_traits.hpp
new file mode 100644
index 0000000..ab05caa
--- /dev/null
+++ b/include/yacppdic/type_traits.hpp
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "yacppdic/factory.hpp"
+
+#include <type_traits>
+
+namespace yacppdic
+{
+
+template <typename NotAFunction>
+struct is_factory : public std::false_type // NOLINT(readability-identifier-naming)
+{
+};
+
+template <typename Return, typename... Args>
+struct is_factory<Factory<Return(Args...)>> : public std::true_type
+{
+};
+
+template <typename PossiblyFunction>
+inline constexpr bool is_factory_v = is_factory<PossiblyFunction>::value;
+
+}
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
new file mode 100644
index 0000000..c1fb283
--- /dev/null
+++ b/test/CMakeLists.txt
@@ -0,0 +1,47 @@
+cmake_minimum_required(VERSION 3.2.0)
+
+set(CMAKE_EXPORT_COMPILE_COMMANDS 1)
+set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_EXTENSIONS OFF)
+
+project(test CXX)
+
+add_subdirectory(lib)
+
+enable_testing()
+
+add_executable(
+ ${PROJECT_NAME}
+ container.test.cpp
+)
+
+target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_20)
+
+target_compile_options(
+ ${PROJECT_NAME}
+ PRIVATE
+ -Wall -Wextra -Wpedantic -Wshadow
+ -Wold-style-cast -Wcast-align -Wno-unused
+ -Wconversion -Wcast-qual -Wctor-dtor-privacy
+ -Wdisabled-optimization -Wformat=2 -Winit-self
+ -Wmissing-declarations -Wmissing-include-dirs
+ -Woverloaded-virtual -Wredundant-decls
+ -Wsign-conversion -Wsign-promo
+ -Wstrict-overflow=5 -Wswitch-default
+ -Wundef -Werror
+ -pedantic -fsanitize=address -fno-exceptions
+)
+
+target_link_options(${PROJECT_NAME} PRIVATE -fsanitize=address)
+
+target_link_libraries(
+ ${PROJECT_NAME}
+ PRIVATE
+ yacppdic
+ gtest_main
+)
+
+include(GoogleTest)
+
+gtest_discover_tests(${PROJECT_NAME})
+
diff --git a/test/container.test.cpp b/test/container.test.cpp
new file mode 100644
index 0000000..54290ea
--- /dev/null
+++ b/test/container.test.cpp
@@ -0,0 +1,41 @@
+#include "yacppdic/container.hpp"
+#include "yacppdic/auto_wirable.hpp"
+#include "gtest/gtest.h"
+
+#include <string_view>
+
+TEST(ContainerTest, BindAndGet)
+{
+ auto container = yacppdic::Container();
+
+ class IObject
+ {
+ public:
+ virtual ~IObject() = default;
+
+ virtual void destroy() = 0;
+
+ virtual std::string_view name() = 0;
+ };
+
+ class Object : public IObject, public yacppdic::AutoWirable<IObject, Object>
+ {
+ public:
+ void destroy() {}
+
+ std::string_view name()
+ {
+ return "A object";
+ }
+ };
+
+ container.bind<IObject>().to<Object>();
+
+ auto object = container.get<IObject>();
+
+ EXPECT_EQ(object->name(), "A object");
+
+ auto object_two = container.get<IObject>();
+
+ EXPECT_NE(object, object_two);
+}
diff --git a/test/lib/CMakeLists.txt b/test/lib/CMakeLists.txt
new file mode 100644
index 0000000..7bf8282
--- /dev/null
+++ b/test/lib/CMakeLists.txt
@@ -0,0 +1,3 @@
+include(FetchContent)
+
+add_subdirectory(gtest)
diff --git a/test/lib/gtest/CMakeLists.txt b/test/lib/gtest/CMakeLists.txt
new file mode 100644
index 0000000..6582343
--- /dev/null
+++ b/test/lib/gtest/CMakeLists.txt
@@ -0,0 +1,9 @@
+message(STATUS "Fetching gtest...")
+
+FetchContent_Declare(
+ gtest
+ GIT_REPOSITORY "https://github.com/google/googletest"
+ GIT_TAG release-1.11.0
+)
+
+FetchContent_MakeAvailable(gtest)