From 5864e5abc43b201c3801fa39a2fcaf9e3a9e8914 Mon Sep 17 00:00:00 2001 From: HampusM Date: Sun, 27 Feb 2022 12:54:10 +0100 Subject: refactor: use dependency injection --- src/CMakeLists.txt | 6 ++- src/DI/auto_wirable.hpp | 28 ++++++++++ src/DI/auto_wirable.tpp | 10 ++++ src/DI/container.hpp | 65 +++++++++++++++++++++++ src/DI/container.tpp | 53 +++++++++++++++++++ src/DI/function_wrapper.hpp | 20 +++++++ src/DI/function_wrapper.tpp | 9 ++++ src/DI/interfaces/wrapper.hpp | 17 ++++++ src/DI/object_type.cpp | 18 +++++++ src/DI/object_type.hpp | 35 ++++++++++++ src/DI/object_wrapper.hpp | 20 +++++++ src/DI/object_wrapper.tpp | 9 ++++ src/argument_parser.cpp | 100 +++++++++++++++++++++++++++++++++++ src/argument_parser.hpp | 27 ++++++++++ src/bootstrap.cpp | 36 +++++++++++++ src/bootstrap.hpp | 5 ++ src/game_of_life.cpp | 97 ++++++--------------------------- src/interfaces/argument_parser.hpp | 23 ++++++++ src/interfaces/randomization.hpp | 31 +++++++++++ src/randomization.cpp | 21 -------- src/randomization.hpp | 35 ------------ src/randomization/generator.cpp | 14 +++++ src/randomization/generator.hpp | 18 +++++++ src/randomization/seed_generator.cpp | 11 ++++ src/randomization/seed_generator.hpp | 17 ++++++ 25 files changed, 587 insertions(+), 138 deletions(-) create mode 100644 src/DI/auto_wirable.hpp create mode 100644 src/DI/auto_wirable.tpp create mode 100644 src/DI/container.hpp create mode 100644 src/DI/container.tpp create mode 100644 src/DI/function_wrapper.hpp create mode 100644 src/DI/function_wrapper.tpp create mode 100644 src/DI/interfaces/wrapper.hpp create mode 100644 src/DI/object_type.cpp create mode 100644 src/DI/object_type.hpp create mode 100644 src/DI/object_wrapper.hpp create mode 100644 src/DI/object_wrapper.tpp create mode 100644 src/argument_parser.cpp create mode 100644 src/argument_parser.hpp create mode 100644 src/bootstrap.cpp create mode 100644 src/bootstrap.hpp create mode 100644 src/interfaces/argument_parser.hpp create mode 100644 src/interfaces/randomization.hpp delete mode 100644 src/randomization.cpp delete mode 100644 src/randomization.hpp create mode 100644 src/randomization/generator.cpp create mode 100644 src/randomization/generator.hpp create mode 100644 src/randomization/seed_generator.cpp create mode 100644 src/randomization/seed_generator.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1e4d106..54ee94f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -10,8 +10,12 @@ endfunction(target_link_libraries_system) file(GLOB SOURCES game_of_life.cpp + bootstrap.cpp conversion.cpp - randomization.cpp) + argument_parser.cpp + randomization/generator.cpp + randomization/seed_generator.cpp + DI/object_type.cpp) add_executable(${PROJECT_NAME} ${SOURCES}) diff --git a/src/DI/auto_wirable.hpp b/src/DI/auto_wirable.hpp new file mode 100644 index 0000000..13d252e --- /dev/null +++ b/src/DI/auto_wirable.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "DI/container.hpp" + +#include + +// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions) +class IGenericAutoWirable +{ +public: + virtual ~IGenericAutoWirable() = default; +}; + +template +class IAutoWirable : public IGenericAutoWirable +{ +public: + static Interface resolve(); +}; + +template +class AutoWirable : public IAutoWirable +{ +public: + static std::shared_ptr resolve(const Container &container); +}; + +#include "auto_wirable.tpp" diff --git a/src/DI/auto_wirable.tpp b/src/DI/auto_wirable.tpp new file mode 100644 index 0000000..6c0d111 --- /dev/null +++ b/src/DI/auto_wirable.tpp @@ -0,0 +1,10 @@ +#pragma once + +#include "auto_wirable.hpp" + +template +std::shared_ptr +AutoWirable::resolve(const Container &container) +{ + return std::make_shared(container.get()...); +} diff --git a/src/DI/container.hpp b/src/DI/container.hpp new file mode 100644 index 0000000..2402894 --- /dev/null +++ b/src/DI/container.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include "DI/object_type.hpp" +#include "interfaces/wrapper.hpp" + +#include +#include +#include +#include + +class Container; + +template +class BindingBuilder +{ +public: + explicit BindingBuilder(Container *container); + + template >> + void to(); + + void to_factory(Interface func); + +private: + Container *_container; +}; + +template +struct is_func : public std::false_type // NOLINT(readability-identifier-naming) +{ +}; + +template +struct is_func> : public std::true_type +{ +}; + +class Container +{ +public: + Container() = default; + + template + BindingBuilder bind(); + + template >> + std::shared_ptr get() const; + + /* + template && + std::is_invocable_v>> + Interface get() const; + */ + + template ::value>> + Interface get() const; + + std::unordered_map, ObjectTypeHasher> + bindings; +}; + +#include "container.tpp" diff --git a/src/DI/container.tpp b/src/DI/container.tpp new file mode 100644 index 0000000..21bf81a --- /dev/null +++ b/src/DI/container.tpp @@ -0,0 +1,53 @@ +#pragma once + +#include "container.hpp" + +#include "function_wrapper.hpp" +#include "object_wrapper.hpp" + +template +BindingBuilder::BindingBuilder(Container *container) : _container(container) +{ +} + +template +template +void BindingBuilder::to() +{ + _container->bindings.emplace( + ObjectType(), + std::dynamic_pointer_cast( + std::make_shared>(*_container))); +} + +template +void BindingBuilder::to_factory(Interface func) +{ + _container->bindings.emplace(ObjectType(), + std::dynamic_pointer_cast( + std::make_shared>(func))); +} + +template +BindingBuilder Container::bind() +{ + return BindingBuilder(this); +} + +template +std::shared_ptr Container::get() const +{ + auto wrapper = std::dynamic_pointer_cast>>( + bindings.at(ObjectType())); + + return wrapper->get(); +} + +template +Interface Container::get() const +{ + auto wrapper = std::dynamic_pointer_cast>( + bindings.at(ObjectType())); + + return wrapper->get(); +} diff --git a/src/DI/function_wrapper.hpp b/src/DI/function_wrapper.hpp new file mode 100644 index 0000000..e4b6779 --- /dev/null +++ b/src/DI/function_wrapper.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include "DI/container.hpp" +#include "DI/interfaces/wrapper.hpp" + +#include + +template +class FunctionWrapper : public IWrapper +{ +public: + explicit FunctionWrapper(Interface func) : _func(func) {} + + [[nodiscard]] Interface get() const; + +private: + const Interface _func; +}; + +#include "function_wrapper.tpp" diff --git a/src/DI/function_wrapper.tpp b/src/DI/function_wrapper.tpp new file mode 100644 index 0000000..4e09957 --- /dev/null +++ b/src/DI/function_wrapper.tpp @@ -0,0 +1,9 @@ +#pragma once + +#include "function_wrapper.hpp" + +template +Interface FunctionWrapper::get() const +{ + return _func; +} diff --git a/src/DI/interfaces/wrapper.hpp b/src/DI/interfaces/wrapper.hpp new file mode 100644 index 0000000..3cc75a0 --- /dev/null +++ b/src/DI/interfaces/wrapper.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include + +// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions) +class IGenericWrapper +{ +public: + virtual ~IGenericWrapper() = default; +}; + +template +class IWrapper : public IGenericWrapper +{ +public: + [[nodiscard]] virtual Interface get() const = 0; +}; diff --git a/src/DI/object_type.cpp b/src/DI/object_type.cpp new file mode 100644 index 0000000..60c7c78 --- /dev/null +++ b/src/DI/object_type.cpp @@ -0,0 +1,18 @@ +#include "object_type.hpp" + +BaseObjectType::BaseObjectType(const std::type_info &type_info) : _type_info(type_info) {} + +bool BaseObjectType::operator==(const BaseObjectType &object_type) const +{ + return hash() == object_type.hash(); +} + +std::size_t BaseObjectType::hash() const +{ + return _type_info.hash_code(); +} + +std::size_t ObjectTypeHasher::operator()(const BaseObjectType &object_type) const +{ + return object_type.hash(); +} diff --git a/src/DI/object_type.hpp b/src/DI/object_type.hpp new file mode 100644 index 0000000..f690a64 --- /dev/null +++ b/src/DI/object_type.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include + +class BaseObjectType +{ +public: + explicit BaseObjectType(const std::type_info &type_info); + + bool operator==(const BaseObjectType &object_type) const; + + [[nodiscard]] std::size_t hash() const; + +private: + const std::type_info &_type_info; +}; + +template +class ObjectType : public BaseObjectType +{ +public: + ObjectType() : BaseObjectType(typeid(Object)) {} +}; + +class IObjectTypeHasher +{ +public: + virtual std::size_t operator()(const BaseObjectType &object_type) const = 0; +}; + +class ObjectTypeHasher : public IObjectTypeHasher +{ +public: + std::size_t operator()(const BaseObjectType &object_type) const override; +}; diff --git a/src/DI/object_wrapper.hpp b/src/DI/object_wrapper.hpp new file mode 100644 index 0000000..0ae66df --- /dev/null +++ b/src/DI/object_wrapper.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include "DI/container.hpp" +#include "DI/interfaces/wrapper.hpp" + +#include + +template +class ObjectWrapper : public IWrapper> +{ +public: + explicit ObjectWrapper(const Container &container) : _container(container) {} + + [[nodiscard]] std::shared_ptr get() const; + +private: + const Container &_container; +}; + +#include "object_wrapper.tpp" diff --git a/src/DI/object_wrapper.tpp b/src/DI/object_wrapper.tpp new file mode 100644 index 0000000..7efefc4 --- /dev/null +++ b/src/DI/object_wrapper.tpp @@ -0,0 +1,9 @@ +#pragma once + +#include "object_wrapper.hpp" + +template +std::shared_ptr ObjectWrapper::get() const +{ + return ObjectImpl::resolve(_container); +} diff --git a/src/argument_parser.cpp b/src/argument_parser.cpp new file mode 100644 index 0000000..b5f14bc --- /dev/null +++ b/src/argument_parser.cpp @@ -0,0 +1,100 @@ +#include "argument_parser.hpp" + +#include "conversion.hpp" +#include "interfaces/randomization.hpp" + +#include +#include +#include + +namespace +{ +void optarg_error(char arg, const std::string_view &error) +{ + std::cout << "Error: Invalid option argument for -" << arg << ". " << error + << std::endl; + exit(EXIT_FAILURE); +} + +/** + * Returns the current optarg as a string view. + */ +std::string_view get_str_optarg() +{ + return std::string_view(optarg); +} + +/** + * Returns the current optarg as a unsigned integer. + * + * @param arg The current command-line argument character + * @param check_zero Whether or not to make sure that the result is not zero + */ +unsigned int get_uint_optarg(char arg, bool check_zero = false) +{ + auto conversion_result = str_to_uint(get_str_optarg()); + + if (!conversion_result.success || (check_zero && conversion_result.result == 0)) + { + optarg_error(arg, conversion_result.fail_reason); + } + + return conversion_result.result; +} +} // namespace + +ArgumentParser::ArgumentParser( + IRandomNumberGeneratorFactory random_number_generator_factory) + : _random_number_generator_factory(std::move(random_number_generator_factory)) +{ +} + +ParsedArguments +ArgumentParser::parse(const std::vector