Skip to content

Commit

Permalink
Fix resolution through more than one nested container
Browse files Browse the repository at this point in the history
  • Loading branch information
ybainier committed Jun 8, 2017
1 parent 508c8e4 commit e9d6c47
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 27 deletions.
36 changes: 36 additions & 0 deletions Hypodermic.Tests/NestedContainerTests.cpp
Expand Up @@ -54,6 +54,42 @@ namespace Testing
BOOST_CHECK_EQUAL(instance->dependency->i, expectedNumber);
}

BOOST_AUTO_TEST_CASE(should_resolve_dependency_in_nested_of_nested_container)
{
// Arrange
int expectedNumber1 = 42;
int expectedNumber2 = 43;

ContainerBuilder builder;
builder.registerType< TopLevelConstructor >();

auto container = builder.build();

ContainerBuilder nestedContainerBuilder1;
nestedContainerBuilder1.registerInstance(std::make_shared< NestedDependency >(expectedNumber1));
auto nestedContainer1 = nestedContainerBuilder1.buildNestedContainerFrom(*container);

// Act
ContainerBuilder nestedContainerBuilder2;
nestedContainerBuilder2.registerInstance(std::make_shared< NestedDependency >(expectedNumber2));
auto nestedContainer2 = nestedContainerBuilder2.buildNestedContainerFrom(*nestedContainer1);

// Assert
auto instance = container->resolve< TopLevelConstructor >();
BOOST_REQUIRE(instance != nullptr);
BOOST_CHECK(instance->dependency == nullptr);

instance = nestedContainer1->resolve< TopLevelConstructor >();
BOOST_REQUIRE(instance != nullptr);
BOOST_REQUIRE(instance->dependency != nullptr);
BOOST_CHECK_EQUAL(instance->dependency->i, expectedNumber1);

instance = nestedContainer2->resolve< TopLevelConstructor >();
BOOST_REQUIRE(instance != nullptr);
BOOST_REQUIRE(instance->dependency != nullptr);
BOOST_CHECK_EQUAL(instance->dependency->i, expectedNumber2);
}

BOOST_AUTO_TEST_CASE(should_keep_reference_on_top_level_registrations)
{
// Arrange
Expand Down
3 changes: 2 additions & 1 deletion Hypodermic/IRegistrationScope.h
Expand Up @@ -20,7 +20,8 @@ namespace Hypodermic

virtual bool tryGetRegistrations(const TypeAliasKey& typeAliasKey, std::vector< std::shared_ptr< RegistrationContext > >& registrationContexts) const = 0;

virtual std::shared_ptr< IRegistrationScope > clone() = 0;
virtual void copyTo(IRegistrationScope& other) const = 0;
virtual void addRegistrationContext(const std::shared_ptr< RegistrationContext >& registrationContext) = 0;
};

} // namespace Hypodermic
29 changes: 14 additions & 15 deletions Hypodermic/NestedRegistrationScope.h
@@ -1,25 +1,20 @@
#pragma once

#include <memory>
#include <mutex>
#include <vector>

#include "Hypodermic/IRegistration.h"
#include "Hypodermic/IRegistrationScope.h"
#include "Hypodermic/RegistrationScope.h"
#include "Hypodermic/TypeAliasKey.h"


namespace Hypodermic
{

class NestedRegistrationScope : public IRegistrationScope
class NestedRegistrationScope : public IRegistrationScope, public std::enable_shared_from_this< NestedRegistrationScope >
{
struct PrivateTag {};

public:
explicit NestedRegistrationScope(PrivateTag)
{
}

explicit NestedRegistrationScope(const std::shared_ptr< IRegistrationScope >& parentScope)
: m_parentScope(parentScope)
{
Expand All @@ -35,15 +30,16 @@ namespace Hypodermic
return scope().tryGetRegistrations(typeAliasKey, registrationContexts);
}

std::shared_ptr< IRegistrationScope > clone() override
void copyTo(IRegistrationScope& other) const override
{
std::lock_guard< decltype(m_mutex) > lock(m_mutex);

auto scopeClone = std::make_shared< NestedRegistrationScope >(PrivateTag());
scopeClone->m_parentScope = m_parentScope;
scopeClone->m_scope = m_scope;
scope().copyTo(other);
}

return scopeClone;
void addRegistrationContext(const std::shared_ptr< RegistrationContext >& registrationContext) override
{
writeScope().addRegistrationContext(registrationContext);
}

private:
Expand All @@ -62,14 +58,17 @@ namespace Hypodermic
std::lock_guard< decltype(m_mutex) > lock(m_mutex);

if (m_scope == nullptr)
m_scope = m_parentScope->clone();
{
m_scope = std::make_shared< RegistrationScope >();
m_parentScope->copyTo(*m_scope);
}

return *m_scope;
}

private:
std::shared_ptr< IRegistrationScope > m_parentScope;
std::shared_ptr< IRegistrationScope > m_scope;
std::shared_ptr< RegistrationScope > m_scope;
mutable std::recursive_mutex m_mutex;
};

Expand Down
58 changes: 47 additions & 11 deletions Hypodermic/RegistrationScope.h
Expand Up @@ -55,40 +55,76 @@ namespace Hypodermic
return true;
}

std::shared_ptr< IRegistrationScope > clone() override
void copyTo(IRegistrationScope& other) const override
{
std::lock_guard< decltype(m_mutex) > lock(m_mutex);

auto scopeClone = std::make_shared< RegistrationScope >();
scopeClone->m_registrationContextsByBaseTypes = m_registrationContextsByBaseTypes;
scopeClone->m_fallbackRegistrationContextsByBaseTypes = m_fallbackRegistrationContextsByBaseTypes;
copyRegistrationContextsTo(other);
copyFallbackRegistrationContextsTo(other);
}

return scopeClone;
void addRegistrationContext(const std::shared_ptr< RegistrationContext >& registrationContext) override
{
if (registrationContext->registration()->typeAliases().empty())
{
addRegistrationContext(createKeyForType(registrationContext->registration()->instanceType()), registrationContext);
return;
}

for (auto&& x : registrationContext->registration()->typeAliases())
addRegistrationContext(x.first, registrationContext);
}

private:
void addRegistration(const TypeAliasKey& typeAliasKey, const std::shared_ptr< IRegistration >& registration)
{
std::lock_guard< decltype(m_mutex) > lock(m_mutex);
addRegistrationContext(typeAliasKey, std::make_shared< RegistrationContext >(m_resolutionContainer, registration));
}

if (registration->isFallback())
void addRegistrationContext(const TypeAliasKey& typeAliasKey, const std::shared_ptr< RegistrationContext >& registrationContext)
{
if (registrationContext->registration()->isFallback())
{
addRegistration(m_fallbackRegistrationContextsByBaseTypes, typeAliasKey, registration);
addRegistrationContext(m_fallbackRegistrationContextsByBaseTypes, typeAliasKey, registrationContext);
}
else
{
addRegistration(m_registrationContextsByBaseTypes, typeAliasKey, registration);
addRegistrationContext(m_registrationContextsByBaseTypes, typeAliasKey, registrationContext);
}
}

template <class TRegistrationContextsByBaseTypes>
void addRegistration(TRegistrationContextsByBaseTypes& container, const TypeAliasKey& typeAliasKey, const std::shared_ptr< IRegistration >& registration)
void addRegistrationContext(TRegistrationContextsByBaseTypes& container, const TypeAliasKey& typeAliasKey, const std::shared_ptr< RegistrationContext >& registrationContext)
{
std::lock_guard< decltype(m_mutex) > lock(m_mutex);

auto it = container.find(typeAliasKey);
if (it == std::end(container))
it = container.insert(std::make_pair(typeAliasKey, std::vector< std::shared_ptr< RegistrationContext > >())).first;

it->second.push_back(std::make_shared< RegistrationContext >(m_resolutionContainer, registration));
it->second.push_back(registrationContext);
}

void copyRegistrationContextsTo(IRegistrationScope& other) const
{
for (auto& x : m_registrationContextsByBaseTypes)
{
for (auto& registrationContext : x.second)
{
other.addRegistrationContext(registrationContext);
}
}
}

void copyFallbackRegistrationContextsTo(IRegistrationScope& other) const
{
for (auto& x : m_fallbackRegistrationContextsByBaseTypes)
{
for (auto& registrationContext : x.second)
{
other.addRegistrationContext(registrationContext);
}
}
}

private:
Expand Down

0 comments on commit e9d6c47

Please sign in to comment.