diff --git a/.github/workflows/MacOs.yml b/.github/workflows/MacOs.yml
index 1091706..588d501 100644
--- a/.github/workflows/MacOs.yml
+++ b/.github/workflows/MacOs.yml
@@ -19,8 +19,7 @@ jobs:
{ tool: apple-clang },
{ tool: gcc, ver: 10 },
{ tool: gcc, ver: 11 },
- { tool: gcc, ver: 12 },
- { tool: gcc, ver: 13 } ]
+ { tool: gcc, ver: 12 } ]
build_type: [ Release ]
os: [ macos-12 ]
std: [ 17 ]
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9e5854f..0a394f1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.15.0)
set(_7BIT_DI_LIBRARY 7bitDI)
set(_7BIT_DI_VERSION_MAJOR 3)
-set(_7BIT_DI_VERSION_MINOR 2)
+set(_7BIT_DI_VERSION_MINOR 3)
set(_7BIT_DI_VERSION_PATCH 0)
set(_7BIT_DI_VERSION ${_7BIT_DI_VERSION_MAJOR}.${_7BIT_DI_VERSION_MINOR}.${_7BIT_DI_VERSION_PATCH})
diff --git a/Docs/advanced-guides.rst b/Docs/advanced-guides.rst
index 0eeae13..b26ea9a 100644
--- a/Docs/advanced-guides.rst
+++ b/Docs/advanced-guides.rst
@@ -11,5 +11,4 @@ Advanced Guides
advanced-guides/using-aliases
advanced-guides/register-utility-class
advanced-guides/injected-utility-class
- advanced-guides/configuring-service-provider
advanced-guides/building-library
diff --git a/Docs/advanced-guides/injected-utility-class.rst b/Docs/advanced-guides/injected-utility-class.rst
index 5ceaf89..36f0558 100644
--- a/Docs/advanced-guides/injected-utility-class.rst
+++ b/Docs/advanced-guides/injected-utility-class.rst
@@ -1,16 +1,16 @@
Injected Utility Class
========================================
-Library provides also Injected_ utility class.
-This base class has inject() method that can be used to inject services in simple inline way, also
-there are InjectedSingleton, InjectedScoped and InjectedTransient base classes that are inheriting
-from Injected and Registered classes to combine these two features. Injected class has also method
-getProvider(), raw provider can be used to get keyed services for example.
+The library provides also Injected_ utility class.
+This base class has inject() method that can be used to inject services in a simple inline way, also
+there are InjectedSingleton, InjectedScoped and InjectedTransient base classes that inherit
+from Injected and Registered classes to combine these two features. The injected class has also a method
+getProvider(), the raw provider can be used to get keyed services for example.
.. _Injected: ../reference/di/utils/injected.html
.. note::
- Class should inherit Injected constructor with 'using Injected::Injected;' in public section
+ The class should inherit Injected constructor with 'using Injected::Injected;' in the public section
.. literalinclude:: ../../Examples/Guides/InjectedUtilityClass.cpp
:caption: Examples/Guides/InjectedUtilityClass
diff --git a/Docs/advanced-guides/register-utility-class.rst b/Docs/advanced-guides/register-utility-class.rst
index ba17282..079de67 100644
--- a/Docs/advanced-guides/register-utility-class.rst
+++ b/Docs/advanced-guides/register-utility-class.rst
@@ -1,13 +1,13 @@
Register Utility Class
========================================
-Library provides simple template RegisterService_ utility class.
-This base class can be used to automatically register class in service collection with use of specialised TRegisterer.
+The library provides a simple template RegisterService_ utility class.
+This base class can be used to automatically register class in service collection with the use of a specialized TRegisterer.
There are already created aliases RegisterSingleton_, RegisterScoped_, RegisterTransient_ that are registering services
in GlobalServices_ singleton.
.. note::
- Class should inherit Injected constructor with 'using Injected::Injected;' in public section
+ The class should inherit Injected constructor with 'using Injected::Injected;' in the public section
.. _RegisterService: ../reference/di/utils/register.html
.. _RegisterSingleton: ../reference/di/utils/register.html
diff --git a/Docs/advanced-guides/using-aliases.rst b/Docs/advanced-guides/using-aliases.rst
index 5d82c33..4a2d9bb 100644
--- a/Docs/advanced-guides/using-aliases.rst
+++ b/Docs/advanced-guides/using-aliases.rst
@@ -2,11 +2,11 @@ Using Aliases
========================================
With the use of aliases, one service can be injected through its multiple base classes, also aliases can be chained.
-In case of injecting multiple aliases all real services will be fetched.
+In case of injecting multiple aliases, all real services will be fetched.
.. warning::
Using aliases is resource intensive, especially for injecting transient and multiple services, provider recursively
- traverses through aliases chain to find proper service. Mixing scoped and singleton aliases for same base type will
+ traverses through the aliases chain to find the proper service. Mixing scoped and singleton aliases for the same base type will
lead to undefined behavior
.. literalinclude:: ../../Examples/Guides/ServiceAliases.cpp
diff --git a/Docs/basic-guides.rst b/Docs/basic-guides.rst
index 81c6ed4..62380f3 100644
--- a/Docs/basic-guides.rst
+++ b/Docs/basic-guides.rst
@@ -12,3 +12,4 @@ Basic Guides
basic-guides/injecting-multiple-services
basic-guides/injection-rules
basic-guides/injecting-service-provider
+ basic-guides/configuring-service-provider
diff --git a/Docs/advanced-guides/configuring-service-provider.rst b/Docs/basic-guides/configuring-service-provider.rst
similarity index 91%
rename from Docs/advanced-guides/configuring-service-provider.rst
rename to Docs/basic-guides/configuring-service-provider.rst
index c90fca8..11d1f67 100644
--- a/Docs/advanced-guides/configuring-service-provider.rst
+++ b/Docs/basic-guides/configuring-service-provider.rst
@@ -10,8 +10,8 @@ read comment documentation for details:
:caption: Include/SevenBit/DI/ServiceProviderOptions.hpp
:language: C++
-Pass the custom options to the ServiceCollection buildServiceProvider method to change produced
-service provider behaviour
+Pass the custom options to the ServiceCollection buildServiceProvider method to change the produced
+service provider behavior
.. literalinclude:: ../../Examples/Guides/ConfiguredServiceProvider.cpp
:caption: Examples/Guides/ConfiguredServiceProvider
diff --git a/Docs/conf.py b/Docs/conf.py
index 5ec2320..e646c4f 100644
--- a/Docs/conf.py
+++ b/Docs/conf.py
@@ -12,7 +12,7 @@ def createIfNotExists(path):
project = "7bitDI"
copyright = "2023, 7BitCoder Sylwester Dawida"
author = "Sylwester Dawida"
-version = "3.2.0"
+version = "3.3.0"
extensions = [
"sphinx.ext.autodoc",
diff --git a/Docs/getting-started.rst b/Docs/getting-started.rst
index 79fb6e7..b942949 100644
--- a/Docs/getting-started.rst
+++ b/Docs/getting-started.rst
@@ -1,6 +1,16 @@
Getting Started
==========================
+Main Features
+--------------------
+
+* Implementation separation
+* Multiple implementations
+* Keyed services
+* Service aliases
+* Thread safe
+* Strong destruction order
+
Supported Platforms
--------------------
@@ -27,8 +37,8 @@ Installation
**There are a few ways of installation:**
-#. Using Cmake fetch content api - Recommended
- Update CMakeLists.txt file with following code
+#. Using Cmake fetch content API - Recommended
+ Update CMakeLists.txt file with the following code
.. code-block:: Cmake
@@ -36,7 +46,7 @@ Installation
FetchContent_Declare(
7bitDI
GIT_REPOSITORY https://github.com/7bitcoder/7bitDI.git
- GIT_TAG v3.2.0
+ GIT_TAG v3.3.0
)
FetchContent_MakeAvailable(7bitDI)
@@ -48,7 +58,7 @@ Installation
.. code-block:: Txt
[requires]
- 7bitdi/3.2.0
+ 7bitdi/3.3.0
change the version to newer if available, then run the command:
diff --git a/Examples/Guides/ConfiguredServiceProvider.cpp b/Examples/Guides/ConfiguredServiceProvider.cpp
index 2b7c8c7..17a67a4 100644
--- a/Examples/Guides/ConfiguredServiceProvider.cpp
+++ b/Examples/Guides/ConfiguredServiceProvider.cpp
@@ -39,6 +39,7 @@ int main()
options.strongDestructionOrder = true;
options.prebuildSingletons = true;
options.checkServiceGlobalUniqueness = false;
+ options.threadSafe = true;
ServiceProvider provider = ServiceCollection{}
.addSingleton()
diff --git a/Include/SevenBit/DI/CmakeDef.hpp b/Include/SevenBit/DI/CmakeDef.hpp
index 9dcd81a..56f8e83 100644
--- a/Include/SevenBit/DI/CmakeDef.hpp
+++ b/Include/SevenBit/DI/CmakeDef.hpp
@@ -13,5 +13,5 @@
#endif
#define _7BIT_DI_VERSION_MAJOR 3
-#define _7BIT_DI_VERSION_MINOR 2
+#define _7BIT_DI_VERSION_MINOR 3
/* #undef _7BIT_DI_VERSION_PATCH */
diff --git a/Include/SevenBit/DI/Details/Containers/Impl/ServiceInstancesMap.hpp b/Include/SevenBit/DI/Details/Containers/Impl/ServiceInstancesMap.hpp
index df48a37..1a8d494 100644
--- a/Include/SevenBit/DI/Details/Containers/Impl/ServiceInstancesMap.hpp
+++ b/Include/SevenBit/DI/Details/Containers/Impl/ServiceInstancesMap.hpp
@@ -42,9 +42,9 @@ namespace sb::di::details
{
for (auto it = _constructionOrder.rbegin(); it != _constructionOrder.rend(); ++it)
{
- if (const auto list = findInstances(*it))
+ if (const auto listPtr = findInstances(*it))
{
- list->clear();
+ listPtr->clear();
}
}
}
diff --git a/Include/SevenBit/DI/Details/Core/IServiceInstanceProviderRoot.hpp b/Include/SevenBit/DI/Details/Core/IServiceInstanceProviderRoot.hpp
index 2005116..67aab32 100644
--- a/Include/SevenBit/DI/Details/Core/IServiceInstanceProviderRoot.hpp
+++ b/Include/SevenBit/DI/Details/Core/IServiceInstanceProviderRoot.hpp
@@ -1,5 +1,7 @@
#pragma once
+#include
+
#include "SevenBit/DI/LibraryConfig.hpp"
#include "SevenBit/DI/Details/Containers/ServiceDescriptorsMap.hpp"
@@ -16,6 +18,8 @@ namespace sb::di::details
virtual ServiceInstancesCreator &getRootCreator() = 0;
+ virtual std::recursive_mutex *tryGetSyncMutex() = 0;
+
virtual ~IServiceInstanceProviderRoot() = default;
};
} // namespace sb::di::details
diff --git a/Include/SevenBit/DI/Details/Core/Impl/ServiceInstanceProvider.hpp b/Include/SevenBit/DI/Details/Core/Impl/ServiceInstanceProvider.hpp
index 9d39b3c..365eb3f 100644
--- a/Include/SevenBit/DI/Details/Core/Impl/ServiceInstanceProvider.hpp
+++ b/Include/SevenBit/DI/Details/Core/Impl/ServiceInstanceProvider.hpp
@@ -27,7 +27,7 @@ namespace sb::di::details
INLINE IServiceInstanceProvider::Ptr ServiceInstanceProvider::createScope() const
{
- return std::make_unique(_root, _options);
+ return std::make_unique(_root, getOptions());
}
INLINE const ServiceInstance &ServiceInstanceProvider::getInstance(const ServiceId &id)
diff --git a/Include/SevenBit/DI/Details/Core/Impl/ServiceInstanceProviderRoot.hpp b/Include/SevenBit/DI/Details/Core/Impl/ServiceInstanceProviderRoot.hpp
index f684349..05de8bb 100644
--- a/Include/SevenBit/DI/Details/Core/Impl/ServiceInstanceProviderRoot.hpp
+++ b/Include/SevenBit/DI/Details/Core/Impl/ServiceInstanceProviderRoot.hpp
@@ -10,12 +10,16 @@ namespace sb::di::details
: ServiceInstanceProvider(*this, options), _descriptorsMap(options.checkServiceGlobalUniqueness),
_singletons(options.strongDestructionOrder)
{
- _descriptorsMap.seal();
}
INLINE void ServiceInstanceProviderRoot::init(ServiceProvider &serviceProvider)
{
ServiceInstanceProvider::init(serviceProvider);
+ _descriptorsMap.seal();
+ if (getOptions().threadSafe)
+ {
+ _mutex = std::make_unique();
+ }
if (getOptions().prebuildSingletons)
{
prebuildSingletons();
diff --git a/Include/SevenBit/DI/Details/Core/ServiceInstanceProvider.hpp b/Include/SevenBit/DI/Details/Core/ServiceInstanceProvider.hpp
index 04ace73..33ea0cb 100644
--- a/Include/SevenBit/DI/Details/Core/ServiceInstanceProvider.hpp
+++ b/Include/SevenBit/DI/Details/Core/ServiceInstanceProvider.hpp
@@ -18,9 +18,9 @@ namespace sb::di::details
class EXPORT ServiceInstanceProvider : public IServiceInstanceProvider
{
ServiceProviderOptions _options;
+ IServiceInstanceProviderRoot &_root;
ServiceInstancesCreator _instancesCreator;
ServiceAliasesCreator _aliasesCreator;
- IServiceInstanceProviderRoot &_root;
ServiceInstancesMap _scoped;
public:
@@ -39,6 +39,8 @@ namespace sb::di::details
[[nodiscard]] IServiceInstanceProvider::Ptr createScope() const override;
+ std::recursive_mutex *tryGetSyncMutex() override { return _root.tryGetSyncMutex(); }
+
const ServiceInstance &getInstance(const TypeId serviceTypeId) override
{
return getInstance(ServiceId{serviceTypeId});
diff --git a/Include/SevenBit/DI/Details/Core/ServiceInstanceProviderRoot.hpp b/Include/SevenBit/DI/Details/Core/ServiceInstanceProviderRoot.hpp
index d51ac52..a051681 100644
--- a/Include/SevenBit/DI/Details/Core/ServiceInstanceProviderRoot.hpp
+++ b/Include/SevenBit/DI/Details/Core/ServiceInstanceProviderRoot.hpp
@@ -13,6 +13,7 @@ namespace sb::di::details
{
ServiceDescriptorsMap _descriptorsMap;
ServiceInstancesMap _singletons;
+ std::unique_ptr _mutex;
public:
using Ptr = std::unique_ptr;
@@ -26,7 +27,6 @@ namespace sb::di::details
_descriptorsMap(begin, end, options.checkServiceGlobalUniqueness),
_singletons(options.strongDestructionOrder)
{
- _descriptorsMap.seal();
}
void init(ServiceProvider &serviceProvider) override;
@@ -37,6 +37,8 @@ namespace sb::di::details
ServiceInstancesCreator &getRootCreator() override { return getCreator(); }
+ std::recursive_mutex *tryGetSyncMutex() override { return _mutex.get(); }
+
private:
void prebuildSingletons();
};
diff --git a/Include/SevenBit/DI/IServiceInstanceProvider.hpp b/Include/SevenBit/DI/IServiceInstanceProvider.hpp
index d9d5015..2ea20cd 100644
--- a/Include/SevenBit/DI/IServiceInstanceProvider.hpp
+++ b/Include/SevenBit/DI/IServiceInstanceProvider.hpp
@@ -1,6 +1,7 @@
#pragma once
#include
+#include
#include
#include
@@ -29,6 +30,13 @@ namespace sb::di
*/
[[nodiscard]] virtual Ptr createScope() const = 0;
+ /**
+ * @brief Get sync mutex
+ * @details Mutex can be used to synchronize service accesses between threads, can be null if synchronization is
+ * not needed
+ */
+ virtual std::recursive_mutex *tryGetSyncMutex() = 0;
+
/**
* @brief Returns service instance reference, might throw exception
* @details If service was not registered or was registered as transient, method throws exception
diff --git a/Include/SevenBit/DI/ServiceCollection.hpp b/Include/SevenBit/DI/ServiceCollection.hpp
index 502f5b8..cfe4bc4 100644
--- a/Include/SevenBit/DI/ServiceCollection.hpp
+++ b/Include/SevenBit/DI/ServiceCollection.hpp
@@ -476,8 +476,8 @@ namespace sb::di
*
* Example:
* @code{.cpp}
- * ServiceCollection{}.add(ServiceLifeTimes::Scoped, "key");
- * ServiceCollection{}.add(ServiceLifeTimes::Transient, "key");
+ * ServiceCollection{}.addKeyed(ServiceLifeTimes::Scoped, "key");
+ * ServiceCollection{}.addKeyed(ServiceLifeTimes::Transient, "key");
* @endcode
*/
template
@@ -515,8 +515,8 @@ namespace sb::di
*
* Example:
* @code{.cpp}
- * ServiceCollection{}.addSingleton("key");
- * ServiceCollection{}.addSingleton("key");
+ * ServiceCollection{}.addKeyedSingleton("key");
+ * ServiceCollection{}.addKeyedSingleton("key");
* @endcode
*/
template
@@ -554,8 +554,8 @@ namespace sb::di
*
* Example:
* @code{.cpp}
- * ServiceCollection{}.addScoped("key");
- * ServiceCollection{}.addScoped("key");
+ * ServiceCollection{}.addKeyedScoped("key");
+ * ServiceCollection{}.addKeyedScoped("key");
* @endcode
*/
template
@@ -593,8 +593,8 @@ namespace sb::di
*
* Example:
* @code{.cpp}
- * ServiceCollection{}.addTransient("key");
- * ServiceCollection{}.addTransient("key");
+ * ServiceCollection{}.addKeyedTransient("key");
+ * ServiceCollection{}.addKeyedTransient("key");
* @endcode
*/
template
@@ -629,7 +629,7 @@ namespace sb::di
* Example:
* @code{.cpp}
* TestClass test;
- * ServiceCollection{}.addSingleton("key", &test);
+ * ServiceCollection{}.addKeyedSingleton("key", &test);
* @endcode
*/
template ServiceCollection &addKeyedSingleton(std::string serviceKey, TService *service)
@@ -667,7 +667,7 @@ namespace sb::di
* Example:
* @code{.cpp}
* ImplementationClass implementation;
- * ServiceCollection{}.addSingleton("key", &implementation);
+ * ServiceCollection{}.addKeyedSingleton("key", &implementation);
* @endcode
*/
template
@@ -710,7 +710,7 @@ namespace sb::di
*
* Example:
* @code{.cpp}
- * ServiceCollection{}.add(ServiceLifeTimes::Scoped, "key",
+ * ServiceCollection{}.addKeyed(ServiceLifeTimes::Scoped, "key",
* []() { return std::make_unique(); });
* @endcode
*/
@@ -750,7 +750,8 @@ namespace sb::di
*
* Example:
* @code{.cpp}
- * ServiceCollection{}.addSingleton("key", []() { return std::make_unique(); });
+ * ServiceCollection{}.addKeyedSingleton("key", []() { return
+ * std::make_unique(); });
* @endcode
*/
template
@@ -789,7 +790,8 @@ namespace sb::di
*
* Example:
* @code{.cpp}
- * ServiceCollection{}.addScoped("key", []() { return std::make_unique(); });
+ * ServiceCollection{}.addKeyedScoped("key", []() { return std::make_unique();
+ * });
* @endcode
*/
template
@@ -828,7 +830,8 @@ namespace sb::di
*
* Example:
* @code{.cpp}
- * ServiceCollection{}.addTransient("key", []() { return std::make_unique(); });
+ * ServiceCollection{}.addKeyedTransient("key", []() { return
+ * std::make_unique(); });
* @endcode
*/
template
@@ -867,7 +870,8 @@ namespace sb::di
*
* Example:
* @code{.cpp}
- * ServiceCollection{}.add(ServiceLifeTimes::Transient, "key", []() { return std::make_unique(); });
+ * ServiceCollection{}.addKeyed(ServiceLifeTimes::Transient, "key", []() { return std::make_unique();
+ * });
* @endcode
*/
template
@@ -904,7 +908,7 @@ namespace sb::di
*
* Example:
* @code{.cpp}
- * ServiceCollection{}.addSingleton("key", []() { return std::make_unique(); });
+ * ServiceCollection{}.addKeyedSingleton("key", []() { return std::make_unique(); });
* @endcode
*/
template ServiceCollection &addKeyedSingleton(std::string serviceKey, FactoryFcn factory)
@@ -940,7 +944,7 @@ namespace sb::di
*
* Example:
* @code{.cpp}
- * ServiceCollection{}.addScoped("key", []() { return std::make_unique(); });
+ * ServiceCollection{}.addKeyedScoped("key", []() { return std::make_unique(); });
* @endcode
*/
template ServiceCollection &addKeyedScoped(std::string serviceKey, FactoryFcn factory)
@@ -976,7 +980,7 @@ namespace sb::di
*
* Example:
* @code{.cpp}
- * ServiceCollection{}.addTransient("key", []() { return std::make_unique(); });
+ * ServiceCollection{}.addKeyedTransient("key", []() { return std::make_unique(); });
* @endcode
*/
template ServiceCollection &addKeyedTransient(std::string serviceKey, FactoryFcn factory)
@@ -1026,7 +1030,7 @@ namespace sb::di
*
* Example:
* @code{.cpp}
- * ServiceCollection{}.addAlias("aliasKey", "key");
+ * ServiceCollection{}.addKeyedAlias("aliasKey", "key");
* @endcode
*/
template
@@ -1045,7 +1049,7 @@ namespace sb::di
*
* Example:
* @code{.cpp}
- * ServiceCollection{}.addAlias("aliasKey");
+ * ServiceCollection{}.addKeyedAlias("aliasKey");
* @endcode
*/
template ServiceCollection &addKeyedAlias(std::string serviceAliasKey)
diff --git a/Include/SevenBit/DI/ServiceProvider.hpp b/Include/SevenBit/DI/ServiceProvider.hpp
index 83c8345..8932be4 100644
--- a/Include/SevenBit/DI/ServiceProvider.hpp
+++ b/Include/SevenBit/DI/ServiceProvider.hpp
@@ -1,6 +1,7 @@
#pragma once
#include
+#include
#include
#include
@@ -15,37 +16,37 @@ namespace sb::di
class ServiceProvider
{
IServiceInstanceProvider::Ptr _instanceProvider;
+ std::recursive_mutex *_syncMutex = nullptr;
public:
using Ptr = std::unique_ptr;
/**
* @brief Constructs service provider with specified instance provider
+ * @details If service instance provider is nullptr, constructor throws exception
+ * @throws sb::di::NullPointerException
*/
explicit ServiceProvider(IServiceInstanceProvider::Ptr instanceProvider)
: _instanceProvider(std::move(instanceProvider))
{
details::Require::notNull(_instanceProvider);
- getInstanceProvider().init(*this);
+ _instanceProvider->init(*this);
+ _syncMutex = _instanceProvider->tryGetSyncMutex();
}
- ServiceProvider(const ServiceProvider &parent) = delete;
+ ServiceProvider(const ServiceProvider &) = delete;
ServiceProvider(ServiceProvider &&) = delete;
- ServiceProvider &operator=(const ServiceProvider &parent) = delete;
- ServiceProvider &operator=(ServiceProvider &&parent) = delete;
+ ServiceProvider &operator=(const ServiceProvider &) = delete;
+ ServiceProvider &operator=(ServiceProvider &&) = delete;
/**
* @brief Returns inner service instance provider
- * @details If service instance provider is nullptr, method throws exception
- * @throws sb::di::NullPointerException
*/
[[nodiscard]] const IServiceInstanceProvider &getInstanceProvider() const { return *_instanceProvider; }
/**
* @brief Returns inner service instance provider
- * @details If service instance provider is nullptr, method throws exception
- * @throws sb::di::NullPointerException
*/
IServiceInstanceProvider &getInstanceProvider() { return *_instanceProvider; }
@@ -96,12 +97,14 @@ namespace sb::di
*/
template TService *tryGetService()
{
- if (const auto instancePtr = getInstanceProvider().tryGetInstance(typeid(TService));
- instancePtr && *instancePtr)
- {
- return instancePtr->getAs();
- }
- return nullptr;
+ return safeAction([&]() -> TService * {
+ if (const auto instancePtr = getInstanceProvider().tryGetInstance(typeid(TService));
+ instancePtr && *instancePtr)
+ {
+ return instancePtr->getAs();
+ }
+ return nullptr;
+ });
}
/**
@@ -111,19 +114,21 @@ namespace sb::di
*
* Example:
* @code{.cpp}
- * auto provider = ServiceCollection{}.addScoped().buildServiceProvider();
+ * auto provider = ServiceCollection{}.addKeyedScoped("key").buildServiceProvider();
*
- * TestClass* service = provider.tryGetService();
+ * TestClass* service = provider.tryGetKeyedService("key");
* @endcode
*/
template TService *tryGetKeyedService(const std::string_view serviceKey)
{
- if (const auto instancePtr = getInstanceProvider().tryGetKeyedInstance(typeid(TService), serviceKey);
- instancePtr && *instancePtr)
- {
- return instancePtr->getAs();
- }
- return nullptr;
+ return safeAction([&]() -> TService * {
+ if (const auto instancePtr = getInstanceProvider().tryGetKeyedInstance(typeid(TService), serviceKey);
+ instancePtr && *instancePtr)
+ {
+ return instancePtr->getAs();
+ }
+ return nullptr;
+ });
}
/**
@@ -140,9 +145,11 @@ namespace sb::di
*/
template TService &getService()
{
- auto &instance = getInstanceProvider().getInstance(typeid(TService));
- details::RequireInstance::valid(instance);
- return *instance.getAs();
+ return *safeAction([&]() -> TService * {
+ auto &instance = getInstanceProvider().getInstance(typeid(TService));
+ details::RequireInstance::valid(instance);
+ return instance.getAs();
+ });
}
/**
@@ -153,16 +160,18 @@ namespace sb::di
*
* Example:
* @code{.cpp}
- * auto provider = ServiceCollection{}.addScoped().buildServiceProvider();
+ * auto provider = ServiceCollection{}.addKeyedScoped("key").buildServiceProvider();
*
- * TestClass& service = provider.getService();
+ * TestClass& service = provider.getKeyedService("key");
* @endcode
*/
template TService &getKeyedService(const std::string_view serviceKey)
{
- auto &instance = getInstanceProvider().getKeyedInstance(typeid(TService), serviceKey);
- details::RequireInstance::valid(instance);
- return *instance.getAs();
+ return *safeAction([&]() -> TService * {
+ auto &instance = getInstanceProvider().getKeyedInstance(typeid(TService), serviceKey);
+ details::RequireInstance::valid(instance);
+ return instance.getAs();
+ });
}
/**
@@ -181,14 +190,16 @@ namespace sb::di
*/
template std::vector getServices()
{
- if (auto instancesPtr = getInstanceProvider().tryGetInstances(typeid(TService)))
- {
- return instancesPtr->map([](const ServiceInstance &instance) {
- details::RequireInstance::valid(instance);
- return instance.getAs();
- });
- }
- return {};
+ return safeAction([&]() -> std::vector {
+ if (auto instancesPtr = getInstanceProvider().tryGetInstances(typeid(TService)))
+ {
+ return instancesPtr->map([](const ServiceInstance &instance) {
+ details::RequireInstance::valid(instance);
+ return instance.getAs();
+ });
+ }
+ return {};
+ });
}
/**
@@ -199,23 +210,25 @@ namespace sb::di
* Example:
* @code{.cpp}
* auto provider = ServiceCollection{}
- * .addScoped()
- * .addScoped()
+ * .addKeyedScoped("key")
+ * .addKeyedScoped("key")
* .buildServiceProvider();
*
- * std::vector services = provider.getServices();
+ * std::vector services = provider.getKeyedServices("key");
* @endcode
*/
template std::vector getKeyedServices(const std::string_view serviceKey)
{
- if (auto instancesPtr = getInstanceProvider().tryGetKeyedInstances(typeid(TService), serviceKey))
- {
- return instancesPtr->map([](const ServiceInstance &instance) {
- details::RequireInstance::valid(instance);
- return instance.getAs();
- });
- }
- return {};
+ return safeAction([&]() -> std::vector {
+ if (auto instancesPtr = getInstanceProvider().tryGetKeyedInstances(typeid(TService), serviceKey))
+ {
+ return instancesPtr->map([](const ServiceInstance &instance) {
+ details::RequireInstance::valid(instance);
+ return instance.getAs();
+ });
+ }
+ return {};
+ });
}
/**
@@ -231,11 +244,13 @@ namespace sb::di
*/
template std::unique_ptr tryCreateService()
{
- if (auto instance = getInstanceProvider().tryCreateInstance(typeid(TService)))
- {
- return instance.moveOutAsUniquePtr();
- }
- return nullptr;
+ return safeAction([&]() -> std::unique_ptr {
+ if (auto instance = getInstanceProvider().tryCreateInstance(typeid(TService)))
+ {
+ return instance.moveOutAsUniquePtr();
+ }
+ return nullptr;
+ });
}
/**
@@ -245,18 +260,20 @@ namespace sb::di
*
* Example:
* @code{.cpp}
- * auto provider = ServiceCollection{}.addTransient().buildServiceProvider();
+ * auto provider = ServiceCollection{}.addKeyedTransient("key").buildServiceProvider();
*
- * std::unique_ptr service = provider.tryCreateService();
+ * std::unique_ptr service = provider.tryCreateKeyedService("key");
* @endcode
*/
template std::unique_ptr tryCreateKeyedService(const std::string_view serviceKey)
{
- if (auto instance = getInstanceProvider().tryCreateKeyedInstance(typeid(TService), serviceKey))
- {
- return instance.moveOutAsUniquePtr();
- }
- return nullptr;
+ return safeAction([&]() -> std::unique_ptr {
+ if (auto instance = getInstanceProvider().tryCreateKeyedInstance(typeid(TService), serviceKey))
+ {
+ return instance.moveOutAsUniquePtr();
+ }
+ return nullptr;
+ });
}
/**
@@ -273,9 +290,11 @@ namespace sb::di
*/
template std::unique_ptr createService()
{
- auto instance = getInstanceProvider().createInstance(typeid(TService));
- details::RequireInstance::valid(instance);
- return instance.moveOutAsUniquePtr();
+ return safeAction([&]() -> std::unique_ptr {
+ auto instance = getInstanceProvider().createInstance(typeid(TService));
+ details::RequireInstance::valid(instance);
+ return instance.moveOutAsUniquePtr();
+ });
}
/**
@@ -286,16 +305,18 @@ namespace sb::di
*
* Example:
* @code{.cpp}
- * auto provider = ServiceCollection{}.addTransient().buildServiceProvider();
+ * auto provider = ServiceCollection{}.addKeyedTransient("key").buildServiceProvider();
*
- * std::unique_ptr service = provider.createService();
+ * std::unique_ptr service = provider.createKeyedService("key");
* @endcode
*/
template std::unique_ptr createKeyedService(const std::string_view serviceKey)
{
- auto instance = getInstanceProvider().createKeyedInstance(typeid(TService), serviceKey);
- details::RequireInstance::valid(instance);
- return instance.moveOutAsUniquePtr();
+ return safeAction([&]() -> std::unique_ptr {
+ auto instance = getInstanceProvider().createKeyedInstance(typeid(TService), serviceKey);
+ details::RequireInstance::valid(instance);
+ return instance.moveOutAsUniquePtr();
+ });
}
/**
@@ -312,16 +333,18 @@ namespace sb::di
*/
template TService createServiceInPlace()
{
- auto instance = getInstanceProvider().createInstanceInPlace(typeid(TService));
- details::RequireInstance::valid(instance);
- if constexpr (std::is_move_constructible_v)
- {
- return instance.moveOutAs();
- }
- else
- {
- return instance.copyAs();
- }
+ return safeAction([&]() -> TService {
+ auto instance = getInstanceProvider().createInstanceInPlace(typeid(TService));
+ details::RequireInstance::valid(instance);
+ if constexpr (std::is_move_constructible_v)
+ {
+ return instance.moveOutAs();
+ }
+ else
+ {
+ return instance.copyAs();
+ }
+ });
}
/**
@@ -332,23 +355,25 @@ namespace sb::di
*
* Example:
* @code{.cpp}
- * auto provider = ServiceCollection{}.addTransient().buildServiceProvider();
+ * auto provider = ServiceCollection{}.addKeyedTransient("key").buildServiceProvider();
*
- * TestClass service = provider.createServiceInPlace();
+ * TestClass service = provider.createKeyedServiceInPlace("key");
* @endcode
*/
template TService createKeyedServiceInPlace(const std::string_view serviceKey)
{
- auto instance = getInstanceProvider().createKeyedInstanceInPlace(typeid(TService), serviceKey);
- details::RequireInstance::valid(instance);
- if constexpr (std::is_move_constructible_v)
- {
- return instance.moveOutAs();
- }
- else
- {
- return instance.copyAs();
- }
+ return safeAction([&]() -> TService {
+ auto instance = getInstanceProvider().createKeyedInstanceInPlace(typeid(TService), serviceKey);
+ details::RequireInstance::valid(instance);
+ if constexpr (std::is_move_constructible_v)
+ {
+ return instance.moveOutAs();
+ }
+ else
+ {
+ return instance.copyAs();
+ }
+ });
}
/**
@@ -367,10 +392,12 @@ namespace sb::di
*/
template std::vector> createServices()
{
- auto instances = getInstanceProvider().tryCreateInstances(typeid(TService));
- return instances.map([&](ServiceInstance &instance) {
- details::RequireInstance::valid(instance);
- return instance.moveOutAsUniquePtr();
+ return safeAction([&]() -> std::vector> {
+ auto instances = getInstanceProvider().tryCreateInstances(typeid(TService));
+ return instances.map([&](ServiceInstance &instance) {
+ details::RequireInstance::valid(instance);
+ return instance.moveOutAsUniquePtr();
+ });
});
}
@@ -382,21 +409,34 @@ namespace sb::di
* Example:
* @code{.cpp}
* auto provider = ServiceCollection{}
- * .addTransient()
- * .addTransient()
+ * .addKeyedTransient("key")
+ * .addKeyedTransient("key")
* .buildServiceProvider();
*
- * std::vector> services = provider.createServices();
+ * std::vector> services = provider.createKeyedServices("key");
* @endcode
*/
template
std::vector> createKeyedServices(const std::string_view serviceKey)
{
- auto instances = getInstanceProvider().tryCreateKeyedInstances(typeid(TService), serviceKey);
- return instances.map([&](ServiceInstance &instance) {
- details::RequireInstance::valid(instance);
- return instance.moveOutAsUniquePtr();
+ return safeAction([&]() -> std::vector> {
+ auto instances = getInstanceProvider().tryCreateKeyedInstances(typeid(TService), serviceKey);
+ return instances.map([&](ServiceInstance &instance) {
+ details::RequireInstance::valid(instance);
+ return instance.moveOutAsUniquePtr();
+ });
});
}
+
+ private:
+ template auto safeAction(TAction action)
+ {
+ if (_syncMutex)
+ {
+ std::lock_guard lock{*_syncMutex};
+ return action();
+ }
+ return action();
+ }
};
} // namespace sb::di
diff --git a/Include/SevenBit/DI/ServiceProviderOptions.hpp b/Include/SevenBit/DI/ServiceProviderOptions.hpp
index c4265d2..8eea451 100644
--- a/Include/SevenBit/DI/ServiceProviderOptions.hpp
+++ b/Include/SevenBit/DI/ServiceProviderOptions.hpp
@@ -33,5 +33,11 @@ namespace sb::di
* @details If set to true provider will search for service in singleton container first then in scoped
*/
bool searchInSigletonsFirst = true;
+
+ /**
+ * @brief Enables thread safe mode
+ * @details Provider will synchronize service accesses between threads
+ */
+ bool threadSafe = false;
};
} // namespace sb::di
diff --git a/README.md b/README.md
index 54db97f..6aafa3b 100644
--- a/README.md
+++ b/README.md
@@ -17,19 +17,20 @@
-## Built With
+## Main Features
-- [Google Test](https://github.com/google/googletest)
-- [Google Benchmark](https://github.com/google/benchmark)
-- [Sphinx](https://www.sphinx-doc.org/en/master/)
-- [Breathe](https://breathe.readthedocs.io/en/latest/)
-- [Quom](https://pypi.org/project/quom/)
+- Implementation separation
+- Multiple implementations
+- Keyed services
+- Service aliases
+- Thread safe
+- Strong destruction order
## Supported Platforms
@@ -52,11 +53,15 @@ The library is officially supported on the following platforms:
If you notice any problems/bugs, please file an issue on the repository GitHub Issue Tracker. Pull requests containing
fixes are welcome!
+## Documentation
+
+https://7bitDI.readthedocs.io
+
## Installation
### There are a few ways of installation:
-### 1. Using Cmake fetch content api - Recommended
+### 1. Using Cmake fetch content API - Recommended
Update CMakeLists.txt file with the following code
@@ -65,7 +70,7 @@ include(FetchContent)
FetchContent_Declare(
7bitDI
GIT_REPOSITORY https://github.com/7bitcoder/7bitDI.git
- GIT_TAG v3.2.0
+ GIT_TAG v3.3.0
)
FetchContent_MakeAvailable(7bitDI)
@@ -78,7 +83,7 @@ Download and install A [Conan](https://conan.io/), and create conanfile.txt in t
```
[requires]
-7bitdi/3.2.0
+7bitdi/3.3.0
```
change the version to newer if available, then run the command:
@@ -230,6 +235,14 @@ actionA, actionB executed.
```
More examples and tutorials are available on the
-[Documentation & Examples](https://7bitDI.readthedocs.io/en/latest/index.html) page
+[Documentation & Examples](https://7bitDI.readthedocs.io) page
+
+## Built With
+
+- [Google Test](https://github.com/google/googletest)
+- [Google Benchmark](https://github.com/google/benchmark)
+- [Sphinx](https://www.sphinx-doc.org/en/master/)
+- [Breathe](https://breathe.readthedocs.io/en/latest/)
+- [Quom](https://pypi.org/project/quom/)
@7bitcoder Sylwester Dawida 2023
diff --git a/Tests/Helpers/Mocks/ServiceInstanceProviderMock.hpp b/Tests/Helpers/Mocks/ServiceInstanceProviderMock.hpp
index dc315cf..4fa46f9 100644
--- a/Tests/Helpers/Mocks/ServiceInstanceProviderMock.hpp
+++ b/Tests/Helpers/Mocks/ServiceInstanceProviderMock.hpp
@@ -7,6 +7,7 @@
struct ServiceInstanceProviderMock : public sb::di::IServiceInstanceProvider
{
MOCK_METHOD((std::unique_ptr), createScope, (), (const override));
+ MOCK_METHOD((std::recursive_mutex *), tryGetSyncMutex, (), (override));
MOCK_METHOD((void), init, (sb::di::ServiceProvider &), (override));
MOCK_METHOD((const sb::di::ServiceInstance *), tryGetInstance, (sb::di::TypeId serviceTypeId), (override));
MOCK_METHOD((const sb::di::ServiceInstance &), getInstance, (sb::di::TypeId serviceTypeId), (override));
diff --git a/Tests/Integration/ThreadSafeTest.cpp b/Tests/Integration/ThreadSafeTest.cpp
new file mode 100644
index 0000000..0211305
--- /dev/null
+++ b/Tests/Integration/ThreadSafeTest.cpp
@@ -0,0 +1,78 @@
+#include
+#include
+
+#include "../Helpers/Classes/Complex.hpp"
+#include
+
+class ThreadSafeTest : public testing::Test
+{
+ protected:
+ static void SetUpTestSuite() {}
+
+ ThreadSafeTest() {}
+
+ void SetUp() override {}
+
+ void TearDown() override {}
+
+ ~ThreadSafeTest() override = default;
+
+ static void TearDownTestSuite() {}
+};
+
+void getSafeServices(sb::di::ServiceProvider &provider)
+{
+ auto &service1 = provider.getService();
+ auto &service2 = provider.getService();
+ auto service3 = provider.createService();
+ auto &service4 = provider.getService();
+ auto &service5 = provider.getService();
+ auto &service6 = provider.getService();
+
+ EXPECT_EQ(service1.number(), 1);
+ EXPECT_EQ(service2.number(), 2);
+ EXPECT_EQ(service3->number(), 3);
+ EXPECT_EQ(service4.number(), 4);
+ EXPECT_EQ(service5.number(), 5);
+ EXPECT_EQ(service6.number(), 6);
+ EXPECT_EQ(service2.getOne(), &service1);
+ EXPECT_EQ(service3->getOne(), &service1);
+ EXPECT_EQ(service3->getTwo(), &service2);
+ EXPECT_EQ(service4.getOne(), &service1);
+ EXPECT_EQ(service4.getTwo(), &service2);
+ EXPECT_NE(service4.getThree().get(), service3.get());
+ EXPECT_EQ(service5.getOne(), &service1);
+ EXPECT_EQ(service5.getTwo(), &service2);
+ EXPECT_NE(service5.makeThree(), service3);
+ EXPECT_EQ(&service6.getOne(), &service1);
+ EXPECT_EQ(&service6.getTwo(), &service2);
+ EXPECT_FALSE(service6.getNonExisting());
+ EXPECT_NE(service6.makeThree(), service3);
+}
+
+TEST_F(ThreadSafeTest, ShouldGetSafeServices)
+{
+ sb::di::ServiceProviderOptions options;
+ options.threadSafe = true;
+
+ auto provider = sb::di::ServiceCollection{}
+ .addSingleton()
+ .addSingleton()
+ .addTransient()
+ .addScoped()
+ .addScoped()
+ .addScoped()
+ .buildServiceProvider(options);
+
+ constexpr size_t maxThreads = 50;
+ std::vector threads;
+ threads.reserve(maxThreads);
+ for (size_t i = 0; i < maxThreads; ++i)
+ {
+ threads.emplace_back(getSafeServices, std::ref(provider));
+ }
+ for (auto &th : threads)
+ {
+ th.join();
+ }
+}