1 /* 2 * Copyright 2014 Google Inc. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef FRUIT_NORMALIZED_COMPONENT_H 18 #define FRUIT_NORMALIZED_COMPONENT_H 19 20 // This include is not required here, but having it here shortens the include trace in error messages. 21 #include <fruit/impl/injection_errors.h> 22 23 #include <fruit/fruit_forward_decls.h> 24 #include <fruit/impl/fruit_internal_forward_decls.h> 25 #include <fruit/impl/meta/component.h> 26 #include <fruit/impl/normalized_component_storage/normalized_component_storage_holder.h> 27 #include <memory> 28 29 namespace fruit { 30 31 /** 32 * This class allows for fast creation of multiple injectors that share most (or all) the bindings. 33 * 34 * This is an advanced feature of Fruit that allows to reduce injection time in some cases; if you're just starting to 35 * use Fruit you might want to ignore this for now (just construct an Injector from your root Component function). 36 * 37 * Using a NormalizedComponent only helps if: 38 * 39 * - You create multiple injectors during the lifetime of a process. E.g. if you only create one injector at startup you 40 * won't benefit from using NormalizedComponent. 41 * - Some of those injectors share all (or almost all) their bindings. 42 * 43 * When both of those requirements apply, you can switch to using NormalizedComponent in the "similar" injectors by 44 * first refactoring the injectors' root components to be of the form: 45 * 46 * fruit::Component<...> getRootComponent(...) { 47 * return fruit::createComponent() 48 * // This contains the bindings common to the group of similar injectors. 49 * .install(getSharedComponent, ...) 50 * // This contains the bindings specific to this injector. 51 * .install(getSpecificComponent, ...); 52 * } 53 * 54 * Then you can change your injector construction from: 55 * 56 * fruit::Injector<...> injector(getRootComponent, ...); 57 * 58 * To: 59 * 60 * fruit::NormalizedComponent<fruit::Required<...>, ...> normalized_component(getSharedComponent, ...); 61 * fruit::Injector<...> injector(normalized_component, getSpecificComponent, ...); 62 * 63 * This splits the work of constructing the Injector in two phases: normalization (where Fruit will call the Component 64 * functions to collect all the bindings and check for some classes of runtime errors) and the actual creation of the 65 * injector, during which Fruit will also collect/check the additional bindings specific to that injector. 66 * 67 * Then you can share the same normalized_component object across all those injectors (also in different threads, 68 * NormalizedComponent is thread-safe), so that the normalization step only occurs once (i.e., you should only construct 69 * NormalizedComponent from getSharedComponent once, otherwise you'd pay the normalization cost multiple times). 70 * 71 * Creating an Injector from a NormalizedComponent and injecting separate instances is very cheap, on the order of 2 us 72 * for an injection graph with 100 classes and 900 edges (for more details see the Benchmarks page of the Fruit wiki: 73 * https://github.com/google/fruit/wiki/benchmarks ). 74 * This might (depending of course on your performance requirements) allow you to create injectors where it would 75 * otherwise be unthinkable, e.g. creating a separate injector for each request in a server. 76 * 77 * Injectors that share the same NormalizedComponent are still independent; for example, if you call injector.get<Foo>() 78 * in two injectors, each injector will construct its own instance of Foo. 79 * 80 * Example usage in a server: 81 * 82 * // In the global scope. 83 * Component<Request> getRequestComponent(Request* request) { 84 * return fruit::createComponent() 85 * .bindInstance(*request); 86 * } 87 * 88 * // At startup (e.g. inside main()). 89 * NormalizedComponent<Required<Request>, Bar, Bar2> normalizedComponent = ...; 90 * 91 * ... 92 * for (...) { 93 * // For each request. 94 * Request request = ...; 95 * 96 * Injector<Foo, Bar> injector(normalizedComponent, getRequestComponent, &request); 97 * Foo* foo = injector.get<Foo*>(); 98 * ... 99 * } 100 * 101 * See also the documentation for the Injector constructor that takes a NormalizedComponent. 102 */ 103 template <typename... Params> 104 class NormalizedComponent { 105 public: 106 /** 107 * The Component used as parameter can have (and usually has) unsatisfied requirements, so it's usually of the form 108 * Component<Required<...>, ...>. 109 * 110 * The given component function is called with the provided arguments to construct the root component. 111 * The constraints on the argument types (if there are any) are the same as the ones for PartialComponent::install(). 112 */ 113 template <typename... FormalArgs, typename... Args> 114 NormalizedComponent(Component<Params...> (*)(FormalArgs...), Args&&... args); 115 116 NormalizedComponent(NormalizedComponent&&) = default; 117 NormalizedComponent(const NormalizedComponent&) = delete; 118 119 NormalizedComponent& operator=(NormalizedComponent&&) = delete; 120 NormalizedComponent& operator=(const NormalizedComponent&) = delete; 121 122 private: 123 NormalizedComponent(fruit::impl::ComponentStorage&& storage, fruit::impl::MemoryPool memory_pool); 124 125 // This is held via a unique_ptr to avoid including normalized_component_storage.h 126 // in fruit.h. 127 fruit::impl::NormalizedComponentStorageHolder storage; 128 129 template <typename... OtherParams> 130 friend class Injector; 131 132 using Comp = fruit::impl::meta::Eval<fruit::impl::meta::ConstructComponentImpl(fruit::impl::meta::Type<Params>...)>; 133 134 using Check1 = typename fruit::impl::meta::CheckIfError<Comp>::type; 135 // Force instantiation of Check1. 136 static_assert(true || sizeof(Check1), ""); 137 }; 138 139 } // namespace fruit 140 141 #include <fruit/impl/normalized_component.defn.h> 142 143 #endif // FRUIT_NORMALIZED_COMPONENT_H 144