• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_INJECTOR_STORAGE_H
18 #define FRUIT_INJECTOR_STORAGE_H
19 
20 #include <fruit/fruit_forward_decls.h>
21 #include <fruit/impl/data_structures/fixed_size_allocator.h>
22 #include <fruit/impl/meta/component.h>
23 #include <fruit/impl/normalized_component_storage/normalized_bindings.h>
24 
25 #include <unordered_map>
26 #include <vector>
27 #include <mutex>
28 #include <thread>
29 
30 namespace fruit {
31 namespace impl {
32 
33 template <typename T>
34 struct GetHelper;
35 
36 /**
37  * A component where all types have to be explicitly registered, and all checks are at runtime.
38  * Used to implement Component<>, don't use directly.
39  *
40  * This class handles the creation of types of the forms:
41  * - shared_ptr<C>, [const] C*, [const] C&, C (where C is an atomic type)
42  * - Injector<T1, ..., Tk> (with T1, ..., Tk of the above forms).
43  */
44 class InjectorStorage {
45 public:
46   // TODO: remove.
47   //  using BindingVectors = std::pair<std::vector<std::pair<TypeId, BindingData>>,
48   //                                   std::vector<std::pair<TypeId, MultibindingData>>>;
49   using Graph = SemistaticGraph<TypeId, NormalizedBinding>;
50 
51   template <typename AnnotatedT>
52   using RemoveAnnotations = fruit::impl::meta::UnwrapType<
53       fruit::impl::meta::Eval<fruit::impl::meta::RemoveAnnotations(fruit::impl::meta::Type<AnnotatedT>)>>;
54 
55   // MSVC 14 has trouble specializing alias templates using expanded pack elements.
56   // This is a known issue:
57   // https://stackoverflow.com/questions/43411542/metaprogramming-failed-to-specialize-alias-template
58   // The workaround is just to use a struct directly.
59   template <typename AnnotatedT>
60   struct AnnotationRemover {
61     using type = RemoveAnnotations<AnnotatedT>;
62   };
63 
64   template <typename T>
65   using NormalizeType = fruit::impl::meta::UnwrapType<
66       fruit::impl::meta::Eval<fruit::impl::meta::NormalizeType(fruit::impl::meta::Type<T>)>>;
67 
68   template <typename T>
69   struct TypeNormalizer {
70     using type = NormalizeType<T>;
71   };
72 
73   template <typename Signature>
74   using SignatureType = fruit::impl::meta::UnwrapType<
75       fruit::impl::meta::Eval<fruit::impl::meta::SignatureType(fruit::impl::meta::Type<Signature>)>>;
76 
77   template <typename Signature>
78   using NormalizedSignatureArgs = fruit::impl::meta::Eval<fruit::impl::meta::NormalizeTypeVector(
79       fruit::impl::meta::SignatureArgs(fruit::impl::meta::Type<Signature>))>;
80 
81   // Prints the specified error and calls exit(1).
82   static void fatal(const std::string& error);
83 
84   template <typename AnnotatedI, typename AnnotatedC>
85   static ComponentStorageEntry createComponentStorageEntryForBind();
86 
87   template <typename AnnotatedI, typename AnnotatedC>
88   static ComponentStorageEntry createComponentStorageEntryForConstBind();
89 
90   template <typename AnnotatedC, typename C>
91   static ComponentStorageEntry createComponentStorageEntryForBindInstance(C& instance);
92 
93   template <typename AnnotatedC, typename C>
94   static ComponentStorageEntry createComponentStorageEntryForBindConstInstance(const C& instance);
95 
96   template <typename AnnotatedSignature, typename Lambda>
97   static ComponentStorageEntry createComponentStorageEntryForProvider();
98 
99   template <typename AnnotatedSignature, typename Lambda, typename AnnotatedI>
100   static ComponentStorageEntry createComponentStorageEntryForCompressedProvider();
101 
102   template <typename AnnotatedSignature>
103   static ComponentStorageEntry createComponentStorageEntryForConstructor();
104 
105   template <typename AnnotatedSignature, typename AnnotatedI>
106   static ComponentStorageEntry createComponentStorageEntryForCompressedConstructor();
107 
108   template <typename AnnotatedT>
109   static ComponentStorageEntry createComponentStorageEntryForMultibindingVectorCreator();
110 
111   template <typename AnnotatedI, typename AnnotatedC>
112   static ComponentStorageEntry createComponentStorageEntryForMultibinding();
113 
114   template <typename AnnotatedC, typename C>
115   static ComponentStorageEntry createComponentStorageEntryForInstanceMultibinding(C& instance);
116 
117   template <typename AnnotatedSignature, typename Lambda>
118   static ComponentStorageEntry createComponentStorageEntryForMultibindingProvider();
119 
120 private:
121   // The NormalizedComponentStorage owned by this object (if any).
122   // Only used for the 1-argument constructor, otherwise it's nullptr.
123   std::unique_ptr<NormalizedComponentStorage> normalized_component_storage_ptr;
124 
125   FixedSizeAllocator allocator;
126 
127   // A graph with injected types as nodes (each node stores the NormalizedBindingData for the type) and dependencies as
128   // edges.
129   // For types that have a constructed object already, the corresponding node is stored as terminal node.
130   SemistaticGraph<TypeId, NormalizedBinding> bindings;
131 
132   // Maps the type index of a type T to the corresponding NormalizedMultibindingSet object (that stores all
133   // multibindings).
134   std::unordered_map<TypeId, NormalizedMultibindingSet> multibindings;
135 
136   // This mutex is used to synchronize concurrent accesses to this InjectorStorage object.
137   std::recursive_mutex mutex;
138 
139 private:
140   template <typename AnnotatedC>
141   static std::shared_ptr<char> createMultibindingVector(InjectorStorage& storage);
142 
143   // If not bound, returns nullptr.
144   NormalizedMultibindingSet* getNormalizedMultibindingSet(TypeId type);
145 
146   // Looks up the location where the type is (or will be) stored, but does not construct the class.
147   template <typename AnnotatedC>
148   Graph::node_iterator lazyGetPtr();
149 
150   // getPtr() is equivalent to getPtrInternal(lazyGetPtr())
151   template <typename C>
152   const C* getPtr(Graph::node_iterator itr);
153 
154   // Similar to the previous, but takes a node_iterator. Use this when the node_iterator is known, it's faster.
155   const void* getPtrInternal(Graph::node_iterator itr);
156 
157   // getPtr(typeInfo) is equivalent to getPtr(lazyGetPtr(typeInfo)).
158   Graph::node_iterator lazyGetPtr(TypeId type);
159 
160   // getPtr(deps, index) is equivalent to getPtr(lazyGetPtr(deps, index)).
161   Graph::node_iterator lazyGetPtr(Graph::edge_iterator deps, std::size_t dep_index);
162 
163   // Similar to getPtr, but the binding might not exist. Returns nullptr if it doesn't.
164   const void* unsafeGetPtr(TypeId type);
165 
166   void* getPtrForMultibinding(TypeId type);
167 
168   // Returns a std::vector<T*>*, or nullptr if there are no multibindings.
169   void* getMultibindings(TypeId type);
170 
171   // Constructs any necessary instances, but NOT the instance set.
172   void ensureConstructedMultibinding(NormalizedMultibindingSet& multibinding_set);
173 
174   template <typename T>
175   friend struct GetFirstStage;
176 
177   template <typename T>
178   friend class fruit::Provider;
179 
180   using object_ptr_t = void*;
181   using const_object_ptr_t = const void*;
182 
183   template <typename I, typename C, typename AnnotatedC>
184   static const_object_ptr_t createInjectedObjectForBind(InjectorStorage& injector,
185                                                         InjectorStorage::Graph::node_iterator node_itr);
186 
187   template <typename C, typename T, typename AnnotatedSignature, typename Lambda>
188   static const_object_ptr_t createInjectedObjectForProvider(InjectorStorage& injector, Graph::node_iterator node_itr);
189 
190   template <typename I, typename C, typename T, typename AnnotatedSignature, typename Lambda>
191   static const_object_ptr_t createInjectedObjectForCompressedProvider(InjectorStorage& injector,
192                                                                       Graph::node_iterator node_itr);
193 
194   template <typename C, typename AnnotatedSignature>
195   static const_object_ptr_t createInjectedObjectForConstructor(InjectorStorage& injector,
196                                                                Graph::node_iterator node_itr);
197 
198   template <typename I, typename C, typename AnnotatedSignature>
199   static const_object_ptr_t createInjectedObjectForCompressedConstructor(InjectorStorage& injector,
200                                                                          Graph::node_iterator node_itr);
201 
202   template <typename I, typename C, typename AnnotatedCPtr>
203   static object_ptr_t createInjectedObjectForMultibinding(InjectorStorage& m);
204 
205   template <typename C, typename T, typename AnnotatedSignature, typename Lambda>
206   static object_ptr_t createInjectedObjectForMultibindingProvider(InjectorStorage& injector);
207 
208 public:
209   // Wraps a std::vector<ComponentStorageEntry>::iterator as an iterator on tuples
210   // (typeId, normalizedBindingData, isTerminal, edgesBegin, edgesEnd)
211   struct BindingDataNodeIter {
212     std::vector<ComponentStorageEntry, ArenaAllocator<ComponentStorageEntry>>::iterator itr;
213 
214     BindingDataNodeIter* operator->();
215 
216     void operator++();
217 
218     bool operator==(const BindingDataNodeIter& other) const;
219     bool operator!=(const BindingDataNodeIter& other) const;
220 
221     std::ptrdiff_t operator-(BindingDataNodeIter other) const;
222 
223     TypeId getId();
224     NormalizedBinding getValue();
225     bool isTerminal();
226     const TypeId* getEdgesBegin();
227     const TypeId* getEdgesEnd();
228   };
229 
230   /**
231    * The MemoryPool is only used during construction, the constructed object *can* outlive the memory pool.
232    */
233   InjectorStorage(ComponentStorage&& storage, const std::vector<TypeId, ArenaAllocator<TypeId>>& exposed_types,
234                   MemoryPool& memory_pool);
235 
236   /**
237    * The MemoryPool is only used during construction, the constructed object *can* outlive the memory pool.
238    */
239   InjectorStorage(const NormalizedComponentStorage& normalized_storage, ComponentStorage&& storage,
240                   MemoryPool& memory_pool);
241 
242   // This is just the default destructor, but we declare it here to avoid including
243   // normalized_component_storage.h in fruit.h.
244   ~InjectorStorage();
245 
246   InjectorStorage(InjectorStorage&&) = delete;
247   InjectorStorage& operator=(InjectorStorage&&) = delete;
248 
249   InjectorStorage(const InjectorStorage& other) = delete;
250   InjectorStorage& operator=(const InjectorStorage& other) = delete;
251 
252   // Usually get<T>() returns a T.
253   // However, get<Annotated<Annotation1, T>>() returns a T, not an Annotated<Annotation1, T>.
254   template <typename AnnotatedT>
255   RemoveAnnotations<AnnotatedT> get();
256 
257   // Similar to the above, but specifying the node_iterator of the type. Use this together with lazyGetPtr when the
258   // node_iterator is known, it's faster.
259   // Note that T should *not* be annotated.
260   template <typename T>
261   T get(InjectorStorage::Graph::node_iterator node_iterator);
262 
263   // Looks up the location where the type is (or will be) stored, but does not construct the class.
264   // get<AnnotatedT>() is equivalent to get<AnnotatedT>(lazyGetPtr<Apply<NormalizeType, AnnotatedT>>(deps, dep_index))
265   // and also                        to get<T>         (lazyGetPtr<Apply<NormalizeType, AnnotatedT>>(deps, dep_index))
266   // dep_index is the index of the dep in `deps'.
267   template <typename AnnotatedC>
268   Graph::node_iterator lazyGetPtr(Graph::edge_iterator deps, std::size_t dep_index,
269                                   Graph::node_iterator bindings_begin) const;
270 
271   // Returns nullptr if AnnotatedC was not bound.
272   template <typename AnnotatedC>
273   const RemoveAnnotations<AnnotatedC>* unsafeGet();
274 
275   template <typename AnnotatedC>
276   const std::vector<RemoveAnnotations<AnnotatedC>*>& getMultibindings();
277 
278   void eagerlyInjectMultibindings();
279 };
280 
281 } // namespace impl
282 } // namespace fruit
283 
284 #include <fruit/impl/injector/injector_storage.defn.h>
285 
286 #endif // FRUIT_INJECTOR_STORAGE_H
287