Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce asGroup() #59

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Hypodermic.Tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ set(HypodermicTests_sources
ContainerTests.cpp
DefaultConstructibleTests.cpp
FactoryTests.cpp
GroupRegistrationTests.cpp
IsCompleteTests.cpp
MemoryTests.cpp
NamedTests.cpp
Expand Down
208 changes: 208 additions & 0 deletions Hypodermic.Tests/GroupRegistrationTests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
#include "stdafx.h"

#include "Hypodermic/ContainerBuilder.h"

#include "TestingTypes.h"


namespace Hypodermic
{
namespace Testing
{

template <class TItem>
struct FilterBaseResolver
{
using Type = IFilter<TItem>;
};

struct GroupRegistrationFixture
{
~GroupRegistrationFixture()
{
Behavior::configureRuntimeRegistration(true);
}
};

BOOST_FIXTURE_TEST_SUITE(GroupRegistrationTests, GroupRegistrationFixture)

BOOST_AUTO_TEST_CASE(should_register_all_types_in_a_group)
{
// Arrange
ContainerBuilder builder;

// Act
builder.registerType< TypeWithGroups >()
.asGroup< IGroup >()
.singleInstance();

auto container = builder.build();

// Assert
auto instance1 = container->resolve< BaseType1 >();
BOOST_CHECK(instance1 != nullptr);

auto derived1 = std::dynamic_pointer_cast< TypeWithGroups >(instance1);
BOOST_CHECK(derived1 != nullptr);

auto instance2 = container->resolve< BaseType2 >();
BOOST_CHECK(instance2 != nullptr);

auto derived2 = std::dynamic_pointer_cast< TypeWithGroups >(instance1);
BOOST_CHECK(derived2 != nullptr);

BOOST_CHECK(derived1 == derived2);

Behavior::configureRuntimeRegistration(false);
BOOST_CHECK_THROW(container->resolve< TypeWithGroups >(), ResolutionException);
BOOST_CHECK_THROW(container->resolve< IFilter<int> >(), ResolutionException);
BOOST_CHECK_THROW(container->resolve< IFilter<char> >(), ResolutionException);
}

BOOST_AUTO_TEST_CASE(should_register_all_types_in_a_group_and_self)
{
// Arrange
ContainerBuilder builder;

// Act
builder.registerType< TypeWithGroups >()
.asGroup< IGroup >()
.asSelf();

auto container = builder.build();

// Assert
auto instance1 = container->resolve< BaseType1 >();
BOOST_CHECK(instance1 != nullptr);

auto derived1 = std::dynamic_pointer_cast< TypeWithGroups >(instance1);
BOOST_CHECK(derived1 != nullptr);

auto instance2 = container->resolve< BaseType2 >();
BOOST_CHECK(instance2 != nullptr);

auto derived2 = std::dynamic_pointer_cast< TypeWithGroups >(instance1);
BOOST_CHECK(derived2 != nullptr);

auto self = container->resolve< TypeWithGroups >();
BOOST_CHECK(derived1 == derived2);
BOOST_CHECK(derived1 != self);

Behavior::configureRuntimeRegistration(false);
BOOST_CHECK_THROW(container->resolve< IFilter<int> >(), ResolutionException);
BOOST_CHECK_THROW(container->resolve< IFilter<char> >(), ResolutionException);
}

BOOST_AUTO_TEST_CASE(should_register_all_types_in_two_groups)
{
// Arrange
ContainerBuilder builder;

// Act
builder.registerType< TypeWithGroups >()
.asGroup< IGroup >()
.asGroup< IGroupWithFilter, FilterBaseResolver >();

auto container = builder.build();

// Assert
auto instance1 = container->resolve< BaseType1 >();
BOOST_CHECK(instance1 != nullptr);

auto derived1 = std::dynamic_pointer_cast< TypeWithGroups >(instance1);
BOOST_CHECK(derived1 != nullptr);

auto instance2 = container->resolve< BaseType2 >();
BOOST_CHECK(instance2 != nullptr);

auto derived2 = std::dynamic_pointer_cast< TypeWithGroups >(instance1);
BOOST_CHECK(derived2 != nullptr);

auto instance3 = container->resolve< IFilter<int> >();
BOOST_CHECK(instance3 != nullptr);

auto derived3 = std::dynamic_pointer_cast< TypeWithGroups >(instance1);
BOOST_CHECK(derived3 != nullptr);

auto instance4 = container->resolve< IFilter<char> >();
BOOST_CHECK(instance4 != nullptr);

auto derived4 = std::dynamic_pointer_cast< TypeWithGroups >(instance1);
BOOST_CHECK(derived4 != nullptr);

BOOST_CHECK(derived1 == derived2);
BOOST_CHECK(derived1 == derived3);
BOOST_CHECK(derived1 == derived4);

Behavior::configureRuntimeRegistration(false);
BOOST_CHECK_THROW(container->resolve< TypeWithGroups >(), ResolutionException);
}

BOOST_AUTO_TEST_CASE(should_ignore_empty_group)
{
// Arrange
ContainerBuilder builder;

// Act
builder.registerType< TypeWithEmptyGroup >()
.asGroup< IGroup >();

auto container = builder.build();

// Assert
auto instance = container->resolve< TypeWithEmptyGroup >();
BOOST_CHECK(instance != nullptr);
}

BOOST_AUTO_TEST_CASE(should_be_functional_for_registerInstance)
{
// Arrange
ContainerBuilder builder;
auto instance = std::make_shared< TypeWithGroups >();

// Act
builder.registerInstance(instance).asGroup< IGroup >();

auto container = builder.build();

// Assert
auto instance1 = container->resolve< BaseType1 >();
BOOST_CHECK(instance1 == instance);

auto instance2 = container->resolve< BaseType2 >();
BOOST_CHECK(instance2 == instance);

Behavior::configureRuntimeRegistration(false);
BOOST_CHECK_THROW(container->resolve< TypeWithGroups >(), ResolutionException);
}

BOOST_AUTO_TEST_CASE(should_be_functional_for_registerInstanceFactory)
{
// Arrange
ContainerBuilder builder;
std::shared_ptr< TypeWithGroups > instance;

// Act
builder.registerInstanceFactory([&instance](ComponentContext&)
{
instance = std::make_shared< TypeWithGroups >();
return instance;
}).asGroup< IGroup >();

auto container = builder.build();

// Assert
auto instance1 = container->resolve< BaseType1 >();
BOOST_CHECK(instance1 == instance);

auto instance2 = container->resolve< BaseType2 >();
BOOST_CHECK(instance2 == instance);

Behavior::configureRuntimeRegistration(false);
BOOST_CHECK_THROW(container->resolve< TypeWithGroups >(), ResolutionException);
}

BOOST_AUTO_TEST_SUITE_END()

} // namespace Testing
} // namespace Hypodermic
1 change: 1 addition & 0 deletions Hypodermic.Tests/Hypodermic.Tests.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@
<ClCompile Include="ContainerTests.cpp" />
<ClCompile Include="DefaultConstructibleTests.cpp" />
<ClCompile Include="FactoryTests.cpp" />
<ClCompile Include="GroupRegistrationTests.cpp" />
<ClCompile Include="IsCompleteTests.cpp" />
<ClCompile Include="main.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
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 @@ -59,6 +59,9 @@
<ClCompile Include="RuntimeRegistrationTests.cpp">
<Filter>Tests</Filter>
</ClCompile>
<ClCompile Include="GroupRegistrationTests.cpp">
<Filter>Tests</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="Tests">
Expand Down
26 changes: 26 additions & 0 deletions Hypodermic.Tests/TestingTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -228,5 +228,31 @@ namespace Testing
std::function< std::shared_ptr< ILoader >() > factory;
};

template <class... TBases>
class IGroup : public TBases...
{
};

template <class TItem>
class IFilter
{
public:
virtual ~IFilter() = default;
};

template <class... TItems>
class IGroupWithFilter : public IFilter<TItems>...
{
};

class TypeWithGroups : public IGroup<BaseType1, BaseType2>,
public IGroupWithFilter<int, char>
{
};

class TypeWithEmptyGroup : public IGroup<>
{
};

} // namespace Testing
} // namespace Hypodermic
78 changes: 78 additions & 0 deletions Hypodermic/AsGroup.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#pragma once

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


namespace Hypodermic
{
namespace RegistrationDescriptorOperations
{

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

public:
template <template <class...> class TGroup, template <class> class TBaseResolver = DefaultBaseResolver, class TDelayedDescriptor = TDescriptor>
typename TDelayedDescriptor::template UpdateDescriptor
<
typename TDescriptorInfo::template RegisterGroup< TGroup, TBaseResolver >::Type
>
::Type& asGroup()
{
typedef typename TDescriptorInfo::template RegisterGroup< TGroup, TBaseResolver >::PackType PackType;
return asGroupItems< TGroup, TBaseResolver, TDelayedDescriptor >(PackType{});
}

private:
template <template <class...> class TGroup, template <class> class TBaseResolver, class TDelayedDescriptor, class... TBases>
typename TDelayedDescriptor::template UpdateDescriptor
<
typename TDescriptorInfo::template RegisterGroup< TGroup, TBaseResolver >::Type
>
::Type& asGroupItems(MetaPack< TBases... >)
{
auto descriptor = static_cast< TDescriptor* >(this);
addGroupItem< TBases... >(descriptor);

auto updatedDescriptor = descriptor->template createUpdate< typename TDescriptorInfo::template RegisterGroup< TGroup, TBaseResolver >::Type >();
descriptor->registrationDescriptorUpdated()(updatedDescriptor);

return *updatedDescriptor;
}

template <int = 0>
void addGroupItem(TDescriptor*)
{
}

template <class TBase, class... TOthers>
void addGroupItem(TDescriptor* descriptor)
{
Extensions::EnforceBaseOf< TDescriptorInfo, TBase, InstanceType >::act();

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;
});

addGroupItem< TOthers... >(descriptor);
}

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

} // namespace RegistrationDescriptorOperations
} // namespace Hypodermic
3 changes: 3 additions & 0 deletions Hypodermic/AutowireableConstructorRegistrationDescriptor.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include "Hypodermic/As.h"
#include "Hypodermic/AsGroup.h"
#include "Hypodermic/AsSelf.h"
#include "Hypodermic/ConstructorDescriptor.h"
#include "Hypodermic/InstanceFactory.h"
Expand All @@ -20,6 +21,7 @@ namespace Hypodermic
template <class TDescriptorInfo>
class AutowireableConstructorRegistrationDescriptor : public RegistrationDescriptorBase< AutowireableConstructorRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >,
public RegistrationDescriptorOperations::As< AutowireableConstructorRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >,
public RegistrationDescriptorOperations::AsGroup< AutowireableConstructorRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >,
public RegistrationDescriptorOperations::AsSelf< AutowireableConstructorRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >,
public RegistrationDescriptorOperations::Named< AutowireableConstructorRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >,
public RegistrationDescriptorOperations::OnActivated< AutowireableConstructorRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >,
Expand All @@ -28,6 +30,7 @@ namespace Hypodermic
public RegistrationDescriptorOperations::With< AutowireableConstructorRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >
{
friend class RegistrationDescriptorOperations::As< AutowireableConstructorRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >;
friend class RegistrationDescriptorOperations::AsGroup< AutowireableConstructorRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >;
friend class RegistrationDescriptorOperations::AsSelf< AutowireableConstructorRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >;
friend class RegistrationDescriptorOperations::Named< AutowireableConstructorRegistrationDescriptor< TDescriptorInfo >, TDescriptorInfo >;
friend class RegistrationDescriptorOperations::OnActivated< 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 @@ -14,6 +14,7 @@ set(Hypodermic_headers
ArgumentPack.h
ArgumentResolver.h
As.h
AsGroup.h
AsSelf.h
AutowireableConstructor.h
AutowireableConstructorRegistrationDescriptor.h
Expand All @@ -28,6 +29,7 @@ set(Hypodermic_headers
ContainerBuilder.h
ContainerInstanceRegistration.h
ContainerInstanceRegistrationActivator.h
DefaultBaseResolver.h
DependencyActivationException.h
DependencyFactories.h
DependencyFactory.h
Expand Down Expand Up @@ -63,6 +65,7 @@ set(Hypodermic_headers
MetaIdentity.h
MetaInsert.h
MetaMap.h
MetaPack.h
MetaPair.h
Named.h
NamedTypeAlias.h
Expand Down
Loading