<|start_filename|>Hypodermic/RegistrationScope.h<|end_filename|>
#pragma once

#include <algorithm>
#include <mutex>
#include <unordered_map>
#include <vector>

#include "Hypodermic/IRegistration.h"
#include "Hypodermic/IRegistrationScope.h"
#include "Hypodermic/RegistrationContext.h"
#include "Hypodermic/ResolutionContainer.h"
#include "Hypodermic/TypeAliasKey.h"


namespace Hypodermic
{

    class RegistrationScope : public IRegistrationScope
    {
    public:
        void addRegistration(const std::shared_ptr< IRegistration >& registration) override
        {
            if (registration->typeAliases().empty())
            {
                addRegistration(createKeyForType(registration->instanceType()), registration);
                return;
            }

            for (auto&& x : registration->typeAliases())
                addRegistration(x.first, registration);
        }
        
        bool tryGetRegistrations(const TypeAliasKey& typeAliasKey, std::vector< std::shared_ptr< RegistrationContext > >& registrationContexts) const override
        {
            std::lock_guard< decltype(m_mutex) > lock(m_mutex);

            auto hasRegistrations = false;
            {
                auto it = m_fallbackRegistrationContextsByBaseTypes.find(typeAliasKey);
                if (it != std::end(m_fallbackRegistrationContextsByBaseTypes))
                {
                    auto& contexts = it->second;
                    std::copy(contexts.rbegin(), contexts.rend(), std::back_inserter(registrationContexts));

                    hasRegistrations = true;
                }
            }

            auto it = m_registrationContextsByBaseTypes.find(typeAliasKey);
            if (it == std::end(m_registrationContextsByBaseTypes))
                return hasRegistrations;

            auto& contexts = it->second;
            std::copy(std::begin(contexts), std::end(contexts), std::back_inserter(registrationContexts));
            return true;
        }

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

            copyRegistrationContextsTo(other);
            copyFallbackRegistrationContextsTo(other);
        }

        void addRegistration(const TypeAliasKey& typeAliasKey, const std::shared_ptr< IRegistration >& registration) override
        {
            addRegistrationContext(typeAliasKey, std::make_shared< RegistrationContext >(m_resolutionContainer, registration));
        }

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

        void copyRegistrationContextsTo(IRegistrationScope& other) const
        {
            copyRegistrationContextsToRegistrationScope(m_registrationContextsByBaseTypes, other);
        }

        void copyFallbackRegistrationContextsTo(IRegistrationScope& other) const
        {
            copyRegistrationContextsToRegistrationScope(m_fallbackRegistrationContextsByBaseTypes, other);
        }

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

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

            it->second.push_back(registrationContext);
        }

        template <class TRegistrationContextsByBaseTypes>
        void copyRegistrationContextsToRegistrationScope(const TRegistrationContextsByBaseTypes& contextsByBaseTypes, IRegistrationScope& other) const
        {
            for (auto& x : contextsByBaseTypes)
            {
                auto& typeAliasKey = x.first;
                auto& contexts = x.second;
            	
                for (auto& registrationContext : contexts)
                {
                    other.addRegistration(typeAliasKey, registrationContext->registration());
                }
            }
        }

    private:
        std::unordered_map< TypeAliasKey, std::vector< std::shared_ptr< RegistrationContext > > > m_registrationContextsByBaseTypes;
        std::unordered_map< TypeAliasKey, std::vector< std::shared_ptr< RegistrationContext > > > m_fallbackRegistrationContextsByBaseTypes;
        mutable std::recursive_mutex m_mutex;
        ResolutionContainer m_resolutionContainer;
    };

} // namespace Hypodermic

<|start_filename|>Hypodermic/As.h<|end_filename|>
#pragma once

#include "Hypodermic/EnforceBaseOf.h"
#include "Hypodermic/TypeAliasKey.h"


namespace Hypodermic
{
namespace RegistrationDescriptorOperations
{

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

    public:
        template <class TBase, class TDelayedDescriptor = TDescriptor>
        typename TDelayedDescriptor::template UpdateDescriptor
        <
            typename TDescriptorInfo::template RegisterBase< TBase >::Type
        >
        ::Type& as()
        {
            Extensions::EnforceBaseOf< TDescriptorInfo, TBase, InstanceType >::act();

            auto descriptor = static_cast< TDescriptor* >(this);
            descriptor->addTypeIfMissing(createKeyForType< TBase >(), [](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 ~As() = default;
    };

} // namespace RegistrationDescriptorOperations
} // namespace Hypodermic

<|start_filename|>Hypodermic.Tests/NamedTests.cpp<|end_filename|>
#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_resolve_named_component_as_itself)
    {
        // Arrange
        ContainerBuilder builder;

        // Act
        builder.registerType< DefaultConstructible1 >().named("default1");

        auto container = builder.build();

        // Assert
        auto instance = container->resolveNamed< DefaultConstructible1 >("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
        BOOST_CHECK_THROW(container->resolve< DefaultConstructibleBase >(), std::exception);
    }

    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_CASE(should_resolve_mixed_named_component_registration_and_type_registration)
	{
		// Arrange
		ContainerBuilder builder;

		// Act
		builder.registerType< DefaultConstructible1 >()
			  .as< DefaultConstructibleBase >() // this will get resolved by Container::resolve< T >()
			  .named< DefaultConstructibleBase >("this won't");

		auto container = builder.build();

		// Assert
    	auto instance = container->resolve< DefaultConstructibleBase >();
		BOOST_CHECK(instance == std::dynamic_pointer_cast< DefaultConstructible1 >(instance));
	}

	BOOST_AUTO_TEST_CASE(should_resolve_all_mixed_named_component_registration_and_type_registration)
	{
		// Arrange
		ContainerBuilder builder;

		// Act
		builder.registerType< DefaultConstructible1 >()
			  .as< DefaultConstructibleBase >() // this will get resolved by Container::resolveAll< T >()
			  .named< DefaultConstructibleBase >("this won't");

		builder.registerType< DefaultConstructible2 >()
    		   .named< DefaultConstructibleBase >("this won't get resolved by Container::resolveAll< T >()")
			   .as< DefaultConstructibleBase >(); // this will

		auto container = builder.build();

		// Assert
		auto instances = container->resolveAll< DefaultConstructibleBase >();
		BOOST_REQUIRE_EQUAL(instances.size(), 2u);
		BOOST_CHECK(instances[0] == std::dynamic_pointer_cast< DefaultConstructible1 >(instances[0]));
		BOOST_CHECK(instances[1] == std::dynamic_pointer_cast< DefaultConstructible2 >(instances[1]));
	}

    BOOST_AUTO_TEST_SUITE_END()

} // namespace Testing
} // namespace Hypodermic

<|start_filename|>Hypodermic/TypeInfo.h<|end_filename|>
#pragma once

#include <regex>
#include <string>
#include <typeinfo>

#include <boost/algorithm/string.hpp>

#if defined(__GNUC__)
# include <cxxabi.h>
#endif /* __GNUC__ */


namespace Hypodermic
{

    struct TypeInfo
    {
        explicit TypeInfo(const std::type_info& typeInfo)
            : m_typeInfo(&typeInfo)
            , m_fullyQualifiedName(dotNetify(demangleTypeName(m_typeInfo->name())))
        {
        }

        const std::type_info& intrinsicTypeInfo() const
        {
            return *m_typeInfo;
        }

        const std::string& fullyQualifiedName() const
        {
            return m_fullyQualifiedName;
        }

        bool operator==(const TypeInfo& rhs) const
        {
            return intrinsicTypeInfo() == rhs.intrinsicTypeInfo();
        }

        static std::string dotNetify(const std::string& typeName)
        {
            return boost::algorithm::replace_all_copy(typeName, "::", ".");
        }

        static std::string demangleTypeName(const std::string& typeName)
        {
#if defined(__GNUC__)
            int status;

            auto demangledName = abi::__cxa_demangle(typeName.c_str(), 0, 0, &status);
            if (demangledName == nullptr)
                return typeName;

            std::string result = demangledName;
            free(demangledName);
            return result;
#else
            std::string demangled = typeName;
            demangled = std::regex_replace(demangled, std::regex("(const\\s+|\\s+const)"), std::string());
            demangled = std::regex_replace(demangled, std::regex("(volatile\\s+|\\s+volatile)"), std::string());
            demangled = std::regex_replace(demangled, std::regex("(static\\s+|\\s+static)"), std::string());
            demangled = std::regex_replace(demangled, std::regex("(class\\s+|\\s+class)"), std::string());
            demangled = std::regex_replace(demangled, std::regex("(struct\\s+|\\s+struct)"), std::string());
            return demangled;
#endif /* defined(__GNUC__) */
        }

    private:
        const std::type_info* m_typeInfo;
        std::string m_fullyQualifiedName;
    };

namespace Utils
{

    template <class T>
    const TypeInfo& getMetaTypeInfo()
    {
        static TypeInfo result(typeid(T));
        return result;
    }

} // namespace Utils
} // namespace Hypodermic


#include <functional>
#include <typeindex>


namespace std
{

    template <>
    struct hash< Hypodermic::TypeInfo >
    {
        typedef Hypodermic::TypeInfo argument_type;
        typedef size_t result_type;

        size_t operator()(const Hypodermic::TypeInfo& value) const
        {
            return hash< type_index >()(type_index(value.intrinsicTypeInfo()));
        }
    };

} // namespace std

<|start_filename|>Hypodermic/IRegistrationRegistry.h<|end_filename|>
#pragma once

#include <memory>


namespace Hypodermic
{

    class IRegistration;

    
    class IRegistrationRegistry
    {
    public:
        virtual ~IRegistrationRegistry() = default;

        virtual void addRegistration(const std::shared_ptr< IRegistration >& registration) = 0;
        virtual void addRegistration(const TypeAliasKey& typeAliasKey, const std::shared_ptr< IRegistration >& registration) = 0;
    };

} // namespace Hypodermic

<|start_filename|>Hypodermic/EnforceDependencyNotAlreadyRegistered.h<|end_filename|>
#pragma once


namespace Hypodermic
{
namespace Extensions
{

	template <class TDescriptorInfo, class TDependency>
	struct EnforceDependencyNotAlreadyRegistered
	{
		template <class T>
		struct Act
		{
			static_assert(!TDescriptorInfo::template IsDependencyRegistered< TDependency >::value, "TDependency is already registered for instance T");

			typedef void Type;
		};

		static typename Act< typename TDescriptorInfo::InstanceType >::Type act() {}
	};

} // namespace Extensions
} // namespace Hypodermic

<|start_filename|>Hypodermic/EnforceBaseOf.h<|end_filename|>
#pragma once

#include <type_traits>


namespace Hypodermic
{
namespace Extensions
{

	template <class TDescriptorInfo, 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() {}
	};

} // namespace Extensions
} // namespace Hypodermic

<|start_filename|>Hypodermic/RegistrationDescriptorBuilder.h<|end_filename|>
#pragma once

#include <type_traits>

#include "Hypodermic/AutowireableConstructor.h"
#include "Hypodermic/AutowireableConstructorRegistrationDescriptor.h"
#include "Hypodermic/ProvidedInstanceFactoryRegistrationDescriptor.h"
#include "Hypodermic/ProvidedInstanceRegistrationDescriptor.h"


namespace Hypodermic
{

    namespace RegistrationDescriptorBuilder
    {

        template <class T>
        struct ForTypeConstruction
        {
            static_assert(Traits::HasAutowireableConstructor< T >::value, "Could not autowire T: you should consider registering T either by providing an instance or an instance factory");

            typedef AutowireableConstructorRegistrationDescriptor< RegistrationDescriptorInfo< T > > Type;
        };


        template <class T>
        struct ForProvidedInstance
        {
            typedef ProvidedInstanceRegistrationDescriptor< RegistrationDescriptorInfo< T, InstanceLifetimes::Persistent > > Type;
        };


        template <class TCallable>
        struct ForProvidedInstanceFactory
        {
        private:
            template <class T>
            struct IsSharedPtr : std::false_type {};

            template <class T>
            struct IsSharedPtr< std::shared_ptr< T > > : std::true_type {};

            template <class TFunctor>
            struct IsInvokableWithContainer
            {
                typedef char yes;
                typedef struct { char dummy[2]; } no;

                template <class T>
                static yes test(decltype(std::declval< T >()(std::declval< ComponentContext& >()))*);

                template <class T>
                static no test(...);

                static bool const value = sizeof(test< TFunctor >(nullptr)) == sizeof(yes);
            };

            template <class TFunctor>
            struct GetResultType
            {
                typedef decltype(std::declval< TFunctor >()(std::declval< ComponentContext& >())) Type;
            };

            template <class TFunctor>
            struct IsResultASharedPtr : IsSharedPtr< typename GetResultType< TFunctor >::Type >
            {
            };

        public:
            static_assert(IsInvokableWithContainer< TCallable >::value, "TCallable should have an invokable function which signature matches std::shared_ptr< T >(ComponentContext&)");
            static_assert(IsResultASharedPtr< TCallable >::value, "TCallable should return a std:shared_ptr");

            typedef typename GetResultType< TCallable >::Type ResultType;
            typedef typename ResultType::element_type InstanceType;

            typedef ProvidedInstanceFactoryRegistrationDescriptor< RegistrationDescriptorInfo< InstanceType > > Type;
        };

    } // namespace RegistrationDescriptorBuilder

} // namespace Hypodermic

<|start_filename|>Hypodermic.Tests/NestedContainerTests.cpp<|end_filename|>
#include "stdafx.h"

#include "Hypodermic/ContainerBuilder.h"

#include "TestingTypes.h"


namespace Hypodermic
{
namespace Testing
{

    BOOST_AUTO_TEST_SUITE(NestedContainerTests)

    BOOST_AUTO_TEST_CASE(should_not_resolve_non_auto_registerable_unregistered_dependency)
    {
        // Arrange
        ContainerBuilder builder;
        builder.registerType< TopLevelConstructor >();

        auto container = builder.build();

        // Act & Assert
        BOOST_CHECK_THROW(container->resolve< TopLevelConstructor >(), std::exception);
    }

    BOOST_AUTO_TEST_CASE(should_resolve_dependency_in_nested_container)
    {
        // Arrange
        int expectedNumber = 42;

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

        auto container = builder.build();

        // Act
        ContainerBuilder nestedContainerBuilder;
        nestedContainerBuilder.registerInstance(std::make_shared< NestedDependency >(expectedNumber));
        auto nestedContainer = nestedContainerBuilder.buildNestedContainerFrom(*container);

        // Assert
        BOOST_CHECK_THROW(container->resolve< TopLevelConstructor >(), std::exception);

        auto instance = nestedContainer->resolve< TopLevelConstructor >();
        BOOST_REQUIRE(instance != nullptr);
        BOOST_REQUIRE(instance->dependency != nullptr);
        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
         BOOST_CHECK_THROW(container->resolve< TopLevelConstructor >(), std::exception);
 
         auto 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
        int expectedNumber = 42;

        std::shared_ptr< Container > nestedContainer;
        {
            ContainerBuilder builder;
            builder.registerType< TopLevelConstructor >();

            auto container = builder.build();

            // Act
            ContainerBuilder nestedContainerBuilder;
            nestedContainerBuilder.registerInstance(std::make_shared< NestedDependency >(expectedNumber));
            nestedContainer = nestedContainerBuilder.buildNestedContainerFrom(*container);
        }

        // Assert
        BOOST_REQUIRE(nestedContainer != nullptr);

        auto instance = nestedContainer->resolve< TopLevelConstructor >();
        BOOST_REQUIRE(instance != nullptr);
        BOOST_REQUIRE(instance->dependency != nullptr);
        BOOST_CHECK_EQUAL(instance->dependency->i, expectedNumber);
    }

    BOOST_AUTO_TEST_CASE(should_resolve_all_registrations_of_a_type)
    {
        // Arrange
        std::shared_ptr< Container > nestedContainer;
        {
            ContainerBuilder builder;
            builder.registerType< DefaultConstructible1 >().as< DefaultConstructibleBase >();

            auto container = builder.build();

            ContainerBuilder nestedContainerBuilder;
            nestedContainerBuilder.registerType< DefaultConstructible2 >().as< DefaultConstructibleBase >();
            nestedContainer = nestedContainerBuilder.buildNestedContainerFrom(*container);
        }

        BOOST_REQUIRE(nestedContainer != nullptr);

        // Act
        auto instances = nestedContainer->resolveAll< DefaultConstructibleBase >();

        // Assert
        BOOST_REQUIRE_EQUAL(instances.size(), 2u);
        BOOST_CHECK(instances[0] != nullptr);
        BOOST_CHECK(instances[1] != nullptr);
        BOOST_CHECK(instances[0] != instances[1]);
    }

    BOOST_AUTO_TEST_CASE(should_resolve_a_type_using_its_last_registration)
    {
        // Arrange
        std::shared_ptr< Container > nestedContainer;
        {
            ContainerBuilder builder;
            builder.registerType< DefaultConstructible1 >().as< DefaultConstructibleBase >();

            auto container = builder.build();

            ContainerBuilder nestedContainerBuilder;
            nestedContainerBuilder.registerType< DefaultConstructible2 >().as< DefaultConstructibleBase >();
            nestedContainer = nestedContainerBuilder.buildNestedContainerFrom(*container);
        }

        BOOST_REQUIRE(nestedContainer != nullptr);

        // Act
        auto instance = nestedContainer->resolve< DefaultConstructibleBase >();

        // Assert
        BOOST_CHECK(instance != nullptr);
        BOOST_CHECK(std::dynamic_pointer_cast< DefaultConstructible2 >(instance) == instance);
    }

    BOOST_AUTO_TEST_CASE(should_no_longer_resolve_the_same_type_when_nested_container_is_released)
    {
        // Arrange
        std::shared_ptr< Container > container;

        std::shared_ptr< DefaultConstructibleBase > nestedInstance;
        {
            ContainerBuilder builder;
            builder.registerType< DefaultConstructible1 >().as< DefaultConstructibleBase >();

            container = builder.build();

            ContainerBuilder nestedContainerBuilder;
            nestedContainerBuilder.registerType< DefaultConstructible2 >().as< DefaultConstructibleBase >();
            auto nestedContainer = nestedContainerBuilder.buildNestedContainerFrom(*container);

            nestedInstance = nestedContainer->resolve< DefaultConstructibleBase >();
        }

        auto instance = container->resolve< DefaultConstructibleBase >();

        // Assert
        BOOST_CHECK(std::dynamic_pointer_cast< DefaultConstructible1 >(instance) == instance);
        BOOST_CHECK(std::dynamic_pointer_cast< DefaultConstructible2 >(nestedInstance) == nestedInstance);
    }

    BOOST_AUTO_TEST_CASE(should_no_longer_resolve_the_same_instance_when_nested_container_is_released)
    {
        // Arrange
        std::shared_ptr< Container > container;

        std::shared_ptr< DefaultConstructibleBase > nestedInstance1;
        std::shared_ptr< DefaultConstructibleBase > nestedInstance2;
        {
            ContainerBuilder builder;
            builder.registerType< DefaultConstructible1 >().as< DefaultConstructibleBase >().singleInstance();

            container = builder.build();

            ContainerBuilder nestedContainerBuilder;
            nestedContainerBuilder.registerType< DefaultConstructible2 >().as< DefaultConstructibleBase >().singleInstance();
            auto nestedContainer = nestedContainerBuilder.buildNestedContainerFrom(*container);

            nestedInstance1 = nestedContainer->resolve< DefaultConstructibleBase >();
            nestedInstance2 = nestedContainer->resolve< DefaultConstructibleBase >();
        }

        auto instance1 = container->resolve< DefaultConstructibleBase >();
        auto instance2 = container->resolve< DefaultConstructibleBase >();

        // Assert
        BOOST_CHECK(std::dynamic_pointer_cast< DefaultConstructible1 >(instance1) == instance2);
        BOOST_CHECK(std::dynamic_pointer_cast< DefaultConstructible2 >(nestedInstance1) == nestedInstance2);
        BOOST_CHECK(instance1 != nestedInstance1);
    }

    BOOST_AUTO_TEST_CASE(should_keep_using_resolved_instance_in_nested_container)
    {
        // Arrange
        std::shared_ptr< Container > container;

        std::shared_ptr< DefaultConstructibleBase > instance;
        std::shared_ptr< DefaultConstructibleBase > parentInstanceFromNestedContainer;
        {
            ContainerBuilder builder;
            builder.registerType< DefaultConstructible1 >().as< DefaultConstructibleBase >().singleInstance();

            container = builder.build();

            instance = container->resolve< DefaultConstructibleBase >();

            ContainerBuilder nestedContainerBuilder;
            nestedContainerBuilder.registerType< DefaultConstructible2 >();
            auto nestedContainer = nestedContainerBuilder.buildNestedContainerFrom(*container);

            auto nestedInstance = nestedContainer->resolve< DefaultConstructible2 >();
            BOOST_CHECK(nestedInstance != nullptr);

            parentInstanceFromNestedContainer = container->resolve< DefaultConstructibleBase >();
        }

        auto instanceAfterNestedContainerIsReleased = container->resolve< DefaultConstructibleBase >();

        // Assert
        BOOST_CHECK(std::dynamic_pointer_cast< DefaultConstructible1 >(instance) == instance);
        BOOST_CHECK(parentInstanceFromNestedContainer == instance);
        BOOST_CHECK(instanceAfterNestedContainerIsReleased == instance);
    }

	BOOST_AUTO_TEST_CASE(should_resolve_all_the_same_components)
    {
	    // Arrange
        ContainerBuilder builder;
        builder.registerType< DefaultConstructible1 >().as< DefaultConstructibleBase >().asSelf();
        builder.registerType< DefaultConstructible2 >().as< DefaultConstructibleBase >().asSelf();

        auto container = builder.build();
        auto components = container->resolveAll< DefaultConstructibleBase >();
    	
    	// Act
        ContainerBuilder nestedBuilder;
        auto nestedContainer = nestedBuilder.buildNestedContainerFrom(*container);

        auto componentsFromNestedContainer = nestedContainer->resolveAll< DefaultConstructibleBase >();

        // Assert
        BOOST_CHECK_EQUAL(components.size(), componentsFromNestedContainer.size());
    }

    BOOST_AUTO_TEST_SUITE_END()

} // namespace Testing
} // namespace Hypodermic

<|start_filename|>Hypodermic/With.h<|end_filename|>
#pragma once

#include "Hypodermic/EnforceBaseOf.h"
#include "Hypodermic/EnforceDependencyNotAlreadyRegistered.h"


namespace Hypodermic
{

    class ComponentContext;

namespace RegistrationDescriptorOperations
{

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

    public:
        template <class TDependency, class TDelayedDescriptor = TDescriptor>
        typename TDelayedDescriptor::template UpdateDescriptor
        <
            typename TDescriptorInfo::template RegisterDependencyFactory< TDependency >::Type
        >
        ::Type& with(const std::function< std::shared_ptr< TDependency >(ComponentContext&) >& factory)
        {
            Extensions::EnforceDependencyNotAlreadyRegistered< TDescriptorInfo, TDependency >::act();

            auto descriptor = static_cast< TDescriptor* >(this);
            descriptor->template addDependencyFactory< TDependency >
            (
                [factory](ComponentContext& c)
                {
                    return std::static_pointer_cast< void >(factory(c));
                }
            );

            auto updatedDescriptor = descriptor->template createUpdate< typename TDescriptorInfo::template RegisterDependencyFactory< TDependency >::Type >();
            descriptor->registrationDescriptorUpdated()(updatedDescriptor);

            return *updatedDescriptor;
        }

        template <class TDependency, class TProvidedDependency, class TDelayedDescriptor = TDescriptor>
        typename TDelayedDescriptor::template UpdateDescriptor
        <
            typename TDescriptorInfo::template RegisterDependencyInstance< TDependency, TProvidedDependency >::Type
        >
        ::Type& with(const std::shared_ptr< TProvidedDependency >& providedInstance)
        {
			Extensions::EnforceBaseOf< TDescriptorInfo, TDependency, TProvidedDependency >::act();
			Extensions::EnforceDependencyNotAlreadyRegistered< TDescriptorInfo, TDependency >::act();

            auto descriptor = static_cast< TDescriptor* >(this);
            descriptor->template addDependencyFactory< TDependency >
            (
                [providedInstance](ComponentContext&)
                {
                    return std::static_pointer_cast< void >(providedInstance);
                }
            );

            auto updatedDescriptor = descriptor->template createUpdate< typename TDescriptorInfo::template RegisterDependencyInstance< TDependency, TProvidedDependency >::Type >();
            descriptor->registrationDescriptorUpdated()(updatedDescriptor);

            return *updatedDescriptor;
        }

        template <class TDependency, class TProvidedDependency, class TDelayedDescriptor = TDescriptor>
        typename TDelayedDescriptor::template UpdateDescriptor
        <
            typename TDescriptorInfo::template RegisterDependency< TDependency, TProvidedDependency >::Type
        >
        ::Type& with()
        {
            Extensions::EnforceBaseOf< TDescriptorInfo, TDependency, TProvidedDependency >::act();
            Extensions::EnforceDependencyNotAlreadyRegistered< TDescriptorInfo, TDependency >::act();

            auto descriptor = static_cast< TDescriptor* >(this);
            descriptor->template addDependencyFactory< TDependency >([](ComponentContext& c)
            {
                return std::static_pointer_cast< void >(c.resolve< TProvidedDependency >());
            });

            auto updatedDescriptor = descriptor->template createUpdate< typename TDescriptorInfo::template RegisterDependency< TDependency, TProvidedDependency >::Type >();
            descriptor->registrationDescriptorUpdated()(updatedDescriptor);

            return *updatedDescriptor;
        }

    protected:
        virtual ~With() = default;
    };

} // namespace RegistrationDescriptorOperations
} // namespace Hypodermic

<|start_filename|>Hypodermic/NestedRegistrationScope.h<|end_filename|>
#pragma once

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

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


namespace Hypodermic
{

    class NestedRegistrationScope : public IRegistrationScope
    {
    public:
        explicit NestedRegistrationScope(const std::shared_ptr< IRegistrationScope >& parentScope)
            : m_parentScope(parentScope)
        {
        }

        void addRegistration(const std::shared_ptr< IRegistration >& registration) override
        {
            writeScope().addRegistration(registration);
        }
        
        bool tryGetRegistrations(const TypeAliasKey& typeAliasKey, std::vector< std::shared_ptr< RegistrationContext > >& registrationContexts) const override
        {
            return scope().tryGetRegistrations(typeAliasKey, registrationContexts);
        }

        void copyTo(IRegistrationScope& other) const override
        {
            scope().copyTo(other);
        }

        void addRegistration(const TypeAliasKey& typeAliasKey, const std::shared_ptr< IRegistration >& registration) override
        {
            writeScope().addRegistration(typeAliasKey, registration);
        }

    private:
        IRegistrationScope& scope() const
        {
            std::lock_guard< decltype(m_mutex) > lock(m_mutex);

            if (m_scope != nullptr)
                return *m_scope;

            return *m_parentScope;
        }

        IRegistrationScope& writeScope()
        {
            std::lock_guard< decltype(m_mutex) > lock(m_mutex);

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

            return *m_scope;
        }

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

} // namespace Hypodermic