diff options
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) |