feat add ioc
All checks were successful
linux-aarch64-cpu-gcc / linux-gcc-aarch64 (push) Successful in 1m19s
linux-mips64-gcc / linux-gcc-mips64el (Debug) (push) Successful in 1m38s
linux-arm-gcc / linux-gcc-armhf (push) Successful in 1m45s
linux-mips64-gcc / linux-gcc-mips64el (Release) (push) Successful in 2m2s
linux-x64-gcc / linux-gcc (Debug) (push) Successful in 2m3s
linux-x64-gcc / linux-gcc (Release) (push) Successful in 2m57s

This commit is contained in:
tqcq 2024-04-10 12:59:23 +00:00
parent 4ad27532b8
commit 80de3add78
4 changed files with 1000 additions and 1 deletions

View File

@ -195,7 +195,8 @@ if(SLED_BUILD_TESTS)
sled_add_test(NAME sled_string_view_test SRCS sled_add_test(NAME sled_string_view_test SRCS
src/sled/nonstd/string_view_test.cc) src/sled/nonstd/string_view_test.cc)
sled_add_test(NAME sled_expected_test SRCS src/sled/nonstd/expected_test.cc) sled_add_test(NAME sled_expected_test SRCS src/sled/nonstd/expected_test.cc)
sled_add_test(NAME sled_config_test SRCS src/sled//config_test.cc) sled_add_test(NAME sled_config_test SRCS src/sled/config_test.cc)
sled_add_test(NAME sled_ioc_test SRCS src/sled/ioc/ioc_test.cc)
endif(SLED_BUILD_TESTS) endif(SLED_BUILD_TESTS)
if(SLED_BUILD_FUZZ) if(SLED_BUILD_FUZZ)

933
src/sled/ioc/ioc.h Normal file
View File

@ -0,0 +1,933 @@
#ifndef SLED_SLED_IOC_IOC_H
#define SLED_SLED_IOC_IOC_H
#pragma once
#include <functional>
#include <memory>
#include <typeinfo>
#include <unordered_map>
#include <vector>
#define CINJECT_VERSION 1000000// 1.000.000
namespace cinject {
/////////////////////////////////////////////////////////
// INJECTION HELPERS FOR PRODUCTION
/////////////////////////////////////////////////////////
template<typename T>
struct ConstructorType {
typedef T Type;
};
#define CINJECT(constructorFunction) \
typedef cinject::ConstructorType<constructorFunction> ConstructorTypedef; \
constructorFunction
#define CINJECT_NAME(component_name) \
static const char *name() { return component_name; }
/////////////////////////////////////////////////////////
// TEMPLATE TYPE HELPERS
/////////////////////////////////////////////////////////
template<typename T>
struct always_false {
enum { value = false };
};
template<typename T>
struct trim_shared_ptr;
template<typename T>
struct trim_shared_ptr<std::shared_ptr<T>> {
typedef T type;
};
template<typename T>
struct is_shared_ptr : public std::false_type {};
template<typename T>
struct is_shared_ptr<std::shared_ptr<T>> : public std::true_type {};
template<typename T>
struct is_vector : public std::false_type {};
template<typename T>
struct is_vector<std::vector<T>> : public std::true_type {};
template<typename T>
struct trim_vector;
template<typename T>
struct trim_vector<std::vector<T>> {
typedef T type;
};
template<typename T>
struct has_constructor_injection {
typedef char true_type[1];
typedef char false_type[2];
template<typename C>
static true_type &check(typename C::ConstructorTypedef *);
template<typename>
static false_type &check(...);
static const bool value = sizeof(check<T>(0)) == sizeof(true_type);
};
template<typename T, typename = int>
struct has_name : std::false_type {};
template<typename T>
struct has_name<T, decltype((void) T::name(), 0)> : std::true_type {};
/////////////////////////////////////////////////////////
// Type HELPER
/////////////////////////////////////////////////////////
struct cinject_unspecified_component {};
struct component_type {
explicit component_type(const std::type_info &t, const std::string &customName = "")
: typeInfo(t),
customName(customName)
{}
const std::type_info &typeInfo;
const std::string customName;
std::string name() const { return customName.empty() ? typeInfo.name() : customName; }
bool specified() const { return typeInfo != typeid(cinject_unspecified_component); }
};
template<typename T>
static component_type
make_component_type(const std::string &customName = "")
{
return component_type(typeid(T), customName);
}
inline bool
operator==(const component_type &first, const component_type &other)
{
return first.typeInfo == other.typeInfo;
}
struct component_type_hash {
size_t operator()(const component_type &type) const { return type.typeInfo.hash_code(); }
};
template<typename T, class Enable = void>
struct type_name {
static const char *value() { return typeid(T).name(); }
};
template<typename T>
struct type_name<T, typename std::enable_if<has_name<T>::value>::type> {
static const char *value() { return T::name(); }
};
/////////////////////////////////////////////////////////
// EXCEPTIONS
/////////////////////////////////////////////////////////
class CircularDependencyFoundException : public std::logic_error {
public:
explicit CircularDependencyFoundException(const component_type &type)
: std::logic_error(std::string("Found circular dependency on object '") + type.name() + "'")
{}
};
class ComponentNotFoundException : public std::logic_error {
public:
explicit ComponentNotFoundException(const component_type &type)
: std::logic_error(std::string("Component for interface '") + type.name() + "' not found")
{}
};
class InvalidOperationException : public std::logic_error {
public:
explicit InvalidOperationException(const char *message) : std::logic_error(message) {}
};
/////////////////////////////////////////////////////////
// INJECTION CONTEXT
/////////////////////////////////////////////////////////
class Container;
class InjectionContext {
public:
InjectionContext(Container &container, component_type requesterComponent) : container_(container)
{
pushType(requesterComponent);
}
~InjectionContext() { popType(); }
Container &getContainer() { return container_; }
void pushType(component_type &type) { componentStack_.emplace_back(type); }
void popType() { componentStack_.pop_back(); }
const std::vector<component_type> &getComponentStack() { return componentStack_; }
const component_type &getRequester()
{
if (componentStack_.size() < 2) { throw InvalidOperationException("Context not valid."); }
return componentStack_[componentStack_.size() - 2];
}
InjectionContext(const InjectionContext &) = delete;
InjectionContext(const InjectionContext &&) = delete;
void operator=(const InjectionContext &) = delete;
void operator=(const InjectionContext &&) = delete;
private:
Container &container_;
std::vector<component_type> componentStack_;
};
/////////////////////////////////////////////////////////
// CONTEXT GUARD
/////////////////////////////////////////////////////////
class ContextGuard {
public:
ContextGuard(InjectionContext *context, component_type type) : context_(context), type_(type)
{
context_->pushType(type);
}
~ContextGuard() { context_->popType(); }
void ensureNoCycle()
{
const std::vector<component_type> &stack = context_->getComponentStack();
for (size_t i = 0; i < stack.size() - 1; ++i) {
if (stack[i] == stack.back()) { throw CircularDependencyFoundException(stack.back()); }
}
}
private:
InjectionContext *context_;
component_type type_;
};
/////////////////////////////////////////////////////////
// INSTANCE RETRIEVERS
/////////////////////////////////////////////////////////
class IInstanceRetriever {
public:
virtual ~IInstanceRetriever() = default;
};
template<typename TInterface>
class InstanceRetriever : public IInstanceRetriever {
public:
virtual std::shared_ptr<TInterface> forwardInstance(InjectionContext *context) = 0;
};
template<typename TImplementation, typename TInterface, typename TInstanceStorage>
class CastInstanceRetriever : public InstanceRetriever<TInterface> {
public:
explicit CastInstanceRetriever(std::shared_ptr<TInstanceStorage> storage) : storage_(storage) {}
std::shared_ptr<TInterface> forwardInstance(InjectionContext *context) override
{
return std::dynamic_pointer_cast<TInterface>(storage_->getInstance(context));
}
private:
std::shared_ptr<TInstanceStorage> storage_;
};
/////////////////////////////////////////////////////////
// CONTAINER DECLARATION
/////////////////////////////////////////////////////////
template<typename... TArgs>
class ComponentBuilder;
/// Container is used to configure bindings between interfaces and implementations.
///
/// Start with this class to configure your application.
/// ### Sample usage
/// ```
/// class IFoo
/// {};
///
/// class Foo : public IFoo
/// {
/// };
///
/// ...
///
/// Container container;
/// container.bind<IFoo>().to<Foo>();
///
/// std::shared_ptr<IFoo> foo = container.get<IFoo>()
/// ```
class Container {
template<typename... TComponents>
friend class ComponentBuilderBase;
public:
Container() = default;
explicit Container(const Container *parentContainer) : parentContainer_(parentContainer) {}
/// Initiates binding configuration for the TArgs component type.
///
/// Start with this class to configure your application.
/// @par Sample usage
/// ```
/// Container c;
/// c.bind<IAnimal>()
/// c.bind<IAnimal, IFlower>()
/// c.bind<IReceiver, ISender, IManager>()
/// ```
///
/// @tparam TArgs One or many components used for registration.
/// @return ComponentBuilder instance used to specific binding configuration
template<typename... TArgs>
ComponentBuilder<TArgs...> Bind();
/// Attempts to resolve all available instances registered to the requested type.
///
/// This function is used when the requested type is vector with simple type as the containing type
///
/// @par Sample usage
/// ```
/// container.get<std::vector<IFoo>>()
/// ```
///
/// @param context [in] Injection context.
/// @tparam TVectorWithInterface Requested type. Usually an interface.
/// @returns vector of instances registered to the requested type identified by the TVectorWithInterface template argument.
template<typename TVectorWithInterface>
typename std::enable_if<is_vector<TVectorWithInterface>::value
&& !is_shared_ptr<typename trim_vector<TVectorWithInterface>::type>::value
&& !std::is_reference<TVectorWithInterface>::value,
std::vector<std::shared_ptr<typename trim_vector<TVectorWithInterface>::type>>>::type
Get(InjectionContext *context = nullptr);
/// Attempts to resolve all available instances registered to the requested type.
///
/// This function is used when the requested type is vector with shared_ptr as the containing type
/// @par Sample usage
/// ```
/// container.get<std::vector<std::shared_ptr<IFoo>>>()
/// ```
///
/// @param context [in] Injection context.
/// @tparam TVectorWithInterface Requested type. Usually an interface.
/// @returns vector of instances registered to the requested type identified by the TVectorWithInterface template argument.
template<typename TVectorWithInterface>
typename std::enable_if<is_vector<TVectorWithInterface>::value
&& is_shared_ptr<typename trim_vector<TVectorWithInterface>::type>::value
&& !std::is_reference<TVectorWithInterface>::value,
std::vector<typename trim_vector<TVectorWithInterface>::type>>::type
Get(InjectionContext *context = nullptr);
/// Attempts to resolve an instance registered to the requested type.
///
/// This function is used when the requested type shared_ptr
///
/// @par Sample usage
/// ```
/// container.get<std::shared_ptr<IFoo>>()
/// ```
///
/// @param context [in] Injection context.
/// @tparam TInterfaceWithSharedPtr Requested type. Usually an interface.
/// @returns Instance registered to the requested type identified by the TInterfaceWithSharedPtr template argument.
template<typename TInterfaceWithSharedPtr>
typename std::enable_if<!is_vector<TInterfaceWithSharedPtr>::value && is_shared_ptr<TInterfaceWithSharedPtr>::value
&& !std::is_reference<TInterfaceWithSharedPtr>::value,
TInterfaceWithSharedPtr>::type
Get(InjectionContext *context = nullptr);
/// Attempts to resolve an instance registered to the requested type.
///
/// This function is used when the requested type simple type
///
/// @par Sample usage
/// ```
/// container.get<IFoo>()
/// ```
///
/// @param context [in] Injection context.
/// @tparam TInterface Requested type. Usually an interface.
/// @returns Instance registered to the requested type identified by the TInterface template argument.
template<typename TInterface>
typename std::enable_if<!is_vector<TInterface>::value && !is_shared_ptr<TInterface>::value
&& !std::is_reference<TInterface>::value,
std::shared_ptr<TInterface>>::type
Get(InjectionContext *context = nullptr);
/// Attempts to resolve an instance registered to the requested type.
///
/// This function is used when the requested type is reference typ
///
/// @par Sample usage
/// ```
/// container.get<const std::vector<IAny>&>()
/// container.get<const std::vector<std::shared_ptr<IAny>>&>()
/// ```
///
/// @note Only vector is supported
/// @param context [in] Injection context.
/// @tparam TInterface Requested type. Usually an interface.
/// @returns Instance registered to the requested type identified by the TInterface template argument.
template<typename TInterface>
typename std::enable_if<std::is_reference<TInterface>::value
&& std::is_const<typename std::remove_reference<TInterface>::type>::value,
typename std::remove_reference<TInterface>::type>::type
Get(InjectionContext *context = nullptr);
private:
void findInstanceRetrievers(std::vector<std::shared_ptr<IInstanceRetriever>> &instanceRetrievers,
const component_type &type) const;
const Container *parentContainer_ = nullptr;
std::unordered_map<component_type, std::vector<std::shared_ptr<IInstanceRetriever>>, component_type_hash>
registrations_;
};
/////////////////////////////////////////////////////////
// CONSTRUCTOR FACTORY
/////////////////////////////////////////////////////////
struct ctor_arg_resolver {
explicit ctor_arg_resolver(InjectionContext *context) : context_(context) {}
template<typename TCtorArgument, typename std::enable_if<!std::is_pointer<TCtorArgument>::value, int>::type = 0>
operator TCtorArgument()
{
return context_->getContainer().Get<TCtorArgument>(context_);
}
InjectionContext *context_;
};
template<typename TInstance>
struct ctor_arg_resolver_1st {
explicit ctor_arg_resolver_1st(InjectionContext *context) : context_(context) {}
template<typename TCtorArgument,
typename std::enable_if<!std::is_same<TCtorArgument, TInstance>::value
&& !std::is_same<TCtorArgument, TInstance &>::value
&& !std::is_pointer<TCtorArgument>::value,
int>::type
= 0>
operator TCtorArgument()
{
return context_->getContainer().Get<TCtorArgument>(context_);
}
InjectionContext *context_;
};
template<typename T, class TEnable = void>
class ConstructorFactory {
static_assert(always_false<T>::value, "Could not deduce any ConstructorFactory");
};
// Factory for trivial constructors with no arguments
template<typename TInstance>
class ConstructorFactory<TInstance,
typename std::enable_if<!has_constructor_injection<TInstance>::value
&& std::is_constructible<TInstance>::value>::type> {
public:
std::shared_ptr<TInstance> createInstance(InjectionContext *context) { return std::make_shared<TInstance>(); }
};
// Factory for automatic injection for one to ten arguments
template<typename TInstance>
class ConstructorFactory<TInstance,
typename std::enable_if<!has_constructor_injection<TInstance>::value
&& !std::is_constructible<TInstance>::value>::type> {
public:
std::shared_ptr<TInstance> createInstance(InjectionContext *context)
{
return try_instantiate(
ctor_arg_resolver(context),
ctor_arg_resolver(context),
ctor_arg_resolver(context),
ctor_arg_resolver(context),
ctor_arg_resolver(context),
ctor_arg_resolver(context),
ctor_arg_resolver(context),
ctor_arg_resolver(context),
ctor_arg_resolver(context),
ctor_arg_resolver_1st<TInstance>(context));
}
private:
template<typename TArg, typename TNextArg, typename... TRestArgs>
typename std::enable_if<std::is_constructible<TInstance, TArg, TNextArg, TRestArgs...>::value,
std::shared_ptr<TInstance>>::type
try_instantiate(TArg a1, TNextArg a2, TRestArgs... args)
{
return std::make_shared<TInstance>(a1, a2, args...);
}
template<typename TArg, typename TNextArg, typename... TRestArgs>
typename std::enable_if<!std::is_constructible<TInstance, TArg, TNextArg, TRestArgs...>::value,
std::shared_ptr<TInstance>>::type
try_instantiate(TArg a1, TNextArg a2, TRestArgs... args)
{
return try_instantiate(a2, args...);
}
template<typename TArg>
typename std::enable_if<std::is_constructible<TInstance, TArg>::value, std::shared_ptr<TInstance>>::type
try_instantiate(TArg arg)
{
return std::make_shared<TInstance>(arg);
}
template<typename TArg>
typename std::enable_if<!std::is_constructible<TInstance, TArg>::value, std::shared_ptr<TInstance>>::type
try_instantiate(TArg arg)
{
static_assert(always_false<TInstance>::value,
"Could not find any suitable constructor for injection. Try explicitly mark the constructor "
"using CINJECT macro");
}
};
template<typename TInstance>
struct ConstructorInvoker;
template<typename TInstance, typename... TConstructorArgs>
struct ConstructorInvoker<TInstance(TConstructorArgs...)> {
static std::shared_ptr<TInstance> invoke(InjectionContext *context)
{
Container &container = context->getContainer();
return std::make_shared<TInstance>(container.Get<TConstructorArgs>(context)...);
}
};
// Factory for injection using the CINJECT macro
template<typename TInstance>
class ConstructorFactory<TInstance, typename std::enable_if<has_constructor_injection<TInstance>::value>::type> {
public:
std::shared_ptr<TInstance> createInstance(InjectionContext *context)
{
return ConstructorInvoker<typename TInstance::ConstructorTypedef::Type>::invoke(context);
}
};
/////////////////////////////////////////////////////////
// FUNCTION FACTORY
/////////////////////////////////////////////////////////
template<typename TInstance>
struct FunctionFactory {
typedef std::function<std::shared_ptr<TInstance>(InjectionContext *)> FactoryMethodType;
FunctionFactory(FactoryMethodType factoryMethod) : factoryMethod_(factoryMethod) {}
std::shared_ptr<TInstance> createInstance(InjectionContext *context) { return factoryMethod_(context); }
FactoryMethodType factoryMethod_;
};
/////////////////////////////////////////////////////////
// CONSTANT FACTORY
/////////////////////////////////////////////////////////
template<typename TInstance>
struct ConstantFactory {
ConstantFactory(std::shared_ptr<TInstance> instance) : instance_(instance) {}
std::shared_ptr<TInstance> createInstance(InjectionContext *context) { return instance_; }
std::shared_ptr<TInstance> instance_;
};
/////////////////////////////////////////////////////////
// INSTANCE STORAGE
/////////////////////////////////////////////////////////
template<typename TImplementation, typename TFactory>
class InstanceStorage {
public:
explicit InstanceStorage(TFactory factory) : factory_(factory) {}
virtual std::shared_ptr<TImplementation> getInstance(InjectionContext *context)
{
if (!isSingleton_) { return createInstance(context); }
if (instance_ == nullptr) { instance_ = createInstance(context); }
return instance_;
}
void setSingleton(bool value) { isSingleton_ = value; }
void setName(const std::string &name) { name_ = name; }
void setName(std::string &&name) { name_ = name; }
private:
std::shared_ptr<TImplementation> createInstance(InjectionContext *context)
{
ContextGuard guard(
context,
make_component_type<TImplementation>(!name_.empty() ? name_ : type_name<TImplementation>::value()));
guard.ensureNoCycle();
return factory_.createInstance(context);
}
TFactory factory_;
bool isSingleton_ = false;
std::shared_ptr<TImplementation> instance_;
std::string name_;
};
/////////////////////////////////////////////////////////
// STORAGE CONFIGURATION
/////////////////////////////////////////////////////////
/// Configures instance storage.
///
/// Instance can be either transient or singleton. If it's singleton, then the same instance is provided whenever it's requested. Otherwise
/// a new instance is always created.
/// @tparam TInstanceStorage Instance storage type.
template<typename TInstanceStorage>
class StorageConfiguration {
public:
explicit StorageConfiguration(std::shared_ptr<TInstanceStorage> storage) : storage_(storage) {}
/// Configures the instance to be handled as singleton
///
///
StorageConfiguration &InSingletonScope()
{
storage_->setSingleton(true);
return *this;
}
/// Configures the instance name
///
///
StorageConfiguration &Alias(const std::string &name)
{
storage_->setName(name);
return *this;
}
/// Configures the instance name
///
///
StorageConfiguration &Alias(std::string &&name)
{
storage_->setName(name);
return *this;
}
private:
std::shared_ptr<TInstanceStorage> storage_;
};
/// Specialized Storage Configuration for Constant Factory
/// @tparam TInstance Instance type to be configured.
template<typename TInstance>
class StorageConfiguration<InstanceStorage<TInstance, ConstantFactory<TInstance>>> {
public:
explicit StorageConfiguration(std::shared_ptr<InstanceStorage<TInstance, ConstantFactory<TInstance>>> storage)
: storage_(storage)
{}
private:
std::shared_ptr<InstanceStorage<TInstance, ConstantFactory<TInstance>>> storage_;
};
/////////////////////////////////////////////////////////
// COMPONENT BUILDER
/////////////////////////////////////////////////////////
/// Builds binding between interfaces and implementations.
///
/// @par Sample usage
/// ```
/// Container c;
///
/// c.bind<IFirst>().to<Implementation>();
/// c.bind<IFirst>().toFunction<Cheetah>([](InjectionContext*) { return std::make_shared<Cheetah>(); });
/// c.bind<IFirst>().toContainer(cheetah);
/// c.bind<Cheetah>().toSelf();
/// ```
/// @note The toSelf is available only when the list of interfaces constains exactly one item
/// @tparam TComponents List of interfaces used for binding
template<typename... TComponents>
class ComponentBuilderBase {
public:
explicit ComponentBuilderBase(Container *container) : container_(container) {}
/// Binds all interfaces to provided implementation identified by the TImplementation type.
///
/// @tparam TImplementation Implementation type.
/// @return StoreConfiguration instance used to configure instance storage.
template<typename TImplementation>
StorageConfiguration<InstanceStorage<TImplementation, ConstructorFactory<TImplementation>>> To()
{
typedef InstanceStorage<TImplementation, ConstructorFactory<TImplementation>> InstanceStorageType;
// Create instance holder
auto instanceStorage = std::make_shared<InstanceStorageType>(ConstructorFactory<TImplementation>());
registerType<TImplementation, InstanceStorageType, TComponents...>(instanceStorage);
return StorageConfiguration<InstanceStorageType>(instanceStorage);
}
/// Binds all interfaces to provided function used to create a new instance.
///
/// @tparam TImplementation Implementation type. The provided function must return an instance that can be converted to TImplementation.
/// @param factoryMethod Function creating a new instance.
/// @return StoreConfiguration instance used to configure instance storage.
template<typename TImplementation>
StorageConfiguration<InstanceStorage<TImplementation, FunctionFactory<TImplementation>>>
ToFunction(typename FunctionFactory<TImplementation>::FactoryMethodType factoryMethod)
{
typedef InstanceStorage<TImplementation, FunctionFactory<TImplementation>> InstanceStorageType;
// Create instance holder
auto instanceStorage = std::make_shared<InstanceStorageType>(factoryMethod);
registerType<TImplementation, InstanceStorageType, TComponents...>(instanceStorage);
return StorageConfiguration<InstanceStorageType>(instanceStorage);
}
/// Binds all interfaces to already existing instance.
///
/// @tparam TImplementation Implementation type. The provided instance must be convertible to TImplementation.
/// @param instance Instance
/// @return StoreConfiguration instance used to configure instance storage.
template<typename TImplementation>
StorageConfiguration<InstanceStorage<TImplementation, ConstantFactory<TImplementation>>>
ToConstant(std::shared_ptr<TImplementation> instance)
{
typedef InstanceStorage<TImplementation, ConstantFactory<TImplementation>> InstanceStorageType;
// Create instance holder
auto instanceStorage = std::make_shared<InstanceStorageType>(instance);
registerType<TImplementation, InstanceStorageType, TComponents...>(instanceStorage);
return StorageConfiguration<InstanceStorageType>(instanceStorage);
}
private:
template<typename TImplementation,
typename TInstanceStorage,
typename TComponent1,
typename TComponentOther,
typename... TRest>
void registerType(std::shared_ptr<TInstanceStorage> instanceStorage)
{
// register
addRegistration<TImplementation, TInstanceStorage, TComponent1>(instanceStorage);
registerType<TImplementation, TInstanceStorage, TComponentOther, TRest...>(instanceStorage);
}
template<typename TImplementation, typename TInstanceStorage, typename TComponent>
void registerType(std::shared_ptr<TInstanceStorage> instanceStorage)
{
// register
addRegistration<TImplementation, TInstanceStorage, TComponent>(instanceStorage);
}
template<typename TImplementation, typename TInstanceStorage, typename TComponent>
void addRegistration(std::shared_ptr<TInstanceStorage> instanceStorage)
{
static_assert(std::is_convertible<TImplementation *, TComponent *>::value,
"No conversion exists from TImplementation* to TComponent*");
container_->registrations_[make_component_type<TComponent>()].emplace_back(std::shared_ptr<IInstanceRetriever>(
new CastInstanceRetriever<TImplementation, TComponent, TInstanceStorage>(instanceStorage)));
}
private:
Container *container_;
};
/// Basic builder used for two and more interfaces.
///
/// @see ComponentBuilderBase
template<typename... TComponents>
class ComponentBuilder : public ComponentBuilderBase<TComponents...> {
public:
explicit ComponentBuilder(Container *container) : ComponentBuilderBase<TComponents...>(container) {}
};
/// Specialization for single component registration that allows the toSelf.
///
/// This class is used only when the number of interfaces is exactly one.
/// @tparam TComponent Interface used for registration.
template<typename TComponent>
class ComponentBuilder<TComponent> : public ComponentBuilderBase<TComponent> {
public:
explicit ComponentBuilder(Container *container) : ComponentBuilderBase<TComponent>(container) {}
/// Registers interface to the same type.
///
/// @par Sample usage
/// ```
/// Container c;
///
/// c.bind<City>().toSelf()
/// ```
/// @return StoreConfiguration instance used to configure instance storage.
StorageConfiguration<InstanceStorage<TComponent, ConstructorFactory<TComponent>>> ToSelf()
{
return ComponentBuilderBase<TComponent>::template To<TComponent>();
}
};
/////////////////////////////////////////////////////////
// CONTAINER IMPLEMENTATION
/////////////////////////////////////////////////////////
template<typename... TArgs>
ComponentBuilder<TArgs...>
Container::Bind()
{
return ComponentBuilder<TArgs...>(this);
}
// container.get<std::vector<IFoo>>()
template<typename TVectorWithInterface>
typename std::enable_if<is_vector<TVectorWithInterface>::value
&& !is_shared_ptr<typename trim_vector<TVectorWithInterface>::type>::value
&& !std::is_reference<TVectorWithInterface>::value,
std::vector<std::shared_ptr<typename trim_vector<TVectorWithInterface>::type>>>::type
Container::Get(InjectionContext *context)
{
typedef typename trim_vector<TVectorWithInterface>::type InterfaceType;
std::unique_ptr<InjectionContext> contextPtr;
if (context == nullptr) {
contextPtr.reset(new InjectionContext(*this, make_component_type<InterfaceType>()));
context = contextPtr.get();
}
std::vector<std::shared_ptr<IInstanceRetriever>> retrievers;
findInstanceRetrievers(retrievers, make_component_type<InterfaceType>());
std::vector<std::shared_ptr<InterfaceType>> instances;
for (std::shared_ptr<IInstanceRetriever> retrieverInterface : retrievers) {
std::shared_ptr<InstanceRetriever<InterfaceType>> retriever
= std::dynamic_pointer_cast<InstanceRetriever<InterfaceType>>(retrieverInterface);
instances.emplace_back(retriever->forwardInstance(context));
}
return instances;
}
// container.get<std::vector<std::shared_ptr<IFoo>>>()
template<typename TVectorWithInterfaceWithSharedPtr>
typename std::enable_if<is_vector<TVectorWithInterfaceWithSharedPtr>::value
&& is_shared_ptr<typename trim_vector<TVectorWithInterfaceWithSharedPtr>::type>::value
&& !std::is_reference<TVectorWithInterfaceWithSharedPtr>::value,
std::vector<typename trim_vector<TVectorWithInterfaceWithSharedPtr>::type>>::type
Container::Get(InjectionContext *context)
{
return Get<
std::vector<typename trim_shared_ptr<typename trim_vector<TVectorWithInterfaceWithSharedPtr>::type>::type>>(
context);
}
// container.get<std::shared_ptr<IFoo>>()
template<typename TInterfaceWithSharedPtr>
typename std::enable_if<!is_vector<TInterfaceWithSharedPtr>::value && is_shared_ptr<TInterfaceWithSharedPtr>::value
&& !std::is_reference<TInterfaceWithSharedPtr>::value,
TInterfaceWithSharedPtr>::type
Container::Get(InjectionContext *context)
{
return Get<typename trim_shared_ptr<TInterfaceWithSharedPtr>::type>(context);
}
// container.get<IFoo>()
template<typename TInterface>
typename std::enable_if<!is_vector<TInterface>::value && !is_shared_ptr<TInterface>::value
&& !std::is_reference<TInterface>::value,
std::shared_ptr<TInterface>>::type
Container::Get(InjectionContext *context)
{
std::unique_ptr<InjectionContext> contextPtr;
if (context == nullptr) {
contextPtr.reset(
new InjectionContext(*this, make_component_type<cinject_unspecified_component>("Unspecified")));
context = contextPtr.get();
}
const component_type type = make_component_type<TInterface>();
std::vector<std::shared_ptr<IInstanceRetriever>> retrievers;
findInstanceRetrievers(retrievers, type);
if (retrievers.size() == 0) { throw ComponentNotFoundException(type); }
std::shared_ptr<InstanceRetriever<TInterface>> retriever
= std::dynamic_pointer_cast<InstanceRetriever<TInterface>>(retrievers[0]);
return retriever->forwardInstance(context);
}
// container.get<const IAny&>()
template<typename TInterface>
typename std::enable_if<std::is_reference<TInterface>::value
&& std::is_const<typename std::remove_reference<TInterface>::type>::value,
typename std::remove_reference<TInterface>::type>::type
Container::Get(InjectionContext *context)
{
return Get<typename std::remove_const<typename std::remove_reference<TInterface>::type>::type>(context);
}
inline void
Container::findInstanceRetrievers(std::vector<std::shared_ptr<IInstanceRetriever>> &instanceRetrievers,
const component_type &type) const
{
auto iter = registrations_.find(type);
if (iter != registrations_.end()) {
const std::vector<std::shared_ptr<IInstanceRetriever>> &currentRetrievers = iter->second;
instanceRetrievers.insert(instanceRetrievers.end(), currentRetrievers.begin(), currentRetrievers.end());
}
if (parentContainer_ != nullptr) { parentContainer_->findInstanceRetrievers(instanceRetrievers, type); }
}
}// namespace cinject
namespace sled {
namespace ioc {
using Container = cinject::Container;
using InjectContext = cinject::InjectionContext;
}// namespace ioc
}// namespace sled
#endif// SLED_SLED_IOC_IOC_H

64
src/sled/ioc/ioc_test.cc Normal file
View File

@ -0,0 +1,64 @@
#include <sled/ioc/ioc.h>
#include <string>
class IRunner {
public:
virtual ~IRunner() = default;
virtual int RunSpeed() = 0;
};
class IWalker {
public:
virtual ~IWalker() {}
virtual int WalkSpeed() = 0;
};
class Cheetah : public IRunner, public IWalker {
public:
int WalkSpeed() override { return 4; }
int RunSpeed() override { return 100; }
};
class Formatter {
public:
std::string Format(int value) { return std::to_string(value); }
};
class Service {
public:
Service(std::shared_ptr<Formatter> formatter) : formatter_(formatter) {}
std::string Format(int value) { return formatter_->Format(value); }
private:
std::shared_ptr<Formatter> formatter_;
};
TEST_SUITE("Inversion Of Control")
{
TEST_CASE("base")
{
sled::ioc::Container container;
container.Bind<IRunner, IWalker>().To<Cheetah>();
auto runner = container.Get<IRunner>();
auto walker = container.Get<IWalker>();
CHECK(runner);
CHECK(walker);
CHECK_EQ(runner.use_count(), 1);
CHECK_EQ(walker.use_count(), 1);
CHECK_EQ(runner->RunSpeed(), 100);
CHECK_EQ(walker->WalkSpeed(), 4);
}
TEST_CASE("deps")
{
sled::ioc::Container container;
container.Bind<Formatter>().ToSelf();
container.Bind<Service>().ToSelf();
auto service = container.Get<Service>();
CHECK_EQ(service->Format(42), "42");
CHECK_EQ(service->Format(44), "44");
}
}

View File

@ -24,6 +24,7 @@
// futures // futures
// #include "sled/futures/promise.h" // #include "sled/futures/promise.h"
#include "sled/ioc/ioc.h"
// lang // lang
#include "sled/lang/attributes.h" #include "sled/lang/attributes.h"