You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
252 lines
9.8 KiB
252 lines
9.8 KiB
/*
|
|
* Copyright 2014 Google Inc. All rights reserved.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#ifndef FRUIT_INJECTOR_H
|
|
#define FRUIT_INJECTOR_H
|
|
|
|
// This include is not required here, but having it here shortens the include trace in error messages.
|
|
#include <fruit/impl/injection_errors.h>
|
|
|
|
#include <fruit/component.h>
|
|
#include <fruit/normalized_component.h>
|
|
#include <fruit/provider.h>
|
|
#include <fruit/impl/meta_operation_wrappers.h>
|
|
|
|
namespace fruit {
|
|
|
|
/**
|
|
* An injector is a class constructed from a component that performs the needed injections and manages the lifetime of
|
|
* the created objects.
|
|
* An injector does *not* need to specify all types bound in the component; you can only specify the "root" type(s) and
|
|
* the injector will also create and store the instances of classes that are needed (directly or indirectly) to inject
|
|
* the root types.
|
|
*
|
|
* Example usage:
|
|
*
|
|
* Component<Foo, Bar> getFooBarComponent() {
|
|
* ...
|
|
* }
|
|
*
|
|
* Injector<Foo, Bar> injector(getFooBarComponent);
|
|
* Foo* foo = injector.get<Foo*>();
|
|
* Bar* bar(injector); // Equivalent to: Bar* bar = injector.get<Bar*>();
|
|
*/
|
|
template <typename... P>
|
|
class Injector {
|
|
public:
|
|
// Moving injectors is allowed.
|
|
Injector(Injector&&) noexcept = default;
|
|
|
|
// Copying injectors is forbidden.
|
|
Injector(const Injector&) = delete;
|
|
|
|
/**
|
|
* This creates an injector from a component function (that can optionally have parameters).
|
|
*
|
|
* Args and FormalArgs (if any) must be the same types; or to be precise, each type in Args must be convertible into
|
|
* the corresponding type in FormalArgs.
|
|
*
|
|
* Example usage:
|
|
*
|
|
* Component<Foo, Bar> getFooBarComponent() {
|
|
* ...
|
|
* }
|
|
*
|
|
* Injector<Foo, Bar> injector(getFooBarComponent);
|
|
* Foo* foo = injector.get<Foo*>();
|
|
* Bar* bar(injector); // Equivalent to: Bar* bar = injector.get<Bar*>();
|
|
*
|
|
* Example usage with arguments:
|
|
*
|
|
* Component<Foo, Bar> getFooBarComponent(int n, double d) {
|
|
* ...
|
|
* }
|
|
*
|
|
* Injector<Foo, Bar> injector(getFooBarComponent, 10, 3.14);
|
|
* Foo* foo = injector.get<Foo*>();
|
|
*/
|
|
template <typename... FormalArgs, typename... Args>
|
|
explicit Injector(Component<P...> (*)(FormalArgs...), Args&&... args);
|
|
|
|
/**
|
|
* This creates an injector from a normalized component and a component function.
|
|
* See the documentation of NormalizedComponent for more details.
|
|
*
|
|
* Args and FormalArgs (if any) must be the same types; or to be precise, each type in Args must be convertible into
|
|
* the corresponding type in FormalArgs.
|
|
*
|
|
* The NormalizedComponent can have requirements, but the Component can't.
|
|
* The NormalizedComponent must remain valid during the lifetime of any Injector object constructed with it.
|
|
*
|
|
* Example usage:
|
|
*
|
|
* // In the global scope.
|
|
* Component<Request> getRequestComponent(Request* request) {
|
|
* return fruit::createComponent()
|
|
* .bindInstance(*request);
|
|
* }
|
|
*
|
|
* // At startup (e.g. inside main()).
|
|
* NormalizedComponent<Required<Request>, Bar, Bar2> normalizedComponent = ...;
|
|
*
|
|
* ...
|
|
* for (...) {
|
|
* // For each request.
|
|
* Request request = ...;
|
|
*
|
|
* Injector<Foo, Bar> injector(normalizedComponent, getRequestComponent, &request);
|
|
* Foo* foo = injector.get<Foo*>();
|
|
* ...
|
|
* }
|
|
*/
|
|
template <typename... NormalizedComponentParams, typename... ComponentParams, typename... FormalArgs,
|
|
typename... Args>
|
|
Injector(const NormalizedComponent<NormalizedComponentParams...>& normalized_component,
|
|
Component<ComponentParams...> (*)(FormalArgs...), Args&&... args);
|
|
|
|
/**
|
|
* Deleted constructor, to ensure that constructing an Injector from a temporary NormalizedComponent doesn't compile.
|
|
*/
|
|
template <typename... NormalizedComponentParams, typename... ComponentParams, typename... FormalArgs,
|
|
typename... Args>
|
|
Injector(NormalizedComponent<NormalizedComponentParams...>&& normalized_component,
|
|
Component<ComponentParams...> (*)(FormalArgs...), Args&&... args) = delete;
|
|
|
|
/**
|
|
* Returns an instance of the specified type. For any class C in the Injector's template parameters, the following
|
|
* variations are allowed:
|
|
*
|
|
* get<C>()
|
|
* get<C*>()
|
|
* get<C&>()
|
|
* get<const C*>()
|
|
* get<const C&>()
|
|
* get<shared_ptr<C>>()
|
|
* get<Provider<C>>()
|
|
* get<Provider<const C>>()
|
|
* get<Annotated<Annotation, C>>() (for any type `Annotation')
|
|
* get<Annotated<Annotation, C*>>() (for any type `Annotation')
|
|
* get<Annotated<Annotation, C&>>() (for any type `Annotation')
|
|
* get<Annotated<Annotation, const C*>>() (for any type `Annotation')
|
|
* get<Annotated<Annotation, const C&>>() (for any type `Annotation')
|
|
* get<Annotated<Annotation, shared_ptr<C>>>() (for any type `Annotation')
|
|
* get<Annotated<Annotation, Provider<C>>>() (for any type `Annotation')
|
|
* get<Annotated<Annotation, Provider<const C>>>() (for any type `Annotation')
|
|
*
|
|
* For any "const C" in the Injector's template parameters, only a subset of those are allowed, specifically:
|
|
*
|
|
* get<C>()
|
|
* get<const C*>()
|
|
* get<const C&>()
|
|
* get<Provider<const C>>()
|
|
* get<Annotated<Annotation, C>>() (for any type `Annotation')
|
|
* get<Annotated<Annotation, const C*>>() (for any type `Annotation')
|
|
* get<Annotated<Annotation, const C&>>() (for any type `Annotation')
|
|
* get<Annotated<Annotation, Provider<const C>>>() (for any type `Annotation')
|
|
*
|
|
* With a non-annotated parameter T, this returns a T.
|
|
* With an annotated parameter AnnotatedT=Annotated<Annotation, T>, this returns a T.
|
|
* E.g. if you want to inject a pointer for an annotated type, you can use this as follows:
|
|
*
|
|
* T* instance = injector.get<Annotated<Annotation, T*>>();
|
|
*
|
|
* The shared_ptr versions come with a slight performance hit, prefer injecting pointers or references if possible.
|
|
* Calling get<> repeatedly for the same class with the same injector will return the same instance.
|
|
*/
|
|
template <typename T>
|
|
fruit::impl::RemoveAnnotations<T> get();
|
|
|
|
/**
|
|
* This is a convenient way to call get(). E.g.:
|
|
*
|
|
* MyInterface* x(injector);
|
|
*
|
|
* is equivalent to:
|
|
*
|
|
* MyInterface* x = injector.get<MyInterface*>();
|
|
*
|
|
* Note that this can't be used to inject an annotated type, i.e. this does NOT work:
|
|
*
|
|
* fruit::Annotated<SomeAnnotation, SomeClass> foo(injector);
|
|
*
|
|
* Because foo would be of type fruit::Annotated, not of type SomeClass. In that case you must use get() instead,
|
|
* e.g.:
|
|
*
|
|
* SomeClass* foo = injector.get<fruit::Annotated<SomeAnnotation, SomeClass*>>();;
|
|
*/
|
|
template <typename T>
|
|
explicit operator T();
|
|
|
|
/**
|
|
* Gets all multibindings for a type T.
|
|
*
|
|
* Multibindings are independent from bindings; so if there is a (normal) binding for T, that is not returned.
|
|
* This returns an empty vector if there are no multibindings.
|
|
*
|
|
* With a non-annotated parameter T, this returns a const std::vector<T*>&.
|
|
* With an annotated parameter AnnotatedT=Annotated<Annotation, T>, this returns a const std::vector<T*>&.
|
|
*/
|
|
template <typename T>
|
|
const std::vector<fruit::impl::RemoveAnnotations<T>*>& getMultibindings();
|
|
|
|
/**
|
|
* This method is deprecated since Fruit injectors can now be accessed concurrently by multiple threads. This will be
|
|
* removed in a future Fruit release.
|
|
*
|
|
* Eagerly injects all reachable bindings and multibindings of this injector.
|
|
* This only creates instances of the types that are either:
|
|
* - exposed by this Injector (i.e. in the Injector's type parameters)
|
|
* - bound by a multibinding
|
|
* - needed to inject one of the above (directly or indirectly)
|
|
*
|
|
* Unreachable bindings (i.e. bindings that are not exposed by this Injector, and that are not used by any reachable
|
|
* binding) are not processed. Bindings that are only used lazily, using a Provider, are NOT eagerly injected.
|
|
*
|
|
* Also note that this guarantee doesn't apply to Providers.
|
|
*/
|
|
FRUIT_DEPRECATED_DECLARATION(void eagerlyInjectAll());
|
|
|
|
private:
|
|
using Check1 = typename fruit::impl::meta::CheckIfError<fruit::impl::meta::Eval<
|
|
fruit::impl::meta::CheckNoRequiredTypesInInjectorArguments(fruit::impl::meta::Type<P>...)>>::type;
|
|
// Force instantiation of Check1.
|
|
static_assert(true || sizeof(Check1), "");
|
|
|
|
using Comp = fruit::impl::meta::Eval<fruit::impl::meta::ConstructComponentImpl(fruit::impl::meta::Type<P>...)>;
|
|
|
|
using Check2 = typename fruit::impl::meta::CheckIfError<Comp>::type;
|
|
using VoidType = fruit::impl::meta::Type<void>;
|
|
// Force instantiation of Check2.
|
|
static_assert(true || sizeof(Check2), "");
|
|
using Check3 = typename fruit::impl::meta::CheckIfError<fruit::impl::meta::Eval<fruit::impl::meta::If(
|
|
fruit::impl::meta::Not(fruit::impl::meta::IsEmptySet(typename Comp::RsSuperset)),
|
|
fruit::impl::meta::ConstructErrorWithArgVector(fruit::impl::InjectorWithRequirementsErrorTag,
|
|
fruit::impl::meta::SetToVector(typename Comp::RsSuperset)),
|
|
VoidType)>>::type;
|
|
// Force instantiation of Check3.
|
|
static_assert(true || sizeof(Check3), "");
|
|
|
|
friend struct fruit::impl::InjectorAccessorForTests;
|
|
|
|
std::unique_ptr<fruit::impl::InjectorStorage> storage;
|
|
};
|
|
|
|
} // namespace fruit
|
|
|
|
#include <fruit/impl/injector.defn.h>
|
|
|
|
#endif // FRUIT_INJECTOR_H
|