diff options
| author | HampusM <hampus@hampusmat.com> | 2022-04-26 20:16:22 +0200 | 
|---|---|---|
| committer | HampusM <hampus@hampusmat.com> | 2022-04-26 20:19:23 +0200 | 
| commit | d2a87d5803c7aaa9a47791a5a0f1494c19671ebc (patch) | |
| tree | 5dcc645cafe5c1986b66b0af0c9a62663c261ecb | |
| parent | dbe0e42817964b700385ac6ed7b7dc2141669e17 (diff) | |
add project & make some tweaks
Added from https://git.hampusmat.com/game-of-life
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) | 
