From 66d76227ecbc4104767b32c5cb5809e3bcd8d838 Mon Sep 17 00:00:00 2001 From: Yohan Bainier Date: Fri, 28 Apr 2017 18:00:30 +0200 Subject: [PATCH] Resolution by name introduced --- Hypodermic.Tests/CMakeLists.txt | 2 + Hypodermic.Tests/Hypodermic.Tests.vcxproj | 1 + .../Hypodermic.Tests.vcxproj.filters | 3 + Hypodermic.Tests/NamedTests.cpp | 94 +++++++++++++++++++ ...ireableConstructorRegistrationDescriptor.h | 3 + Hypodermic/CMakeLists.txt | 3 + Hypodermic/ComponentContext.h | 14 +++ Hypodermic/Container.h | 13 +++ Hypodermic/Hypodermic.vcxproj | 2 + Hypodermic/Hypodermic.vcxproj.filters | 6 ++ Hypodermic/Named.h | 69 ++++++++++++++ Hypodermic/NamedTypeAlias.h | 51 ++++++++++ ...dedInstanceFactoryRegistrationDescriptor.h | 3 + .../ProvidedInstanceRegistrationDescriptor.h | 3 + Hypodermic/TypeAlias.h | 2 +- Hypodermic/TypeAliasKey.h | 14 ++- Hypodermic/TypeInfo.h | 2 +- 17 files changed, 282 insertions(+), 3 deletions(-) create mode 100644 Hypodermic.Tests/NamedTests.cpp create mode 100644 Hypodermic/Named.h create mode 100644 Hypodermic/NamedTypeAlias.h diff --git a/Hypodermic.Tests/CMakeLists.txt b/Hypodermic.Tests/CMakeLists.txt index cd88a7d..41c2b10 100644 --- a/Hypodermic.Tests/CMakeLists.txt +++ b/Hypodermic.Tests/CMakeLists.txt @@ -21,6 +21,7 @@ set(HypodermicTests_sources GithubTests.cpp IsCompleteTests.cpp MemoryTests.cpp + NamedTests.cpp NestedContainerTests.cpp PerformanceTests.cpp PersistentInstanceRegistrationTests.cpp @@ -28,6 +29,7 @@ set(HypodermicTests_sources ProvidedInstanceFactoryRegistrationTests.cpp ProvidedInstanceRegistrationTests.cpp RegistrationTests.cpp + UseIfNoneTests.cpp ) include_directories("..") diff --git a/Hypodermic.Tests/Hypodermic.Tests.vcxproj b/Hypodermic.Tests/Hypodermic.Tests.vcxproj index d30abef..6356676 100644 --- a/Hypodermic.Tests/Hypodermic.Tests.vcxproj +++ b/Hypodermic.Tests/Hypodermic.Tests.vcxproj @@ -197,6 +197,7 @@ NotUsing + diff --git a/Hypodermic.Tests/Hypodermic.Tests.vcxproj.filters b/Hypodermic.Tests/Hypodermic.Tests.vcxproj.filters index a9d64cf..4e5e3eb 100644 --- a/Hypodermic.Tests/Hypodermic.Tests.vcxproj.filters +++ b/Hypodermic.Tests/Hypodermic.Tests.vcxproj.filters @@ -53,6 +53,9 @@ Tests + + Tests + diff --git a/Hypodermic.Tests/NamedTests.cpp b/Hypodermic.Tests/NamedTests.cpp new file mode 100644 index 0000000..3f23816 --- /dev/null +++ b/Hypodermic.Tests/NamedTests.cpp @@ -0,0 +1,94 @@ +#include "stdafx.h" + +#include "Hypodermic/ContainerBuilder.h" + +#include "TestingTypes.h" + + +namespace Hypodermic +{ +namespace Testing +{ + + BOOST_AUTO_TEST_SUITE(NamedTests) + + BOOST_AUTO_TEST_CASE(should_resolve_named_component) + { + // Arrange + ContainerBuilder builder; + + // Act + builder.registerType< DefaultConstructible1 >().named< DefaultConstructibleBase >("default1"); + + auto container = builder.build(); + + // Assert + auto instance = container->resolveNamed< DefaultConstructibleBase >("default1"); + BOOST_CHECK(instance != nullptr); + } + + BOOST_AUTO_TEST_CASE(should_not_resolve_named_component_without_its_name) + { + // Arrange + ContainerBuilder builder; + + // Act + builder.registerType< DefaultConstructible1 >().named< DefaultConstructibleBase >("default1"); + + auto container = builder.build(); + + // Assert + auto instance = container->resolve< DefaultConstructibleBase >(); + BOOST_CHECK(instance == nullptr); + } + + BOOST_AUTO_TEST_CASE(should_resolve_the_right_named_component) + { + // Arrange + ContainerBuilder builder; + + // Act + builder.registerType< DefaultConstructible1 >().named< DefaultConstructibleBase >("default1"); + builder.registerType< DefaultConstructible2 >().named< DefaultConstructibleBase >("default2"); + + auto container = builder.build(); + + // Assert + auto instance = container->resolveNamed< DefaultConstructibleBase >("default1"); + BOOST_CHECK(instance != nullptr); + BOOST_CHECK(std::dynamic_pointer_cast< DefaultConstructible1 >(instance) == instance); + } + + BOOST_AUTO_TEST_CASE(should_not_conflict_with_anonymous_registrations) + { + // Arrange + ContainerBuilder builder; + + // Act + auto instance = std::make_shared< DefaultConstructible1 >(); + builder.registerInstance(instance).as< DefaultConstructibleBase >(); + + builder.registerType< DefaultConstructible1 >().named< DefaultConstructibleBase >("default"); + + auto container = builder.build(); + + // Assert + auto resolvedInstance = container->resolve< DefaultConstructibleBase >(); + auto namedInstance1 = container->resolveNamed< DefaultConstructibleBase >("default"); + auto namedInstance2 = container->resolveNamed< DefaultConstructibleBase >("default"); + + BOOST_CHECK(resolvedInstance == instance); + + BOOST_CHECK(namedInstance1 != nullptr); + BOOST_CHECK(namedInstance1 != instance); + + BOOST_CHECK(namedInstance2 != nullptr); + BOOST_CHECK(namedInstance2 != instance); + + BOOST_CHECK(namedInstance1 != namedInstance2); + } + + BOOST_AUTO_TEST_SUITE_END() + +} // namespace Testing +} // namespace Hypodermic \ No newline at end of file diff --git a/Hypodermic/AutowireableConstructorRegistrationDescriptor.h b/Hypodermic/AutowireableConstructorRegistrationDescriptor.h index f3a544a..691547c 100644 --- a/Hypodermic/AutowireableConstructorRegistrationDescriptor.h +++ b/Hypodermic/AutowireableConstructorRegistrationDescriptor.h @@ -5,6 +5,7 @@ #include "Hypodermic/ConstructorDescriptor.h" #include "Hypodermic/InstanceFactory.h" #include "Hypodermic/Log.h" +#include "Hypodermic/Named.h" #include "Hypodermic/OnActivated.h" #include "Hypodermic/RegistrationBuilder.h" #include "Hypodermic/RegistrationDescriptorBase.h" @@ -20,6 +21,7 @@ namespace Hypodermic class AutowireableConstructorRegistrationDescriptor : public RegistrationDescriptorBase< AutowireableConstructorRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >, public RegistrationDescriptorOperations::As< AutowireableConstructorRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >, public RegistrationDescriptorOperations::AsSelf< AutowireableConstructorRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >, + public RegistrationDescriptorOperations::Named< AutowireableConstructorRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >, public RegistrationDescriptorOperations::OnActivated< AutowireableConstructorRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >, public RegistrationDescriptorOperations::SingleInstance< AutowireableConstructorRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >, public RegistrationDescriptorOperations::UseIfNone< AutowireableConstructorRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >, @@ -27,6 +29,7 @@ namespace Hypodermic { friend class RegistrationDescriptorOperations::As< AutowireableConstructorRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >; friend class RegistrationDescriptorOperations::AsSelf< AutowireableConstructorRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >; + friend class RegistrationDescriptorOperations::Named< AutowireableConstructorRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >; friend class RegistrationDescriptorOperations::OnActivated< AutowireableConstructorRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >; friend class RegistrationDescriptorOperations::SingleInstance< AutowireableConstructorRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >; friend class RegistrationDescriptorOperations::UseIfNone< AutowireableConstructorRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >; diff --git a/Hypodermic/CMakeLists.txt b/Hypodermic/CMakeLists.txt index 6545526..ca7d4dc 100644 --- a/Hypodermic/CMakeLists.txt +++ b/Hypodermic/CMakeLists.txt @@ -58,6 +58,8 @@ set(Hypodermic_headers MetaInsert.h MetaMap.h MetaPair.h + Named.h + NamedTypeAlias.h NestedRegistrationScope.h NoopLoggerSink.h OnActivated.h @@ -89,6 +91,7 @@ set(Hypodermic_headers TypeAliases.h TypeAliasKey.h TypeInfo.h + UseIfNone.h With.h ) diff --git a/Hypodermic/ComponentContext.h b/Hypodermic/ComponentContext.h index 4e53724..595bf27 100644 --- a/Hypodermic/ComponentContext.h +++ b/Hypodermic/ComponentContext.h @@ -65,6 +65,20 @@ namespace Hypodermic return resolveAll< T >(createKeyForType< T >()); } + /// + /// Resolve an instance of type T by both its type and a name + /// + /// The type to resolve (i.e. get an instance of T) + /// The name of the object to resolve + /// A shared pointer on an instance of type T + template + std::shared_ptr< T > resolveNamed(const std::string& name) + { + static_assert(Traits::IsComplete< T >::value, "T should be a complete type"); + + return resolve< T >(createKeyForNamedType< T >(name)); + } + private: template std::shared_ptr< T > resolve(const TypeAliasKey& typeAliasKey) diff --git a/Hypodermic/Container.h b/Hypodermic/Container.h index 57b7738..b15bb22 100644 --- a/Hypodermic/Container.h +++ b/Hypodermic/Container.h @@ -75,6 +75,19 @@ namespace Hypodermic return componentContext.resolveAll< T >(); } + /// + /// Resolve an instance of type T by both its type and a name + /// + /// The type to resolve (i.e. get an instance of T) + /// The name of the object to resolve + /// A shared pointer on an instance of type T + template + std::shared_ptr< T > resolveNamed(const std::string& name) + { + ComponentContext componentContext(shared_from_this(), m_registrationScope, m_runtimeRegistrationBuilder); + return componentContext.resolveNamed< T >(name); + } + private: std::shared_ptr< IRegistrationScope > m_registrationScope; std::shared_ptr< IRuntimeRegistrationBuilder > m_runtimeRegistrationBuilder; diff --git a/Hypodermic/Hypodermic.vcxproj b/Hypodermic/Hypodermic.vcxproj index b88d9d3..c0cdc19 100644 --- a/Hypodermic/Hypodermic.vcxproj +++ b/Hypodermic/Hypodermic.vcxproj @@ -168,6 +168,8 @@ + + diff --git a/Hypodermic/Hypodermic.vcxproj.filters b/Hypodermic/Hypodermic.vcxproj.filters index 5667bf2..80f277f 100644 --- a/Hypodermic/Hypodermic.vcxproj.filters +++ b/Hypodermic/Hypodermic.vcxproj.filters @@ -293,5 +293,11 @@ Descriptors\Operations + + Descriptors\Operations + + + TypeAlias + \ No newline at end of file diff --git a/Hypodermic/Named.h b/Hypodermic/Named.h new file mode 100644 index 0000000..db84299 --- /dev/null +++ b/Hypodermic/Named.h @@ -0,0 +1,69 @@ +#pragma once + +#include + +#include "Hypodermic/TypeAliasKey.h" + + +namespace Hypodermic +{ +namespace RegistrationDescriptorOperations +{ + + template + < + class TDescriptor, + class TDescriptorInfo + > + class Named + { + private: + typedef typename TDescriptorInfo::InstanceType InstanceType; + + template + struct EnforceBaseOf + { + static_assert(std::is_base_of< TBase, T >::value && !std::is_same< TBase, T >::value, "TBase should be a base of T"); + + static void act() {} + }; + + template + struct EnforceBaseNotAlreadyRegistered + { + static_assert(!TDescriptorInfo::template IsBaseRegistered< TBase >::value, "TBase is already registered for instance T"); + + static void act() {} + }; + + public: + template + typename TDelayedDescriptor::template UpdateDescriptor + < + typename TDescriptorInfo::template RegisterBase< TBase >::Type + > + ::Type& named(const std::string& name) + { + EnforceBaseOf< TBase, InstanceType >::act(); + EnforceBaseNotAlreadyRegistered< TBase >::act(); + + auto descriptor = static_cast< TDescriptor* >(this); + descriptor->addTypeIfMissing(createKeyForNamedType< TBase >(name), [](const std::shared_ptr< void >& x) + { + auto instanceDynamicType = std::static_pointer_cast< InstanceType >(x); + auto instanceStaticType = std::static_pointer_cast< TBase >(instanceDynamicType); + return instanceStaticType; + }); + + auto updatedDescriptor = descriptor->template createUpdate< typename TDescriptorInfo::template RegisterBase< TBase >::Type >(); + descriptor->registrationDescriptorUpdated()(updatedDescriptor); + + return *updatedDescriptor; + } + + protected: + virtual ~Named() {} + }; + +} // namespace RegistrationDescriptorOperations +} // namespace Hypodermic \ No newline at end of file diff --git a/Hypodermic/NamedTypeAlias.h b/Hypodermic/NamedTypeAlias.h new file mode 100644 index 0000000..2129d0e --- /dev/null +++ b/Hypodermic/NamedTypeAlias.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include + +#include "Hypodermic/ITypeAlias.h" +#include "Hypodermic/TypeInfo.h" + + +namespace Hypodermic +{ + + class NamedTypeAlias : public ITypeAlias + { + public: + explicit NamedTypeAlias(const TypeInfo& typeInfo, const std::string& name) + : m_typeInfo(typeInfo) + , m_name(name) + { + } + + bool operator==(const ITypeAlias& rhs) const override + { + const ITypeAlias* self = this; + if (self == &rhs) + return true; + + auto rhsTypeAlias = dynamic_cast< const NamedTypeAlias* >(&rhs); + if (rhsTypeAlias == nullptr) + return false; + + return m_typeInfo == rhsTypeAlias->m_typeInfo && m_name == rhsTypeAlias->m_name; + } + + std::size_t hashCode() const override + { + auto hashCode = std::hash< std::type_index >()(m_typeInfo.intrinsicTypeInfo()); + return (hashCode * 397) ^ std::hash< std::string >()(m_name); + } + + const TypeInfo& typeInfo() const override + { + return m_typeInfo; + } + + private: + TypeInfo m_typeInfo; + std::string m_name; + }; + +} // namespace Hypodermic \ No newline at end of file diff --git a/Hypodermic/ProvidedInstanceFactoryRegistrationDescriptor.h b/Hypodermic/ProvidedInstanceFactoryRegistrationDescriptor.h index a78907a..2dc4034 100644 --- a/Hypodermic/ProvidedInstanceFactoryRegistrationDescriptor.h +++ b/Hypodermic/ProvidedInstanceFactoryRegistrationDescriptor.h @@ -4,6 +4,7 @@ #include "Hypodermic/AsSelf.h" #include "Hypodermic/InstanceFactory.h" #include "Hypodermic/Log.h" +#include "Hypodermic/Named.h" #include "Hypodermic/OnActivated.h" #include "Hypodermic/RegistrationBuilder.h" #include "Hypodermic/RegistrationDescriptorBase.h" @@ -18,12 +19,14 @@ namespace Hypodermic class ProvidedInstanceFactoryRegistrationDescriptor : public RegistrationDescriptorBase< ProvidedInstanceFactoryRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >, public RegistrationDescriptorOperations::As< ProvidedInstanceFactoryRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >, public RegistrationDescriptorOperations::AsSelf< ProvidedInstanceFactoryRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >, + public RegistrationDescriptorOperations::Named< ProvidedInstanceFactoryRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >, public RegistrationDescriptorOperations::OnActivated< ProvidedInstanceFactoryRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >, public RegistrationDescriptorOperations::SingleInstance< ProvidedInstanceFactoryRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >, public RegistrationDescriptorOperations::UseIfNone< ProvidedInstanceFactoryRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo > { friend class RegistrationDescriptorOperations::As< ProvidedInstanceFactoryRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >; friend class RegistrationDescriptorOperations::AsSelf< ProvidedInstanceFactoryRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >; + friend class RegistrationDescriptorOperations::Named< ProvidedInstanceFactoryRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >; friend class RegistrationDescriptorOperations::OnActivated< ProvidedInstanceFactoryRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >; friend class RegistrationDescriptorOperations::SingleInstance< ProvidedInstanceFactoryRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >; friend class RegistrationDescriptorOperations::UseIfNone< ProvidedInstanceFactoryRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >; diff --git a/Hypodermic/ProvidedInstanceRegistrationDescriptor.h b/Hypodermic/ProvidedInstanceRegistrationDescriptor.h index 18e2b02..d0988e4 100644 --- a/Hypodermic/ProvidedInstanceRegistrationDescriptor.h +++ b/Hypodermic/ProvidedInstanceRegistrationDescriptor.h @@ -3,6 +3,7 @@ #include "Hypodermic/As.h" #include "Hypodermic/AsSelf.h" #include "Hypodermic/Log.h" +#include "Hypodermic/Named.h" #include "Hypodermic/RegistrationBuilder.h" #include "Hypodermic/RegistrationDescriptorBase.h" #include "Hypodermic/UseIfNone.h" @@ -15,10 +16,12 @@ namespace Hypodermic class ProvidedInstanceRegistrationDescriptor : public RegistrationDescriptorBase< ProvidedInstanceRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >, public RegistrationDescriptorOperations::As< ProvidedInstanceRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >, public RegistrationDescriptorOperations::AsSelf< ProvidedInstanceRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >, + public RegistrationDescriptorOperations::Named< ProvidedInstanceRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >, public RegistrationDescriptorOperations::UseIfNone< ProvidedInstanceRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo > { friend class RegistrationDescriptorOperations::As< ProvidedInstanceRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >; friend class RegistrationDescriptorOperations::AsSelf< ProvidedInstanceRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >; + friend class RegistrationDescriptorOperations::Named< ProvidedInstanceRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >; friend class RegistrationDescriptorOperations::UseIfNone< ProvidedInstanceRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >; public: diff --git a/Hypodermic/TypeAlias.h b/Hypodermic/TypeAlias.h index 93789cb..67625ea 100644 --- a/Hypodermic/TypeAlias.h +++ b/Hypodermic/TypeAlias.h @@ -12,7 +12,7 @@ namespace Hypodermic class TypeAlias : public ITypeAlias { public: - TypeAlias(const TypeInfo& typeInfo) + explicit TypeAlias(const TypeInfo& typeInfo) : m_typeInfo(typeInfo) { } diff --git a/Hypodermic/TypeAliasKey.h b/Hypodermic/TypeAliasKey.h index c5a0a28..be1cec5 100644 --- a/Hypodermic/TypeAliasKey.h +++ b/Hypodermic/TypeAliasKey.h @@ -5,6 +5,7 @@ #include #include "Hypodermic/ITypeAlias.h" +#include "Hypodermic/NamedTypeAlias.h" #include "Hypodermic/TypeAlias.h" #include "Hypodermic/TypeInfo.h" @@ -41,16 +42,27 @@ namespace Hypodermic template - inline TypeAliasKey createKeyForType() + TypeAliasKey createKeyForType() { return createKeyForType(Utils::getMetaTypeInfo< T >()); } + template + TypeAliasKey createKeyForNamedType(const std::string& name) + { + return createKeyForNamedType(Utils::getMetaTypeInfo< T >(), name); + } + inline TypeAliasKey createKeyForType(const TypeInfo& typeInfo) { return TypeAliasKey(std::make_shared< TypeAlias >(typeInfo)); } + inline TypeAliasKey createKeyForNamedType(const TypeInfo& typeInfo, const std::string& name) + { + return TypeAliasKey(std::make_shared< NamedTypeAlias >(typeInfo, name)); + } + } // namespace Hypodermic diff --git a/Hypodermic/TypeInfo.h b/Hypodermic/TypeInfo.h index d20e0df..21603f4 100644 --- a/Hypodermic/TypeInfo.h +++ b/Hypodermic/TypeInfo.h @@ -74,7 +74,7 @@ namespace Utils { template - inline const TypeInfo& getMetaTypeInfo() + const TypeInfo& getMetaTypeInfo() { static TypeInfo result(typeid(T)); return result;