| /* |
| * 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_STORAGE_DEFN_H |
| #define FRUIT_INJECTOR_STORAGE_DEFN_H |
| |
| #include <fruit/impl/component_storage/component_storage_entry.h> |
| #include <fruit/impl/fruit_assert.h> |
| #include <fruit/impl/meta/component.h> |
| #include <fruit/impl/meta/vector.h> |
| #include <fruit/impl/util/demangle_type_name.h> |
| #include <fruit/impl/util/lambda_invoker.h> |
| #include <fruit/impl/util/type_info.h> |
| |
| #include <cassert> |
| |
| // Redundant, but makes KDevelop happy. |
| #include <fruit/impl/injector/injector_storage.h> |
| |
| namespace fruit { |
| namespace impl { |
| |
| inline InjectorStorage::BindingDataNodeIter* InjectorStorage::BindingDataNodeIter::operator->() { |
| return this; |
| } |
| |
| inline void InjectorStorage::BindingDataNodeIter::operator++() { |
| ++itr; |
| } |
| |
| inline bool InjectorStorage::BindingDataNodeIter::operator==(const BindingDataNodeIter& other) const { |
| return itr == other.itr; |
| } |
| |
| inline bool InjectorStorage::BindingDataNodeIter::operator!=(const BindingDataNodeIter& other) const { |
| return itr != other.itr; |
| } |
| |
| inline std::ptrdiff_t InjectorStorage::BindingDataNodeIter::operator-(BindingDataNodeIter other) const { |
| return itr - other.itr; |
| } |
| |
| inline TypeId InjectorStorage::BindingDataNodeIter::getId() { |
| // For these kinds the type_id has a different meaning, but we never need to call this method for those. |
| FruitAssert(itr->kind != ComponentStorageEntry::Kind::COMPRESSED_BINDING); |
| FruitAssert(itr->kind != ComponentStorageEntry::Kind::LAZY_COMPONENT_WITH_NO_ARGS); |
| FruitAssert(itr->kind != ComponentStorageEntry::Kind::REPLACED_LAZY_COMPONENT_WITH_NO_ARGS); |
| FruitAssert(itr->kind != ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_NO_ARGS); |
| FruitAssert(itr->kind != ComponentStorageEntry::Kind::LAZY_COMPONENT_WITH_ARGS); |
| FruitAssert(itr->kind != ComponentStorageEntry::Kind::REPLACED_LAZY_COMPONENT_WITH_ARGS); |
| FruitAssert(itr->kind != ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_ARGS); |
| return itr->type_id; |
| } |
| |
| inline NormalizedBinding InjectorStorage::BindingDataNodeIter::getValue() { |
| return NormalizedBinding(*itr); |
| } |
| |
| inline bool InjectorStorage::BindingDataNodeIter::isTerminal() { |
| #ifdef FRUIT_EXTRA_DEBUG |
| if (itr->kind != ComponentStorageEntry::Kind::BINDING_FOR_CONSTRUCTED_OBJECT && |
| itr->kind != ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION && |
| itr->kind != ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION && |
| itr->kind != ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_WITH_UNKNOWN_ALLOCATION) { |
| std::cerr << "Unexpected binding kind: " << (std::size_t)itr->kind << std::endl; |
| FruitAssert(false); |
| } |
| #endif |
| return itr->kind == ComponentStorageEntry::Kind::BINDING_FOR_CONSTRUCTED_OBJECT; |
| } |
| |
| inline const TypeId* InjectorStorage::BindingDataNodeIter::getEdgesBegin() { |
| FruitAssert(itr->kind == ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION || |
| itr->kind == ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION || |
| itr->kind == ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_WITH_UNKNOWN_ALLOCATION); |
| return itr->binding_for_object_to_construct.deps->deps; |
| } |
| |
| inline const TypeId* InjectorStorage::BindingDataNodeIter::getEdgesEnd() { |
| FruitAssert(itr->kind == ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION || |
| itr->kind == ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION || |
| itr->kind == ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_WITH_UNKNOWN_ALLOCATION); |
| return itr->binding_for_object_to_construct.deps->deps + itr->binding_for_object_to_construct.deps->num_deps; |
| } |
| |
| template <typename AnnotatedT> |
| struct GetFirstStage; |
| |
| // General case, value. |
| template <typename C> |
| struct GetFirstStage { |
| const C* operator()(InjectorStorage& injector, InjectorStorage::Graph::node_iterator node_itr) { |
| return injector.getPtr<C>(node_itr); |
| } |
| }; |
| |
| template <typename C> |
| struct GetFirstStage<const C> { |
| const C* operator()(InjectorStorage& injector, InjectorStorage::Graph::node_iterator node_itr) { |
| return injector.getPtr<C>(node_itr); |
| } |
| }; |
| |
| template <typename C> |
| struct GetFirstStage<std::shared_ptr<C>> { |
| // This method is covered by tests, even though lcov doesn't detect that. |
| C* operator()(InjectorStorage& injector, InjectorStorage::Graph::node_iterator node_itr) { |
| FruitAssert(node_itr.getNode().is_nonconst); |
| return const_cast<C*>(injector.getPtr<C>(node_itr)); |
| } |
| }; |
| |
| template <typename C> |
| struct GetFirstStage<C*> { |
| C* operator()(InjectorStorage& injector, InjectorStorage::Graph::node_iterator node_itr) { |
| FruitAssert(node_itr.getNode().is_nonconst); |
| return const_cast<C*>(injector.getPtr<C>(node_itr)); |
| } |
| }; |
| |
| template <typename C> |
| struct GetFirstStage<const C*> { |
| const C* operator()(InjectorStorage& injector, InjectorStorage::Graph::node_iterator node_itr) { |
| return injector.getPtr<C>(node_itr); |
| } |
| }; |
| |
| template <typename C> |
| struct GetFirstStage<C&> { |
| C* operator()(InjectorStorage& injector, InjectorStorage::Graph::node_iterator node_itr) { |
| FruitAssert(node_itr.getNode().is_nonconst); |
| return const_cast<C*>(injector.getPtr<C>(node_itr)); |
| } |
| }; |
| |
| template <typename C> |
| struct GetFirstStage<const C&> { |
| // This method is covered by tests, even though lcov doesn't detect that. |
| const C* operator()(InjectorStorage& injector, InjectorStorage::Graph::node_iterator node_itr) { |
| return injector.getPtr<C>(node_itr); |
| } |
| }; |
| |
| template <typename C> |
| struct GetFirstStage<Provider<C>> { |
| Provider<C> operator()(InjectorStorage& injector, InjectorStorage::Graph::node_iterator node_itr) { |
| return Provider<C>(&injector, node_itr); |
| } |
| }; |
| |
| template <typename Annotation, typename T> |
| struct GetFirstStage<fruit::Annotated<Annotation, T>> : public GetFirstStage<T> {}; |
| |
| template <typename AnnotatedT> |
| struct GetSecondStage; |
| |
| // General case, value. |
| template <typename C> |
| struct GetSecondStage { |
| C operator()(const C* p) { |
| return *p; |
| } |
| }; |
| |
| template <typename C> |
| struct GetSecondStage<const C> { |
| const C operator()(const C* p) { |
| return *p; |
| } |
| }; |
| |
| template <typename C> |
| struct GetSecondStage<std::shared_ptr<C>> { |
| // This method is covered by tests, even though lcov doesn't detect that. |
| std::shared_ptr<C> operator()(C* p) { |
| return std::shared_ptr<C>(std::shared_ptr<char>(), p); |
| } |
| }; |
| |
| template <typename C> |
| struct GetSecondStage<C*> { |
| C* operator()(C* p) { |
| return p; |
| } |
| }; |
| |
| template <typename C> |
| struct GetSecondStage<const C*> { |
| // This method is covered by tests, even though lcov doesn't detect that. |
| const C* operator()(const C* p) { |
| return p; |
| } |
| }; |
| |
| template <typename C> |
| struct GetSecondStage<C&> { |
| C& operator()(C* p) { |
| return *p; |
| } |
| }; |
| |
| template <typename C> |
| struct GetSecondStage<const C&> { |
| const C& operator()(const C* p) { |
| return *p; |
| } |
| }; |
| |
| template <typename C> |
| struct GetSecondStage<Provider<C>> { |
| Provider<C> operator()(Provider<C> p) { |
| return p; |
| } |
| }; |
| |
| template <typename Annotation, typename T> |
| struct GetSecondStage<fruit::Annotated<Annotation, T>> : public GetSecondStage<T> {}; |
| |
| template <typename AnnotatedT> |
| inline InjectorStorage::RemoveAnnotations<AnnotatedT> InjectorStorage::get() { |
| return GetSecondStage<AnnotatedT>()(GetFirstStage<AnnotatedT>()(*this, lazyGetPtr<NormalizeType<AnnotatedT>>())); |
| } |
| |
| template <typename T> |
| inline T InjectorStorage::get(InjectorStorage::Graph::node_iterator node_iterator) { |
| FruitStaticAssert(fruit::impl::meta::IsSame(fruit::impl::meta::Type<T>, |
| fruit::impl::meta::RemoveAnnotations(fruit::impl::meta::Type<T>))); |
| return GetSecondStage<T>()(GetFirstStage<T>()(*this, node_iterator)); |
| } |
| |
| template <typename AnnotatedC> |
| inline InjectorStorage::Graph::node_iterator InjectorStorage::lazyGetPtr() { |
| return lazyGetPtr(getTypeId<AnnotatedC>()); |
| } |
| |
| template <typename AnnotatedC> |
| inline InjectorStorage::Graph::node_iterator |
| InjectorStorage::lazyGetPtr(Graph::edge_iterator deps, std::size_t dep_index, Graph::node_iterator bindings_begin) { |
| Graph::node_iterator itr = deps.getNodeIterator(dep_index, bindings_begin); |
| FruitAssert(bindings.find(getTypeId<AnnotatedC>()) == itr); |
| FruitAssert(!(bindings.end() == itr)); |
| return itr; |
| } |
| |
| template <typename C> |
| inline const C* InjectorStorage::getPtr(Graph::node_iterator itr) { |
| FruitStaticAssert(fruit::impl::meta::IsSame(fruit::impl::meta::Type<C>, |
| fruit::impl::meta::NormalizeType(fruit::impl::meta::Type<C>))); |
| const void* p = getPtrInternal(itr); |
| return reinterpret_cast<const C*>(p); |
| } |
| |
| template <typename AnnotatedC> |
| inline const InjectorStorage::RemoveAnnotations<AnnotatedC>* InjectorStorage::unsafeGet() { |
| using C = RemoveAnnotations<AnnotatedC>; |
| const void* p = unsafeGetPtr(getTypeId<AnnotatedC>()); |
| return reinterpret_cast<const C*>(p); |
| } |
| |
| inline InjectorStorage::Graph::node_iterator InjectorStorage::lazyGetPtr(TypeId type) { |
| return bindings.at(type); |
| } |
| |
| inline const void* InjectorStorage::unsafeGetPtr(TypeId type) { |
| Graph::node_iterator itr = bindings.find(type); |
| if (itr == bindings.end()) { |
| return nullptr; |
| } |
| return getPtrInternal(itr); |
| } |
| |
| template <typename AnnotatedC> |
| inline const std::vector<InjectorStorage::RemoveAnnotations<AnnotatedC>*>& InjectorStorage::getMultibindings() { |
| using C = RemoveAnnotations<AnnotatedC>; |
| void* p = getMultibindings(getTypeId<AnnotatedC>()); |
| if (p == nullptr) { |
| static std::vector<C*> empty_vector; |
| return empty_vector; |
| } else { |
| return *reinterpret_cast<std::vector<C*>*>(p); |
| } |
| } |
| |
| inline const void* InjectorStorage::getPtrInternal(Graph::node_iterator node_itr) { |
| NormalizedBinding& normalized_binding = node_itr.getNode(); |
| if (!node_itr.isTerminal()) { |
| normalized_binding.object = normalized_binding.create(*this, node_itr); |
| FruitAssert(node_itr.isTerminal()); |
| } |
| return normalized_binding.object; |
| } |
| |
| inline NormalizedMultibindingSet* InjectorStorage::getNormalizedMultibindingSet(TypeId type) { |
| auto itr = multibindings.find(type); |
| if (itr != multibindings.end()) |
| return &(itr->second); |
| else |
| return nullptr; |
| } |
| |
| template <typename AnnotatedC> |
| inline std::shared_ptr<char> InjectorStorage::createMultibindingVector(InjectorStorage& storage) { |
| using C = RemoveAnnotations<AnnotatedC>; |
| TypeId type = getTypeId<AnnotatedC>(); |
| NormalizedMultibindingSet* multibinding_set = storage.getNormalizedMultibindingSet(type); |
| |
| // This method is only called if there was at least 1 multibinding (otherwise the would-be caller would have returned |
| // nullptr |
| // instead of calling this). |
| FruitAssert(multibinding_set != nullptr); |
| |
| if (multibinding_set->v.get() != nullptr) { |
| // Result cached, return early. |
| return multibinding_set->v; |
| } |
| |
| storage.ensureConstructedMultibinding(*multibinding_set); |
| |
| std::vector<C*> s; |
| s.reserve(multibinding_set->elems.size()); |
| for (const NormalizedMultibinding& multibinding : multibinding_set->elems) { |
| FruitAssert(multibinding.is_constructed); |
| s.push_back(reinterpret_cast<C*>(multibinding.object)); |
| } |
| |
| std::shared_ptr<std::vector<C*>> vector_ptr = std::make_shared<std::vector<C*>>(std::move(s)); |
| std::shared_ptr<char> result(vector_ptr, reinterpret_cast<char*>(vector_ptr.get())); |
| |
| multibinding_set->v = result; |
| |
| return result; |
| } |
| |
| template <typename I, typename C, typename AnnotatedC> |
| InjectorStorage::const_object_ptr_t |
| InjectorStorage::createInjectedObjectForBind(InjectorStorage& injector, |
| InjectorStorage::Graph::node_iterator node_itr) { |
| |
| InjectorStorage::Graph::node_iterator bindings_begin = injector.bindings.begin(); |
| const C* cPtr = injector.get<const C*>(injector.lazyGetPtr<AnnotatedC>(node_itr.neighborsBegin(), 0, bindings_begin)); |
| node_itr.setTerminal(); |
| // This step is needed when the cast C->I changes the pointer |
| // (e.g. for multiple inheritance). |
| const I* iPtr = static_cast<const I*>(cPtr); |
| return reinterpret_cast<const_object_ptr_t>(iPtr); |
| } |
| |
| // I, C must not be pointers. |
| template <typename AnnotatedI, typename AnnotatedC> |
| inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForBind() { |
| using I = RemoveAnnotations<AnnotatedI>; |
| using C = RemoveAnnotations<AnnotatedC>; |
| FruitStaticAssert(fruit::impl::meta::Not(fruit::impl::meta::IsPointer(fruit::impl::meta::Type<I>))); |
| FruitStaticAssert(fruit::impl::meta::Not(fruit::impl::meta::IsPointer(fruit::impl::meta::Type<C>))); |
| ComponentStorageEntry result; |
| result.kind = ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION; |
| result.type_id = getTypeId<AnnotatedI>(); |
| ComponentStorageEntry::BindingForObjectToConstruct& binding = result.binding_for_object_to_construct; |
| binding.create = createInjectedObjectForBind<I, C, AnnotatedC>; |
| binding.deps = getBindingDeps<fruit::impl::meta::Vector<fruit::impl::meta::Type<AnnotatedC>>>(); |
| #ifdef FRUIT_EXTRA_DEBUG |
| binding.is_nonconst = true; |
| #endif |
| return result; |
| } |
| |
| // I, C must not be pointers. |
| template <typename AnnotatedI, typename AnnotatedC> |
| inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForConstBind() { |
| using I = RemoveAnnotations<AnnotatedI>; |
| using C = RemoveAnnotations<AnnotatedC>; |
| FruitStaticAssert(fruit::impl::meta::Not(fruit::impl::meta::IsPointer(fruit::impl::meta::Type<I>))); |
| FruitStaticAssert(fruit::impl::meta::Not(fruit::impl::meta::IsPointer(fruit::impl::meta::Type<C>))); |
| ComponentStorageEntry result; |
| result.kind = ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION; |
| result.type_id = getTypeId<AnnotatedI>(); |
| ComponentStorageEntry::BindingForObjectToConstruct& binding = result.binding_for_object_to_construct; |
| binding.create = createInjectedObjectForBind<I, C, AnnotatedC>; |
| binding.deps = getBindingDeps<fruit::impl::meta::Vector<fruit::impl::meta::Type<AnnotatedC>>>(); |
| #ifdef FRUIT_EXTRA_DEBUG |
| binding.is_nonconst = false; |
| #endif |
| return result; |
| } |
| |
| template <typename AnnotatedC, typename C> |
| inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForBindInstance(C& instance) { |
| ComponentStorageEntry result; |
| result.kind = ComponentStorageEntry::Kind::BINDING_FOR_CONSTRUCTED_OBJECT; |
| result.type_id = getTypeId<AnnotatedC>(); |
| ComponentStorageEntry::BindingForConstructedObject& binding = result.binding_for_constructed_object; |
| binding.object_ptr = &instance; |
| #ifdef FRUIT_EXTRA_DEBUG |
| binding.is_nonconst = true; |
| #endif |
| return result; |
| } |
| |
| template <typename AnnotatedC, typename C> |
| inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForBindConstInstance(const C& instance) { |
| ComponentStorageEntry result; |
| result.kind = ComponentStorageEntry::Kind::BINDING_FOR_CONSTRUCTED_OBJECT; |
| result.type_id = getTypeId<AnnotatedC>(); |
| ComponentStorageEntry::BindingForConstructedObject& binding = result.binding_for_constructed_object; |
| binding.object_ptr = &instance; |
| #ifdef FRUIT_EXTRA_DEBUG |
| binding.is_nonconst = false; |
| #endif |
| return result; |
| } |
| |
| // The inner operator() takes an InjectorStorage& and a Graph::edge_iterator (the type's deps) and |
| // returns the injected object as a C*. |
| // This takes care of move-constructing a C into the injector's own allocator if needed. |
| template <typename AnnotatedSignature, typename Lambda, bool lambda_returns_pointer, |
| typename AnnotatedT = InjectorStorage::SignatureType<AnnotatedSignature>, |
| typename AnnotatedArgVector = |
| fruit::impl::meta::Eval<fruit::impl::meta::SignatureArgs(fruit::impl::meta::Type<AnnotatedSignature>)>, |
| typename Indexes = |
| fruit::impl::meta::Eval<fruit::impl::meta::GenerateIntSequence(fruit::impl::meta::VectorSize( |
| fruit::impl::meta::SignatureArgs(fruit::impl::meta::Type<AnnotatedSignature>)))>> |
| struct InvokeLambdaWithInjectedArgVector; |
| |
| // AnnotatedT is of the form C* or Annotated<Annotation, C*> |
| template <typename AnnotatedSignature, typename Lambda, typename AnnotatedT, typename... AnnotatedArgs, |
| typename... Indexes> |
| struct InvokeLambdaWithInjectedArgVector<AnnotatedSignature, Lambda, true /* lambda_returns_pointer */, AnnotatedT, |
| fruit::impl::meta::Vector<AnnotatedArgs...>, |
| fruit::impl::meta::Vector<Indexes...>> { |
| using CPtr = InjectorStorage::RemoveAnnotations<AnnotatedT>; |
| using AnnotatedC = InjectorStorage::NormalizeType<AnnotatedT>; |
| |
| FRUIT_ALWAYS_INLINE |
| CPtr operator()(InjectorStorage& injector, FixedSizeAllocator& allocator) { |
| // `injector' *is* used below, but when there are no AnnotatedArgs some compilers report it as unused. |
| (void)injector; |
| CPtr cPtr = |
| LambdaInvoker::invoke<Lambda, typename InjectorStorage::AnnotationRemover< |
| typename fruit::impl::meta::TypeUnwrapper<AnnotatedArgs>::type>::type...>( |
| injector.get<fruit::impl::meta::UnwrapType<AnnotatedArgs>>()...); |
| |
| allocator.registerExternallyAllocatedObject(cPtr); |
| |
| // This can happen if the user-supplied provider returns nullptr. |
| if (cPtr == nullptr) { |
| InjectorStorage::fatal("attempting to get an instance for the type " + std::string(getTypeId<AnnotatedC>()) + |
| " but the provider returned nullptr"); |
| FRUIT_UNREACHABLE; // LCOV_EXCL_LINE |
| } |
| |
| return cPtr; |
| } |
| |
| // This is not inlined in outerConstructHelper so that when get<> needs to construct an object more complex than a |
| // pointer |
| // (e.g. a shared_ptr), that happens as late as possible so that it's easier for the optimizer to optimize out some |
| // operations (e.g. the increment/decrement/check of shared_ptr's reference count). |
| template <typename... GetFirstStageResults> |
| FRUIT_ALWAYS_INLINE CPtr innerConstructHelper(InjectorStorage& injector, |
| GetFirstStageResults... getFirstStageResults) { |
| // `injector' *is* used below, but when there are no AnnotatedArgs some compilers report it as unused. |
| (void)injector; |
| return LambdaInvoker::invoke<Lambda, typename InjectorStorage::AnnotationRemover< |
| typename fruit::impl::meta::TypeUnwrapper<AnnotatedArgs>::type>::type...>( |
| GetSecondStage<InjectorStorage::RemoveAnnotations<fruit::impl::meta::UnwrapType<AnnotatedArgs>>>()( |
| getFirstStageResults)...); |
| } |
| |
| // This is not inlined in operator() so that all the lazyGetPtr() calls happen first (instead of being interleaved |
| // with the get() calls). The lazyGetPtr() calls don't branch, while the get() calls branch on the result of the |
| // lazyGetPtr()s, so it's faster to execute them in this order. |
| template <typename... NodeItrs> |
| FRUIT_ALWAYS_INLINE CPtr outerConstructHelper(InjectorStorage& injector, NodeItrs... nodeItrs) { |
| // `injector' *is* used below, but when there are no AnnotatedArgs some compilers report it as unused. |
| (void)injector; |
| return innerConstructHelper( |
| injector, GetFirstStage<InjectorStorage::RemoveAnnotations<fruit::impl::meta::UnwrapType<AnnotatedArgs>>>()( |
| injector, nodeItrs)...); |
| } |
| |
| FRUIT_ALWAYS_INLINE |
| CPtr operator()(InjectorStorage& injector, SemistaticGraph<TypeId, NormalizedBinding>& bindings, |
| FixedSizeAllocator& allocator, InjectorStorage::Graph::edge_iterator deps) { |
| // `deps' *is* used below, but when there are no AnnotatedArgs some compilers report it as unused. |
| (void)deps; |
| |
| InjectorStorage::Graph::node_iterator bindings_begin = bindings.begin(); |
| // `bindings_begin' *is* used below, but when there are no AnnotatedArgs some compilers report it as unused. |
| (void)bindings_begin; |
| CPtr cPtr = outerConstructHelper( |
| injector, |
| injector.lazyGetPtr<InjectorStorage::NormalizeType<fruit::impl::meta::UnwrapType<AnnotatedArgs>>>( |
| deps, Indexes::value, bindings_begin)...); |
| allocator.registerExternallyAllocatedObject(cPtr); |
| |
| // This can happen if the user-supplied provider returns nullptr. |
| if (cPtr == nullptr) { |
| InjectorStorage::fatal("attempting to get an instance for the type " + std::string(getTypeId<AnnotatedC>()) + |
| " but the provider returned nullptr"); |
| FRUIT_UNREACHABLE; // LCOV_EXCL_LINE |
| } |
| |
| return cPtr; |
| } |
| }; |
| |
| template <typename AnnotatedSignature, typename Lambda, typename AnnotatedC, typename... AnnotatedArgs, |
| typename... Indexes> |
| struct InvokeLambdaWithInjectedArgVector<AnnotatedSignature, Lambda, false /* lambda_returns_pointer */, AnnotatedC, |
| fruit::impl::meta::Vector<AnnotatedArgs...>, |
| fruit::impl::meta::Vector<Indexes...>> { |
| using C = InjectorStorage::RemoveAnnotations<AnnotatedC>; |
| |
| FRUIT_ALWAYS_INLINE |
| C* operator()(InjectorStorage& injector, FixedSizeAllocator& allocator) { |
| // `injector' *is* used below, but when there are no AnnotatedArgs some compilers report it as unused. |
| (void)injector; |
| return allocator.constructObject<AnnotatedC, C&&>( |
| LambdaInvoker::invoke<Lambda, typename InjectorStorage::AnnotationRemover< |
| typename fruit::impl::meta::TypeUnwrapper<AnnotatedArgs>::type>::type&&...>( |
| injector.get<typename fruit::impl::meta::TypeUnwrapper<AnnotatedArgs>::type>()...)); |
| } |
| |
| // This is not inlined in outerConstructHelper so that when get<> needs to construct an object more complex than a |
| // pointer |
| // (e.g. a shared_ptr), that happens as late as possible so that it's easier for the optimizer to optimize out some |
| // operations (e.g. the increment/decrement/check of shared_ptr's reference count). |
| template <typename... GetFirstStageResults> |
| FRUIT_ALWAYS_INLINE C* innerConstructHelper(InjectorStorage& injector, FixedSizeAllocator& allocator, |
| GetFirstStageResults... getFirstStageResults) { |
| // `injector' *is* used below, but when there are no AnnotatedArgs some compilers report it as unused. |
| (void)injector; |
| return allocator.constructObject<AnnotatedC, C&&>( |
| LambdaInvoker::invoke<Lambda, typename InjectorStorage::AnnotationRemover< |
| typename fruit::impl::meta::TypeUnwrapper<AnnotatedArgs>::type>::type...>( |
| GetSecondStage<typename InjectorStorage::AnnotationRemover< |
| typename fruit::impl::meta::TypeUnwrapper<AnnotatedArgs>::type>::type>()(getFirstStageResults)...)); |
| } |
| |
| // This is not inlined in operator() so that all the lazyGetPtr() calls happen first (instead of being interleaved |
| // with the get() calls). The lazyGetPtr() calls don't branch, while the get() calls branch on the result of the |
| // lazyGetPtr()s, so it's faster to execute them in this order. |
| template <typename... NodeItrs> |
| FRUIT_ALWAYS_INLINE C* outerConstructHelper(InjectorStorage& injector, FixedSizeAllocator& allocator, |
| NodeItrs... nodeItrs) { |
| // `injector' *is* used below, but when there are no AnnotatedArgs some compilers report it as unused. |
| (void)injector; |
| return innerConstructHelper( |
| injector, allocator, |
| GetFirstStage<InjectorStorage::RemoveAnnotations<fruit::impl::meta::UnwrapType<AnnotatedArgs>>>()(injector, |
| nodeItrs)...); |
| } |
| |
| C* operator()(InjectorStorage& injector, SemistaticGraph<TypeId, NormalizedBinding>& bindings, |
| FixedSizeAllocator& allocator, InjectorStorage::Graph::edge_iterator deps) { |
| InjectorStorage::Graph::node_iterator bindings_begin = bindings.begin(); |
| // `bindings_begin' *is* used below, but when there are no AnnotatedArgs some compilers report it as unused. |
| (void)bindings_begin; |
| |
| // `deps' *is* used below, but when there are no AnnotatedArgs some compilers report it as unused. |
| (void)deps; |
| |
| // `injector' *is* used below, but when there are no AnnotatedArgs some compilers report it as unused. |
| (void)injector; |
| C* p = outerConstructHelper( |
| injector, allocator, |
| injector.lazyGetPtr<InjectorStorage::NormalizeType<fruit::impl::meta::UnwrapType<AnnotatedArgs>>>( |
| deps, Indexes::value, bindings_begin)...); |
| return p; |
| } |
| }; |
| |
| template <typename C, typename T, typename AnnotatedSignature, typename Lambda> |
| InjectorStorage::const_object_ptr_t InjectorStorage::createInjectedObjectForProvider(InjectorStorage& injector, |
| Graph::node_iterator node_itr) { |
| C* cPtr = InvokeLambdaWithInjectedArgVector<AnnotatedSignature, Lambda, std::is_pointer<T>::value>()( |
| injector, injector.bindings, injector.allocator, node_itr.neighborsBegin()); |
| node_itr.setTerminal(); |
| return reinterpret_cast<const_object_ptr_t>(cPtr); |
| } |
| |
| template <typename AnnotatedSignature, typename Lambda> |
| inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForProvider() { |
| #ifdef FRUIT_EXTRA_DEBUG |
| using Signature = |
| fruit::impl::meta::UnwrapType<fruit::impl::meta::Eval<fruit::impl::meta::RemoveAnnotationsFromSignature( |
| fruit::impl::meta::Type<AnnotatedSignature>)>>; |
| FruitStaticAssert(fruit::impl::meta::IsSame(fruit::impl::meta::Type<Signature>, |
| fruit::impl::meta::FunctionSignature(fruit::impl::meta::Type<Lambda>))); |
| #endif |
| using AnnotatedT = SignatureType<AnnotatedSignature>; |
| using AnnotatedC = NormalizeType<AnnotatedT>; |
| // T is either C or C*. |
| using T = RemoveAnnotations<AnnotatedT>; |
| using C = NormalizeType<T>; |
| ComponentStorageEntry result; |
| constexpr bool needs_allocation = !std::is_pointer<T>::value; |
| result.kind = needs_allocation |
| ? ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION |
| : ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION; |
| result.type_id = getTypeId<AnnotatedC>(); |
| ComponentStorageEntry::BindingForObjectToConstruct& binding = result.binding_for_object_to_construct; |
| binding.create = createInjectedObjectForProvider<C, T, AnnotatedSignature, Lambda>; |
| binding.deps = getBindingDeps<NormalizedSignatureArgs<AnnotatedSignature>>(); |
| #ifdef FRUIT_EXTRA_DEBUG |
| binding.is_nonconst = true; |
| #endif |
| return result; |
| } |
| |
| template <typename I, typename C, typename T, typename AnnotatedSignature, typename Lambda> |
| InjectorStorage::const_object_ptr_t |
| InjectorStorage::createInjectedObjectForCompressedProvider(InjectorStorage& injector, Graph::node_iterator node_itr) { |
| C* cPtr = InvokeLambdaWithInjectedArgVector<AnnotatedSignature, Lambda, std::is_pointer<T>::value>()( |
| injector, injector.bindings, injector.allocator, node_itr.neighborsBegin()); |
| node_itr.setTerminal(); |
| I* iPtr = static_cast<I*>(cPtr); |
| return reinterpret_cast<object_ptr_t>(iPtr); |
| } |
| |
| template <typename AnnotatedSignature, typename Lambda, typename AnnotatedI> |
| inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForCompressedProvider() { |
| #ifdef FRUIT_EXTRA_DEBUG |
| using Signature = |
| fruit::impl::meta::UnwrapType<fruit::impl::meta::Eval<fruit::impl::meta::RemoveAnnotationsFromSignature( |
| fruit::impl::meta::Type<AnnotatedSignature>)>>; |
| FruitStaticAssert(fruit::impl::meta::IsSame(fruit::impl::meta::Type<Signature>, |
| fruit::impl::meta::FunctionSignature(fruit::impl::meta::Type<Lambda>))); |
| #endif |
| using AnnotatedT = SignatureType<AnnotatedSignature>; |
| using AnnotatedC = NormalizeType<AnnotatedT>; |
| // T is either C or C*. |
| using T = RemoveAnnotations<AnnotatedT>; |
| using C = NormalizeType<T>; |
| using I = RemoveAnnotations<AnnotatedI>; |
| ComponentStorageEntry result; |
| result.kind = ComponentStorageEntry::Kind::COMPRESSED_BINDING; |
| result.type_id = getTypeId<AnnotatedI>(); |
| ComponentStorageEntry::CompressedBinding& binding = result.compressed_binding; |
| binding.c_type_id = getTypeId<AnnotatedC>(); |
| binding.create = createInjectedObjectForCompressedProvider<I, C, T, AnnotatedSignature, Lambda>; |
| return result; |
| } |
| |
| // The inner operator() takes an InjectorStorage& and a Graph::edge_iterator (the type's deps) and |
| // returns the injected object as a C*. |
| // This takes care of allocating the required space into the injector's allocator. |
| template <typename AnnotatedSignature, |
| typename Indexes = |
| fruit::impl::meta::Eval<fruit::impl::meta::GenerateIntSequence(fruit::impl::meta::VectorSize( |
| fruit::impl::meta::SignatureArgs(fruit::impl::meta::Type<AnnotatedSignature>)))>> |
| struct InvokeConstructorWithInjectedArgVector; |
| |
| template <typename AnnotatedC, typename... AnnotatedArgs, typename... Indexes> |
| struct InvokeConstructorWithInjectedArgVector<AnnotatedC(AnnotatedArgs...), fruit::impl::meta::Vector<Indexes...>> { |
| using C = InjectorStorage::RemoveAnnotations<AnnotatedC>; |
| |
| // This is not inlined in outerConstructHelper so that when get<> needs to construct an object more complex than a |
| // pointer |
| // (e.g. a shared_ptr), that happens as late as possible so that it's easier for the optimizer to optimize out some |
| // operations (e.g. the increment/decrement/check of shared_ptr's reference count). |
| template <typename... GetFirstStageResults> |
| FRUIT_ALWAYS_INLINE C* innerConstructHelper(InjectorStorage& injector, FixedSizeAllocator& allocator, |
| GetFirstStageResults... getFirstStageResults) { |
| // `injector' *is* used below, but when there are no AnnotatedArgs some compilers report it as unused. |
| (void)injector; |
| return allocator.constructObject<AnnotatedC, typename InjectorStorage::AnnotationRemover<AnnotatedArgs>::type&&...>( |
| GetSecondStage<InjectorStorage::RemoveAnnotations<AnnotatedArgs>>()(getFirstStageResults)...); |
| } |
| |
| // This is not inlined in operator() so that all the lazyGetPtr() calls happen first (instead of being interleaved |
| // with the get() calls). The lazyGetPtr() calls don't branch, while the get() calls branch on the result of the |
| // lazyGetPtr()s, so it's faster to execute them in this order. |
| template <typename... NodeItrs> |
| FRUIT_ALWAYS_INLINE C* outerConstructHelper(InjectorStorage& injector, FixedSizeAllocator& allocator, |
| NodeItrs... nodeItrs) { |
| // `injector' *is* used below, but when there are no AnnotatedArgs some compilers report it as unused. |
| (void)injector; |
| return innerConstructHelper( |
| injector, allocator, GetFirstStage<InjectorStorage::RemoveAnnotations<AnnotatedArgs>>()(injector, nodeItrs)...); |
| } |
| |
| FRUIT_ALWAYS_INLINE |
| C* operator()(InjectorStorage& injector, SemistaticGraph<TypeId, NormalizedBinding>& bindings, |
| FixedSizeAllocator& allocator, InjectorStorage::Graph::edge_iterator deps) { |
| |
| // `deps' *is* used below, but when there are no Args some compilers report it as unused. |
| (void)deps; |
| |
| InjectorStorage::Graph::node_iterator bindings_begin = bindings.begin(); |
| // `bindings_begin' *is* used below, but when there are no Args some compilers report it as unused. |
| (void)bindings_begin; |
| C* p = outerConstructHelper(injector, allocator, |
| injector.lazyGetPtr<typename InjectorStorage::TypeNormalizer<AnnotatedArgs>::type>( |
| deps, Indexes::value, bindings_begin)...); |
| return p; |
| } |
| }; |
| |
| template <typename C, typename AnnotatedSignature> |
| InjectorStorage::const_object_ptr_t InjectorStorage::createInjectedObjectForConstructor(InjectorStorage& injector, |
| Graph::node_iterator node_itr) { |
| C* cPtr = InvokeConstructorWithInjectedArgVector<AnnotatedSignature>()(injector, injector.bindings, |
| injector.allocator, node_itr.neighborsBegin()); |
| node_itr.setTerminal(); |
| return reinterpret_cast<InjectorStorage::object_ptr_t>(cPtr); |
| } |
| |
| template <typename AnnotatedSignature> |
| inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForConstructor() { |
| using AnnotatedC = SignatureType<AnnotatedSignature>; |
| using C = RemoveAnnotations<AnnotatedC>; |
| ComponentStorageEntry result; |
| result.kind = ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION; |
| result.type_id = getTypeId<AnnotatedC>(); |
| ComponentStorageEntry::BindingForObjectToConstruct& binding = result.binding_for_object_to_construct; |
| binding.create = createInjectedObjectForConstructor<C, AnnotatedSignature>; |
| binding.deps = getBindingDeps<NormalizedSignatureArgs<AnnotatedSignature>>(); |
| #ifdef FRUIT_EXTRA_DEBUG |
| binding.is_nonconst = true; |
| #endif |
| return result; |
| } |
| |
| template <typename I, typename C, typename AnnotatedSignature> |
| InjectorStorage::const_object_ptr_t |
| InjectorStorage::createInjectedObjectForCompressedConstructor(InjectorStorage& injector, |
| Graph::node_iterator node_itr) { |
| C* cPtr = InvokeConstructorWithInjectedArgVector<AnnotatedSignature>()(injector, injector.bindings, |
| injector.allocator, node_itr.neighborsBegin()); |
| node_itr.setTerminal(); |
| I* iPtr = static_cast<I*>(cPtr); |
| return reinterpret_cast<object_ptr_t>(iPtr); |
| } |
| |
| template <typename AnnotatedSignature, typename AnnotatedI> |
| inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForCompressedConstructor() { |
| using AnnotatedC = SignatureType<AnnotatedSignature>; |
| using C = RemoveAnnotations<AnnotatedC>; |
| using I = RemoveAnnotations<AnnotatedI>; |
| ComponentStorageEntry result; |
| result.kind = ComponentStorageEntry::Kind::COMPRESSED_BINDING; |
| result.type_id = getTypeId<AnnotatedI>(); |
| ComponentStorageEntry::CompressedBinding& binding = result.compressed_binding; |
| binding.c_type_id = getTypeId<AnnotatedC>(); |
| binding.create = createInjectedObjectForCompressedConstructor<I, C, AnnotatedSignature>; |
| return result; |
| } |
| |
| template <typename AnnotatedT> |
| inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForMultibindingVectorCreator() { |
| ComponentStorageEntry result; |
| result.kind = ComponentStorageEntry::Kind::MULTIBINDING_VECTOR_CREATOR; |
| result.type_id = getTypeId<AnnotatedT>(); |
| ComponentStorageEntry::MultibindingVectorCreator& binding = result.multibinding_vector_creator; |
| binding.get_multibindings_vector = createMultibindingVector<AnnotatedT>; |
| return result; |
| } |
| |
| template <typename I, typename C, typename AnnotatedCPtr> |
| InjectorStorage::object_ptr_t InjectorStorage::createInjectedObjectForMultibinding(InjectorStorage& m) { |
| C* cPtr = m.get<AnnotatedCPtr>(); |
| // This step is needed when the cast C->I changes the pointer |
| // (e.g. for multiple inheritance). |
| I* iPtr = static_cast<I*>(cPtr); |
| return reinterpret_cast<InjectorStorage::object_ptr_t>(iPtr); |
| } |
| |
| template <typename AnnotatedI, typename AnnotatedC> |
| inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForMultibinding() { |
| using AnnotatedCPtr = fruit::impl::meta::UnwrapType< |
| fruit::impl::meta::Eval<fruit::impl::meta::AddPointerInAnnotatedType(fruit::impl::meta::Type<AnnotatedC>)>>; |
| using I = RemoveAnnotations<AnnotatedI>; |
| using C = RemoveAnnotations<AnnotatedC>; |
| ComponentStorageEntry result; |
| result.kind = ComponentStorageEntry::Kind::MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION; |
| result.type_id = getTypeId<AnnotatedI>(); |
| ComponentStorageEntry::MultibindingForObjectToConstruct& binding = result.multibinding_for_object_to_construct; |
| binding.create = createInjectedObjectForMultibinding<I, C, AnnotatedCPtr>; |
| binding.deps = getBindingDeps<fruit::impl::meta::Vector<fruit::impl::meta::Type<AnnotatedC>>>(); |
| return result; |
| } |
| |
| template <typename AnnotatedC, typename C> |
| inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForInstanceMultibinding(C& instance) { |
| ComponentStorageEntry result; |
| result.kind = ComponentStorageEntry::Kind::MULTIBINDING_FOR_CONSTRUCTED_OBJECT; |
| result.type_id = getTypeId<AnnotatedC>(); |
| ComponentStorageEntry::MultibindingForConstructedObject& binding = result.multibinding_for_constructed_object; |
| binding.object_ptr = &instance; |
| return result; |
| } |
| |
| template <typename C, typename T, typename AnnotatedSignature, typename Lambda> |
| InjectorStorage::object_ptr_t InjectorStorage::createInjectedObjectForMultibindingProvider(InjectorStorage& injector) { |
| C* cPtr = InvokeLambdaWithInjectedArgVector<AnnotatedSignature, Lambda, std::is_pointer<T>::value>()( |
| injector, injector.allocator); |
| return reinterpret_cast<object_ptr_t>(cPtr); |
| } |
| |
| template <typename AnnotatedSignature, typename Lambda> |
| inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForMultibindingProvider() { |
| #ifdef FRUIT_EXTRA_DEBUG |
| using Signature = |
| fruit::impl::meta::UnwrapType<fruit::impl::meta::Eval<fruit::impl::meta::RemoveAnnotationsFromSignature( |
| fruit::impl::meta::Type<AnnotatedSignature>)>>; |
| FruitStaticAssert(fruit::impl::meta::IsSame(fruit::impl::meta::Type<Signature>, |
| fruit::impl::meta::FunctionSignature(fruit::impl::meta::Type<Lambda>))); |
| #endif |
| |
| using AnnotatedT = SignatureType<AnnotatedSignature>; |
| using AnnotatedC = NormalizeType<AnnotatedT>; |
| using T = RemoveAnnotations<AnnotatedT>; |
| using C = NormalizeType<T>; |
| ComponentStorageEntry result; |
| bool needs_allocation = !std::is_pointer<T>::value; |
| if (needs_allocation) |
| result.kind = ComponentStorageEntry::Kind::MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION; |
| else |
| result.kind = ComponentStorageEntry::Kind::MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION; |
| result.type_id = getTypeId<AnnotatedC>(); |
| ComponentStorageEntry::MultibindingForObjectToConstruct& binding = result.multibinding_for_object_to_construct; |
| binding.create = createInjectedObjectForMultibindingProvider<C, T, AnnotatedSignature, Lambda>; |
| binding.deps = getBindingDeps<NormalizedSignatureArgs<AnnotatedSignature>>(); |
| return result; |
| } |
| |
| } // namespace fruit |
| } // namespace impl |
| |
| #endif // FRUIT_INJECTOR_STORAGE_DEFN_H |