-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin/main' into Jake/Movement+KeyInput
- Loading branch information
Showing
11 changed files
with
233 additions
and
73 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,16 @@ | ||
--- Checks: 'clang-analyzer-*, | ||
concurrency-*, | ||
cppcoreguidelines-*, | ||
-cppcoreguidelines-non-private-member-variables-in-classes, | ||
misc-*, | ||
-misc-non-private-member-variables-in-classes, | ||
modernize-*, | ||
-modernize-use-trailing-return-type, | ||
performance-*, | ||
portability-*, | ||
readability-*, | ||
-readability-identifier-length, | ||
-readability-uppercase-literal-suffix -readability-magic-numbers ... | ||
--- | ||
Checks: 'clang-analyzer-*, | ||
concurrency-*, | ||
cppcoreguidelines-*, | ||
-cppcoreguidelines-non-private-member-variables-in-classes, | ||
misc-*, | ||
-misc-non-private-member-variables-in-classes, | ||
modernize-*, | ||
-modernize-use-trailing-return-type, | ||
performance-*, | ||
portability-*, | ||
readability-*, | ||
-readability-identifier-length, | ||
-readability-uppercase-literal-suffix | ||
-readability-magic-numbers' | ||
... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
#pragma once | ||
#include <fmt/format.h> | ||
#include <stdexcept> | ||
|
||
class FatalError : public std::runtime_error { | ||
public: | ||
template <typename... Args> | ||
explicit FatalError(fmt::format_string<Args...> fmt, Args&&... args) | ||
: std::runtime_error(fmt::format(fmt, std::forward<Args>(args)...)) {} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
#pragma once | ||
#include <bave/core/polymorphic.hpp> | ||
|
||
/// \brief Base class for all services. | ||
class IService : public bave::Polymorphic {}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
#pragma once | ||
#include <bave/core/not_null.hpp> | ||
#include <bave/core/ptr.hpp> | ||
#include <src/fatal_error.hpp> | ||
#include <src/services/service.hpp> | ||
#include <memory> | ||
#include <mutex> | ||
#include <typeindex> | ||
#include <unordered_map> | ||
|
||
/// \brief Concept constraining Type to a subclass of IService. | ||
template <typename Type> | ||
concept ServiceT = std::derived_from<Type, IService>; | ||
|
||
/// \brief Container of stored services. | ||
/// | ||
/// Intended usage: | ||
/// 1. Own an instance in main / app Driver. | ||
/// 2. Bind services to subclasses. (From and To being the same type is also OK.) | ||
/// 3. Pass a const reference of Services to dependencies | ||
/// Point 3 ensures dependencies cannot bind new / remove existing services, | ||
/// but can locate and use already bound ones. | ||
class Services { | ||
public: | ||
/// \brief Bind a service instance to a type (From). | ||
/// \param service Concrete instance of service to bind. | ||
/// \pre service must not be null, From must not already be bound. | ||
template <ServiceT From, std::derived_from<From> To> | ||
void bind(std::unique_ptr<To> service) { | ||
if (!service) { throw FatalError{"Attempt to bind null service"}; } | ||
static auto const index = std::type_index{typeid(From)}; | ||
auto lock = std::scoped_lock{m_mutex}; | ||
if (m_services.contains(index)) { throw FatalError{"Attempt to bind duplicate service"}; } | ||
m_services.insert_or_assign(index, std::move(service)); | ||
} | ||
|
||
/// \brief Remove a bound service instance. Type need not actually be bound. | ||
template <ServiceT Type> | ||
void remove() { | ||
static auto const index = std::type_index{typeid(Type)}; | ||
auto lock = std::scoped_lock{m_mutex}; | ||
m_services.erase(index); | ||
} | ||
|
||
/// \brief Remove all bound service instances. | ||
void remove_all() { | ||
auto lock = std::scoped_lock{m_mutex}; | ||
m_services.clear(); | ||
} | ||
|
||
/// \brief Check if a service is bound. | ||
/// \returns true if bound. | ||
template <ServiceT Type> | ||
[[nodiscard]] auto contains() const -> bool { | ||
return find<Type>() != nullptr; | ||
} | ||
|
||
/// \brief Locate a service instance if bound. | ||
/// \returns Pointer to service instance if bound, else nullptr. | ||
template <ServiceT Type> | ||
[[nodiscard]] auto find() const -> bave::Ptr<Type> { | ||
static auto const index = std::type_index{typeid(Type)}; | ||
auto lock = std::scoped_lock{m_mutex}; | ||
if (auto const it = m_services.find(index); it != m_services.end()) { | ||
return static_cast<Type*>(it->second.get()); | ||
} | ||
return {}; | ||
} | ||
|
||
/// \brief Obtain a bound service. | ||
/// \returns Mutable reference to bound service. | ||
/// \pre Type must be bound. | ||
template <ServiceT Type> | ||
[[nodiscard]] auto get() const -> Type& { | ||
auto ret = find<Type>(); | ||
if (!ret) { throw FatalError{"Service not found"}; } | ||
return *ret; | ||
} | ||
|
||
/// \brief Obtain the count of bound services. | ||
/// \returns Count of bound services. | ||
[[nodiscard]] auto size() const -> std::size_t { | ||
auto lock = std::scoped_lock{m_mutex}; | ||
return m_services.size(); | ||
} | ||
|
||
private: | ||
std::unordered_map<std::type_index, std::unique_ptr<IService>> m_services{}; | ||
mutable std::mutex m_mutex{}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
#include <bave/core/pinned.hpp> | ||
#include <src/services/services.hpp> | ||
#include <src/tests/test.hpp> | ||
|
||
namespace { | ||
namespace one { | ||
struct Foo : IService { | ||
int value{}; | ||
}; | ||
} // namespace one | ||
|
||
namespace two { | ||
struct Foo : IService { | ||
int value{}; | ||
}; | ||
} // namespace two | ||
|
||
ADD_TEST(Services_TypeIntegrity) { | ||
auto services = Services{}; | ||
|
||
auto foo_one = std::make_unique<one::Foo>(); | ||
foo_one->value = 1; | ||
services.bind<one::Foo>(std::move(foo_one)); | ||
|
||
auto foo_two = std::make_unique<two::Foo>(); | ||
foo_two->value = 2; | ||
services.bind<two::Foo>(std::move(foo_two)); | ||
|
||
ASSERT(services.contains<one::Foo>()); | ||
EXPECT(services.get<one::Foo>().value == 1); | ||
ASSERT(services.contains<two::Foo>()); | ||
EXPECT(services.get<two::Foo>().value == 2); | ||
} | ||
|
||
ADD_TEST(Services_GetException) { | ||
auto services = Services{}; | ||
auto thrown = false; | ||
try { | ||
[[maybe_unused]] auto& foo = services.get<one::Foo>(); | ||
} catch (FatalError const&) { thrown = true; } | ||
|
||
EXPECT(thrown); | ||
} | ||
|
||
ADD_TEST(Services_DuplicateException) { | ||
auto services = Services{}; | ||
|
||
services.bind<one::Foo>(std::make_unique<one::Foo>()); | ||
|
||
auto thrown = false; | ||
try { | ||
services.bind<one::Foo>(std::make_unique<one::Foo>()); | ||
} catch (FatalError const&) { thrown = true; } | ||
|
||
EXPECT(thrown); | ||
} | ||
|
||
ADD_TEST(Services_BindSubclass) { | ||
auto services = Services{}; | ||
|
||
struct Interface : IService { | ||
virtual auto get_value() -> int = 0; | ||
}; | ||
|
||
struct Concrete : Interface { | ||
auto get_value() -> int final { return 42; } | ||
}; | ||
|
||
services.bind<Interface>(std::make_unique<Concrete>()); | ||
|
||
ASSERT(services.contains<Interface>()); | ||
EXPECT(services.get<Interface>().get_value() == 42); | ||
} | ||
} // namespace |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.