Skip to content

Commit

Permalink
Resolution by name introduced
Browse files Browse the repository at this point in the history
  • Loading branch information
ybainier committed Apr 28, 2017
1 parent 4bd138d commit 66d7622
Show file tree
Hide file tree
Showing 17 changed files with 282 additions and 3 deletions.
2 changes: 2 additions & 0 deletions Hypodermic.Tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ set(HypodermicTests_sources
GithubTests.cpp
IsCompleteTests.cpp
MemoryTests.cpp
NamedTests.cpp
NestedContainerTests.cpp
PerformanceTests.cpp
PersistentInstanceRegistrationTests.cpp
ProvidedDependenciesTests.cpp
ProvidedInstanceFactoryRegistrationTests.cpp
ProvidedInstanceRegistrationTests.cpp
RegistrationTests.cpp
UseIfNoneTests.cpp
)

include_directories("..")
Expand Down
1 change: 1 addition & 0 deletions Hypodermic.Tests/Hypodermic.Tests.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="MemoryTests.cpp" />
<ClCompile Include="NamedTests.cpp" />
<ClCompile Include="NestedContainerTests.cpp" />
<ClCompile Include="PerformanceTests.cpp" />
<ClCompile Include="PersistentInstanceRegistrationTests.cpp" />
Expand Down
3 changes: 3 additions & 0 deletions Hypodermic.Tests/Hypodermic.Tests.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@
<ClCompile Include="UseIfNoneTests.cpp">
<Filter>Tests</Filter>
</ClCompile>
<ClCompile Include="NamedTests.cpp">
<Filter>Tests</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="Tests">
Expand Down
94 changes: 94 additions & 0 deletions Hypodermic.Tests/NamedTests.cpp
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions Hypodermic/AutowireableConstructorRegistrationDescriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -20,13 +21,15 @@ 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 >,
public RegistrationDescriptorOperations::With< AutowireableConstructorRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >
{
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 >;
Expand Down
3 changes: 3 additions & 0 deletions Hypodermic/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ set(Hypodermic_headers
MetaInsert.h
MetaMap.h
MetaPair.h
Named.h
NamedTypeAlias.h
NestedRegistrationScope.h
NoopLoggerSink.h
OnActivated.h
Expand Down Expand Up @@ -89,6 +91,7 @@ set(Hypodermic_headers
TypeAliases.h
TypeAliasKey.h
TypeInfo.h
UseIfNone.h
With.h
)

Expand Down
14 changes: 14 additions & 0 deletions Hypodermic/ComponentContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,20 @@ namespace Hypodermic
return resolveAll< T >(createKeyForType< T >());
}

/// <summary>
/// Resolve an instance of type T by both its type and a name
/// </summary>
/// <param name="T">The type to resolve (i.e. get an instance of T)</param>
/// <param name="name">The name of the object to resolve</param>
/// <returns>A shared pointer on an instance of type T</returns>
template <class T>
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 <class T>
std::shared_ptr< T > resolve(const TypeAliasKey& typeAliasKey)
Expand Down
13 changes: 13 additions & 0 deletions Hypodermic/Container.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,19 @@ namespace Hypodermic
return componentContext.resolveAll< T >();
}

/// <summary>
/// Resolve an instance of type T by both its type and a name
/// </summary>
/// <param name="T">The type to resolve (i.e. get an instance of T)</param>
/// <param name="name">The name of the object to resolve</param>
/// <returns>A shared pointer on an instance of type T</returns>
template <class T>
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;
Expand Down
2 changes: 2 additions & 0 deletions Hypodermic/Hypodermic.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@
<ClInclude Include="MetaInsert.h" />
<ClInclude Include="MetaMap.h" />
<ClInclude Include="MetaPair.h" />
<ClInclude Include="Named.h" />
<ClInclude Include="NamedTypeAlias.h" />
<ClInclude Include="PointerAlignment.h" />
<ClInclude Include="UseIfNone.h" />
<ClInclude Include="ResolutionInfo.h" />
Expand Down
6 changes: 6 additions & 0 deletions Hypodermic/Hypodermic.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -293,5 +293,11 @@
<ClInclude Include="UseIfNone.h">
<Filter>Descriptors\Operations</Filter>
</ClInclude>
<ClInclude Include="Named.h">
<Filter>Descriptors\Operations</Filter>
</ClInclude>
<ClInclude Include="NamedTypeAlias.h">
<Filter>TypeAlias</Filter>
</ClInclude>
</ItemGroup>
</Project>
69 changes: 69 additions & 0 deletions Hypodermic/Named.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#pragma once

#include <type_traits>

#include "Hypodermic/TypeAliasKey.h"


namespace Hypodermic
{
namespace RegistrationDescriptorOperations
{

template
<
class TDescriptor,
class TDescriptorInfo
>
class Named
{
private:
typedef typename TDescriptorInfo::InstanceType InstanceType;

template <class TBase, class T>
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 <class TBase>
struct EnforceBaseNotAlreadyRegistered
{
static_assert(!TDescriptorInfo::template IsBaseRegistered< TBase >::value, "TBase is already registered for instance T");

static void act() {}
};

public:
template <class TBase, class TDelayedDescriptor = TDescriptor>
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
51 changes: 51 additions & 0 deletions Hypodermic/NamedTypeAlias.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#pragma once

#include <string>
#include <utility>

#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
3 changes: 3 additions & 0 deletions Hypodermic/ProvidedInstanceFactoryRegistrationDescriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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 >;
Expand Down
Loading

0 comments on commit 66d7622

Please sign in to comment.