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.
157 lines
4.8 KiB
157 lines
4.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_PROVIDER_H
|
|
#define FRUIT_PROVIDER_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>
|
|
|
|
namespace fruit {
|
|
|
|
/**
|
|
* A Provider is a class that allows access to instances of the types used as parameters of the Provider template.
|
|
* It's possible to inject a Provider<MyClass> instead of MyClass itself, and this allows lazy injection.
|
|
* For example:
|
|
*
|
|
* class S {
|
|
* private:
|
|
* Bar* bar = nullptr;
|
|
*
|
|
* public:
|
|
* INJECT(S(Foo* foo, Provider<Bar> barProvider)) {
|
|
* if (foo->needsBar()) {
|
|
* bar = barProvider.get();
|
|
* }
|
|
* }
|
|
* };
|
|
*
|
|
* In the example above, Bar will only be created if get<Bar*> is called.
|
|
* This can be useful if Bar is expensive to create (or some other types that need to be injected when a Bar is injected
|
|
* are) or if there are other side effects of the Bar constructor that are undesirable when !foo->needsBar().
|
|
* It's also possible to store the Provider object in a field, and create the Bar instance when the first method that
|
|
* needs it is called:
|
|
*
|
|
* class S {
|
|
* private:
|
|
* Provider<Bar> barProvider;
|
|
*
|
|
* public:
|
|
* INJECT(S(Provider<Bar> barProvider))
|
|
* : barProvider(barProvider) {
|
|
* }
|
|
*
|
|
* void execute() {
|
|
* if (...) {
|
|
* Bar* bar = barProvider.get();
|
|
* ...
|
|
* }
|
|
* }
|
|
* };
|
|
*
|
|
* As usual, Fruit ensures that (at most) one instance is ever created in a given injector; so if the Bar object was
|
|
* already constructed, the get() will simply return it.
|
|
*
|
|
* Note that you can inject a Provider<Foo> whenever you could have injected a Foo.
|
|
* It doesn't matter if Foo was bound using PartialComponent::registerProvider() or not.
|
|
*/
|
|
template <typename C>
|
|
class Provider {
|
|
private:
|
|
using Check1 =
|
|
typename fruit::impl::meta::CheckIfError<fruit::impl::meta::Eval<fruit::impl::meta::CheckNormalizedTypes(
|
|
fruit::impl::meta::RemoveConstFromTypes(fruit::impl::meta::Vector<fruit::impl::meta::Type<C>>))>>::type;
|
|
// Force instantiation of Check1.
|
|
static_assert(true || sizeof(Check1), "");
|
|
|
|
using Check2 =
|
|
typename fruit::impl::meta::CheckIfError<fruit::impl::meta::Eval<fruit::impl::meta::CheckNotAnnotatedTypes(
|
|
fruit::impl::meta::Vector<fruit::impl::meta::Type<C>>)>>::type;
|
|
// Force instantiation of Check2.
|
|
static_assert(true || sizeof(Check2), "");
|
|
|
|
public:
|
|
/**
|
|
* Returns an instance of the specified type. The following variations are allowed:
|
|
*
|
|
* On a Provider<Foo>, you can call:
|
|
*
|
|
* - provider.get<Foo>()
|
|
* - provider.get<Foo*>()
|
|
* - provider.get<Foo&>()
|
|
* - provider.get<const Foo*>()
|
|
* - provider.get<const Foo&>()
|
|
* - provider.get<std::shared_ptr<Foo>>()
|
|
* - provider.get<Provider<Foo>>()
|
|
* - provider.get<Provider<const Foo>>()
|
|
*
|
|
* On a Provider<const Foo>, you can call:
|
|
*
|
|
* - provider.get<Foo>()
|
|
* - provider.get<const Foo*>()
|
|
* - provider.get<const Foo&>()
|
|
* - provider.get<Provider<const Foo>>()
|
|
*
|
|
* The shared_ptr version is slightly slower than the ones returning a reference/pointer, use those if possible.
|
|
*
|
|
* Calling get<> repeatedly for the same class with the same injector will return the same instance (except for the
|
|
* first variation above, that returns a value; in that case, another copy of the same instance will be returned).
|
|
*/
|
|
template <typename T>
|
|
T get();
|
|
|
|
/**
|
|
* This is a convenient way to call get(). E.g.:
|
|
*
|
|
* C& x(provider);
|
|
*
|
|
* is equivalent to:
|
|
*
|
|
* C& x = provider.get<C&>();
|
|
*/
|
|
template <typename T>
|
|
explicit operator T();
|
|
|
|
/**
|
|
* This is equivalent to get<C*>(), it's provided for convenience.
|
|
*/
|
|
C* get();
|
|
|
|
private:
|
|
// This is NOT owned by the provider object. It is not deleted on destruction.
|
|
// This is never nullptr.
|
|
fruit::impl::InjectorStorage* storage;
|
|
fruit::impl::InjectorStorage::Graph::node_iterator itr;
|
|
|
|
Provider(fruit::impl::InjectorStorage* storage, fruit::impl::InjectorStorage::Graph::node_iterator itr);
|
|
|
|
friend class fruit::impl::InjectorStorage;
|
|
|
|
template <typename T>
|
|
friend struct fruit::impl::GetFirstStage;
|
|
|
|
template <typename... OtherPs>
|
|
friend class Injector;
|
|
};
|
|
|
|
} // namespace fruit
|
|
|
|
#include <fruit/impl/provider.defn.h>
|
|
|
|
#endif // FRUIT_PROVIDER_H
|