/* * 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_NORMALIZED_COMPONENT_H #define FRUIT_NORMALIZED_COMPONENT_H // This include is not required here, but having it here shortens the include trace in error messages. #include #include #include #include #include #include namespace fruit { /** * This class allows for fast creation of multiple injectors that share most (or all) the bindings. * * This is an advanced feature of Fruit that allows to reduce injection time in some cases; if you're just starting to * use Fruit you might want to ignore this for now (just construct an Injector from your root Component function). * * Using a NormalizedComponent only helps if: * * - You create multiple injectors during the lifetime of a process. E.g. if you only create one injector at startup you * won't benefit from using NormalizedComponent. * - Some of those injectors share all (or almost all) their bindings. * * When both of those requirements apply, you can switch to using NormalizedComponent in the "similar" injectors by * first refactoring the injectors' root components to be of the form: * * fruit::Component<...> getRootComponent(...) { * return fruit::createComponent() * // This contains the bindings common to the group of similar injectors. * .install(getSharedComponent, ...) * // This contains the bindings specific to this injector. * .install(getSpecificComponent, ...); * } * * Then you can change your injector construction from: * * fruit::Injector<...> injector(getRootComponent, ...); * * To: * * fruit::NormalizedComponent, ...> normalized_component(getSharedComponent, ...); * fruit::Injector<...> injector(normalized_component, getSpecificComponent, ...); * * This splits the work of constructing the Injector in two phases: normalization (where Fruit will call the Component * functions to collect all the bindings and check for some classes of runtime errors) and the actual creation of the * injector, during which Fruit will also collect/check the additional bindings specific to that injector. * * Then you can share the same normalized_component object across all those injectors (also in different threads, * NormalizedComponent is thread-safe), so that the normalization step only occurs once (i.e., you should only construct * NormalizedComponent from getSharedComponent once, otherwise you'd pay the normalization cost multiple times). * * Creating an Injector from a NormalizedComponent and injecting separate instances is very cheap, on the order of 2 us * for an injection graph with 100 classes and 900 edges (for more details see the Benchmarks page of the Fruit wiki: * https://github.com/google/fruit/wiki/benchmarks ). * This might (depending of course on your performance requirements) allow you to create injectors where it would * otherwise be unthinkable, e.g. creating a separate injector for each request in a server. * * Injectors that share the same NormalizedComponent are still independent; for example, if you call injector.get() * in two injectors, each injector will construct its own instance of Foo. * * Example usage in a server: * * // In the global scope. * Component getRequestComponent(Request* request) { * return fruit::createComponent() * .bindInstance(*request); * } * * // At startup (e.g. inside main()). * NormalizedComponent, Bar, Bar2> normalizedComponent = ...; * * ... * for (...) { * // For each request. * Request request = ...; * * Injector injector(normalizedComponent, getRequestComponent, &request); * Foo* foo = injector.get(); * ... * } * * See also the documentation for the Injector constructor that takes a NormalizedComponent. */ template class NormalizedComponent { public: /** * The Component used as parameter can have (and usually has) unsatisfied requirements, so it's usually of the form * Component, ...>. * * The given component function is called with the provided arguments to construct the root component. * The constraints on the argument types (if there are any) are the same as the ones for PartialComponent::install(). */ template explicit NormalizedComponent(Component (*)(FormalArgs...), Args&&... args); NormalizedComponent(NormalizedComponent&& storage) noexcept : storage(std::move(storage.storage)) {} NormalizedComponent(const NormalizedComponent&) = delete; NormalizedComponent& operator=(NormalizedComponent&&) = delete; NormalizedComponent& operator=(const NormalizedComponent&) = delete; private: NormalizedComponent(fruit::impl::ComponentStorage&& storage, fruit::impl::MemoryPool memory_pool); // This is held via a unique_ptr to avoid including normalized_component_storage.h // in fruit.h. fruit::impl::NormalizedComponentStorageHolder storage; template friend class Injector; using Comp = fruit::impl::meta::Eval...)>; using Check1 = typename fruit::impl::meta::CheckIfError::type; // Force instantiation of Check1. static_assert(true || sizeof(Check1), ""); }; } // namespace fruit #include #endif // FRUIT_NORMALIZED_COMPONENT_H