/* * 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 #include #include #include #include #include #include #include // Redundant, but makes KDevelop happy. #include 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() { #if 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 struct GetFirstStage; // General case, value. template struct GetFirstStage { const C* operator()(InjectorStorage& injector, InjectorStorage::Graph::node_iterator node_itr) { return injector.getPtr(node_itr); } }; template struct GetFirstStage { const C* operator()(InjectorStorage& injector, InjectorStorage::Graph::node_iterator node_itr) { return injector.getPtr(node_itr); } }; template struct GetFirstStage> { // 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(injector.getPtr(node_itr)); } }; template struct GetFirstStage { C* operator()(InjectorStorage& injector, InjectorStorage::Graph::node_iterator node_itr) { FruitAssert(node_itr.getNode().is_nonconst); return const_cast(injector.getPtr(node_itr)); } }; template struct GetFirstStage { const C* operator()(InjectorStorage& injector, InjectorStorage::Graph::node_iterator node_itr) { return injector.getPtr(node_itr); } }; template struct GetFirstStage { C* operator()(InjectorStorage& injector, InjectorStorage::Graph::node_iterator node_itr) { FruitAssert(node_itr.getNode().is_nonconst); return const_cast(injector.getPtr(node_itr)); } }; template struct GetFirstStage { // 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(node_itr); } }; template struct GetFirstStage> { Provider operator()(InjectorStorage& injector, InjectorStorage::Graph::node_iterator node_itr) { return Provider(&injector, node_itr); } }; template struct GetFirstStage> : public GetFirstStage {}; template struct GetSecondStage; // General case, value. template struct GetSecondStage { C operator()(const C* p) { return *p; } }; template struct GetSecondStage { const C operator()(const C* p) { return *p; } }; template struct GetSecondStage> { // This method is covered by tests, even though lcov doesn't detect that. std::shared_ptr operator()(C* p) { return std::shared_ptr(std::shared_ptr(), p); } }; template struct GetSecondStage { C* operator()(C* p) { return p; } }; template struct GetSecondStage { // This method is covered by tests, even though lcov doesn't detect that. const C* operator()(const C* p) { return p; } }; template struct GetSecondStage { C& operator()(C* p) { return *p; } }; template struct GetSecondStage { const C& operator()(const C* p) { return *p; } }; template struct GetSecondStage> { Provider operator()(Provider p) { return p; } }; template struct GetSecondStage> : public GetSecondStage {}; template inline InjectorStorage::RemoveAnnotations InjectorStorage::get() { std::lock_guard lock(mutex); return GetSecondStage()(GetFirstStage()(*this, lazyGetPtr>())); } template inline T InjectorStorage::get(InjectorStorage::Graph::node_iterator node_iterator) { FruitStaticAssert(fruit::impl::meta::IsSame(fruit::impl::meta::Type, fruit::impl::meta::RemoveAnnotations(fruit::impl::meta::Type))); std::lock_guard lock(mutex); return GetSecondStage()(GetFirstStage()(*this, node_iterator)); } template inline InjectorStorage::Graph::node_iterator InjectorStorage::lazyGetPtr() { return lazyGetPtr(getTypeId()); } template inline InjectorStorage::Graph::node_iterator InjectorStorage::lazyGetPtr(Graph::edge_iterator deps, std::size_t dep_index, Graph::node_iterator bindings_begin) const { // Here we (intentionally) do not lock `mutex', since this is a read-only method that only accesses immutable data. Graph::node_iterator itr = deps.getNodeIterator(dep_index, bindings_begin); FruitAssert(bindings.find(getTypeId()) == Graph::const_node_iterator(itr)); FruitAssert(!(bindings.end() == Graph::const_node_iterator(itr))); return itr; } template inline const C* InjectorStorage::getPtr(Graph::node_iterator itr) { FruitStaticAssert(fruit::impl::meta::IsSame(fruit::impl::meta::Type, fruit::impl::meta::NormalizeType(fruit::impl::meta::Type))); const void* p = getPtrInternal(itr); return reinterpret_cast(p); } template inline const InjectorStorage::RemoveAnnotations* InjectorStorage::unsafeGet() { std::lock_guard lock(mutex); using C = RemoveAnnotations; const void* p = unsafeGetPtr(getTypeId()); return reinterpret_cast(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 inline const std::vector*>& InjectorStorage::getMultibindings() { std::lock_guard lock(mutex); using C = RemoveAnnotations; void* p = getMultibindings(getTypeId()); if (p == nullptr) { static std::vector empty_vector; return empty_vector; } else { return *reinterpret_cast*>(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 inline std::shared_ptr InjectorStorage::createMultibindingVector(InjectorStorage& storage) { using C = RemoveAnnotations; TypeId type = getTypeId(); 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 s; s.reserve(multibinding_set->elems.size()); for (const NormalizedMultibinding& multibinding : multibinding_set->elems) { FruitAssert(multibinding.is_constructed); s.push_back(reinterpret_cast(multibinding.object)); } std::shared_ptr> vector_ptr = std::make_shared>(std::move(s)); std::shared_ptr result(vector_ptr, reinterpret_cast(vector_ptr.get())); multibinding_set->v = result; return result; } template 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(injector.lazyGetPtr(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(cPtr); return reinterpret_cast(iPtr); } // I, C must not be pointers. template inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForBind() { using I = RemoveAnnotations; using C = RemoveAnnotations; FruitStaticAssert(fruit::impl::meta::Not(fruit::impl::meta::IsPointer(fruit::impl::meta::Type))); FruitStaticAssert(fruit::impl::meta::Not(fruit::impl::meta::IsPointer(fruit::impl::meta::Type))); ComponentStorageEntry result; result.kind = ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION; result.type_id = getTypeId(); ComponentStorageEntry::BindingForObjectToConstruct& binding = result.binding_for_object_to_construct; binding.create = createInjectedObjectForBind; binding.deps = getBindingDeps>>(); #if FRUIT_EXTRA_DEBUG binding.is_nonconst = true; #endif return result; } // I, C must not be pointers. template inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForConstBind() { using I = RemoveAnnotations; using C = RemoveAnnotations; FruitStaticAssert(fruit::impl::meta::Not(fruit::impl::meta::IsPointer(fruit::impl::meta::Type))); FruitStaticAssert(fruit::impl::meta::Not(fruit::impl::meta::IsPointer(fruit::impl::meta::Type))); ComponentStorageEntry result; result.kind = ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION; result.type_id = getTypeId(); ComponentStorageEntry::BindingForObjectToConstruct& binding = result.binding_for_object_to_construct; binding.create = createInjectedObjectForBind; binding.deps = getBindingDeps>>(); #if FRUIT_EXTRA_DEBUG binding.is_nonconst = false; #endif return result; } template inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForBindInstance(C& instance) { ComponentStorageEntry result; result.kind = ComponentStorageEntry::Kind::BINDING_FOR_CONSTRUCTED_OBJECT; result.type_id = getTypeId(); ComponentStorageEntry::BindingForConstructedObject& binding = result.binding_for_constructed_object; binding.object_ptr = &instance; #if FRUIT_EXTRA_DEBUG binding.is_nonconst = true; #endif return result; } template inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForBindConstInstance(const C& instance) { ComponentStorageEntry result; result.kind = ComponentStorageEntry::Kind::BINDING_FOR_CONSTRUCTED_OBJECT; result.type_id = getTypeId(); ComponentStorageEntry::BindingForConstructedObject& binding = result.binding_for_constructed_object; binding.object_ptr = &instance; #if 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 AnnotatedArgVector = fruit::impl::meta::Eval)>, typename Indexes = fruit::impl::meta::Eval)))>> struct InvokeLambdaWithInjectedArgVector; // AnnotatedT is of the form C* or Annotated template struct InvokeLambdaWithInjectedArgVector, fruit::impl::meta::Vector> { using CPtr = InjectorStorage::RemoveAnnotations; using AnnotatedC = InjectorStorage::NormalizeType; 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::type>::type...>( injector.get>()...); 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()) + " 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 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::type>::type...>( GetSecondStage>>()( 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 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>>()( injector, nodeItrs)...); } FRUIT_ALWAYS_INLINE CPtr operator()(InjectorStorage& injector, SemistaticGraph& 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>>( deps, fruit::impl::meta::getIntValue(), 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()) + " but the provider returned nullptr"); FRUIT_UNREACHABLE; // LCOV_EXCL_LINE } return cPtr; } }; template struct InvokeLambdaWithInjectedArgVector, fruit::impl::meta::Vector> { using C = InjectorStorage::RemoveAnnotations; 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( LambdaInvoker::invoke::type>::type&&...>( injector.get::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 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( LambdaInvoker::invoke::type>::type...>( GetSecondStage::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 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>>()(injector, nodeItrs)...); } C* operator()(InjectorStorage& injector, SemistaticGraph& 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>>( deps, fruit::impl::meta::getIntValue(), bindings_begin)...); return p; } }; template InjectorStorage::const_object_ptr_t InjectorStorage::createInjectedObjectForProvider(InjectorStorage& injector, Graph::node_iterator node_itr) { C* cPtr = InvokeLambdaWithInjectedArgVector::value>()( injector, injector.bindings, injector.allocator, node_itr.neighborsBegin()); node_itr.setTerminal(); return reinterpret_cast(cPtr); } template inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForProvider() { #if FRUIT_EXTRA_DEBUG using Signature = fruit::impl::meta::UnwrapType)>>; FruitStaticAssert(fruit::impl::meta::IsSame(fruit::impl::meta::Type, fruit::impl::meta::FunctionSignature(fruit::impl::meta::Type))); #endif using AnnotatedT = SignatureType; using AnnotatedC = NormalizeType; // T is either C or C*. using T = RemoveAnnotations; using C = NormalizeType; ComponentStorageEntry result; constexpr bool needs_allocation = !std::is_pointer::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(); ComponentStorageEntry::BindingForObjectToConstruct& binding = result.binding_for_object_to_construct; binding.create = createInjectedObjectForProvider; binding.deps = getBindingDeps>(); #if FRUIT_EXTRA_DEBUG binding.is_nonconst = true; #endif return result; } template InjectorStorage::const_object_ptr_t InjectorStorage::createInjectedObjectForCompressedProvider(InjectorStorage& injector, Graph::node_iterator node_itr) { C* cPtr = InvokeLambdaWithInjectedArgVector::value>()( injector, injector.bindings, injector.allocator, node_itr.neighborsBegin()); node_itr.setTerminal(); I* iPtr = static_cast(cPtr); return reinterpret_cast(iPtr); } template inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForCompressedProvider() { #if FRUIT_EXTRA_DEBUG using Signature = fruit::impl::meta::UnwrapType)>>; FruitStaticAssert(fruit::impl::meta::IsSame(fruit::impl::meta::Type, fruit::impl::meta::FunctionSignature(fruit::impl::meta::Type))); #endif using AnnotatedT = SignatureType; using AnnotatedC = NormalizeType; // T is either C or C*. using T = RemoveAnnotations; using C = NormalizeType; using I = RemoveAnnotations; ComponentStorageEntry result; result.kind = ComponentStorageEntry::Kind::COMPRESSED_BINDING; result.type_id = getTypeId(); ComponentStorageEntry::CompressedBinding& binding = result.compressed_binding; binding.c_type_id = getTypeId(); binding.create = createInjectedObjectForCompressedProvider; 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 )))>> struct InvokeConstructorWithInjectedArgVector; template struct InvokeConstructorWithInjectedArgVector> { using C = InjectorStorage::RemoveAnnotations; // 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 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::type&&...>( GetSecondStage>()(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 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>()(injector, nodeItrs)...); } FRUIT_ALWAYS_INLINE C* operator()(InjectorStorage& injector, SemistaticGraph& 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::type>( deps, fruit::impl::meta::getIntValue(), bindings_begin)...); return p; } }; template InjectorStorage::const_object_ptr_t InjectorStorage::createInjectedObjectForConstructor(InjectorStorage& injector, Graph::node_iterator node_itr) { C* cPtr = InvokeConstructorWithInjectedArgVector()(injector, injector.bindings, injector.allocator, node_itr.neighborsBegin()); node_itr.setTerminal(); return reinterpret_cast(cPtr); } template inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForConstructor() { using AnnotatedC = SignatureType; using C = RemoveAnnotations; ComponentStorageEntry result; result.kind = ComponentStorageEntry::Kind::BINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_ALLOCATION; result.type_id = getTypeId(); ComponentStorageEntry::BindingForObjectToConstruct& binding = result.binding_for_object_to_construct; binding.create = createInjectedObjectForConstructor; binding.deps = getBindingDeps>(); #if FRUIT_EXTRA_DEBUG binding.is_nonconst = true; #endif return result; } template InjectorStorage::const_object_ptr_t InjectorStorage::createInjectedObjectForCompressedConstructor(InjectorStorage& injector, Graph::node_iterator node_itr) { C* cPtr = InvokeConstructorWithInjectedArgVector()(injector, injector.bindings, injector.allocator, node_itr.neighborsBegin()); node_itr.setTerminal(); I* iPtr = static_cast(cPtr); return reinterpret_cast(iPtr); } template inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForCompressedConstructor() { using AnnotatedC = SignatureType; using C = RemoveAnnotations; using I = RemoveAnnotations; ComponentStorageEntry result; result.kind = ComponentStorageEntry::Kind::COMPRESSED_BINDING; result.type_id = getTypeId(); ComponentStorageEntry::CompressedBinding& binding = result.compressed_binding; binding.c_type_id = getTypeId(); binding.create = createInjectedObjectForCompressedConstructor; return result; } template inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForMultibindingVectorCreator() { ComponentStorageEntry result; result.kind = ComponentStorageEntry::Kind::MULTIBINDING_VECTOR_CREATOR; result.type_id = getTypeId(); ComponentStorageEntry::MultibindingVectorCreator& binding = result.multibinding_vector_creator; binding.get_multibindings_vector = createMultibindingVector; return result; } template InjectorStorage::object_ptr_t InjectorStorage::createInjectedObjectForMultibinding(InjectorStorage& m) { C* cPtr = m.get(); // This step is needed when the cast C->I changes the pointer // (e.g. for multiple inheritance). I* iPtr = static_cast(cPtr); return reinterpret_cast(iPtr); } template inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForMultibinding() { using AnnotatedCPtr = fruit::impl::meta::UnwrapType< fruit::impl::meta::Eval)>>; using I = RemoveAnnotations; using C = RemoveAnnotations; ComponentStorageEntry result; result.kind = ComponentStorageEntry::Kind::MULTIBINDING_FOR_OBJECT_TO_CONSTRUCT_THAT_NEEDS_NO_ALLOCATION; result.type_id = getTypeId(); ComponentStorageEntry::MultibindingForObjectToConstruct& binding = result.multibinding_for_object_to_construct; binding.create = createInjectedObjectForMultibinding; binding.deps = getBindingDeps>>(); return result; } template inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForInstanceMultibinding(C& instance) { ComponentStorageEntry result; result.kind = ComponentStorageEntry::Kind::MULTIBINDING_FOR_CONSTRUCTED_OBJECT; result.type_id = getTypeId(); ComponentStorageEntry::MultibindingForConstructedObject& binding = result.multibinding_for_constructed_object; binding.object_ptr = &instance; return result; } template InjectorStorage::object_ptr_t InjectorStorage::createInjectedObjectForMultibindingProvider(InjectorStorage& injector) { C* cPtr = InvokeLambdaWithInjectedArgVector::value>()( injector, injector.allocator); return reinterpret_cast(cPtr); } template inline ComponentStorageEntry InjectorStorage::createComponentStorageEntryForMultibindingProvider() { #if FRUIT_EXTRA_DEBUG using Signature = fruit::impl::meta::UnwrapType)>>; FruitStaticAssert(fruit::impl::meta::IsSame(fruit::impl::meta::Type, fruit::impl::meta::FunctionSignature(fruit::impl::meta::Type))); #endif using AnnotatedT = SignatureType; using AnnotatedC = NormalizeType; using T = RemoveAnnotations; using C = NormalizeType; ComponentStorageEntry result; bool needs_allocation = !std::is_pointer::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(); ComponentStorageEntry::MultibindingForObjectToConstruct& binding = result.multibinding_for_object_to_construct; binding.create = createInjectedObjectForMultibindingProvider; binding.deps = getBindingDeps>(); return result; } } // namespace fruit } // namespace impl #endif // FRUIT_INJECTOR_STORAGE_DEFN_H