aboutsummaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorClayton Lemons <MrElusive@gmail.com>2017-10-25 16:29:40 -0500
committerClayton Lemons <MrElusive@gmail.com>2017-10-25 16:29:40 -0500
commit7cdc5104a4413de15bf1b4e5045f0eb622b1e4ff (patch)
tree0fdd312c36ccd3a00d3a5a411aa160a659e35e98 /include
parentec296992a2f5b1bb43ee6749daf35c4284632996 (diff)
downloadgoogle-fruit-7cdc5104a4413de15bf1b4e5045f0eb622b1e4ff.tar.gz
Added support for MSVC 14 (VS2015)
The primary modes of failure (both compilation and runtime) for MSVC 14 were the following: 1. MSVC 14 is not able to properly parse alias template specializations when expanded parameter pack elements are used as a template argument. 2. MSVC 14 has trouble in some instances recognizing that a specialized alias template is a type. 3. Assuming struct Y inherits from struct X and y is an instance of Y, the pointer to y that is used for binding can get mangled when creating a Component. For example, fruit::createComponent().bindInstance<Y, Y>(y).bind<X, Y>() will mangle the pointer to y such that fruit::Injector::get() returns an invalid reference, causing a crash. I'm not exactly sure what the compiler is doing, but this happens when the PartialComponent returned by the chained binds calls is copied when it is passed by value to the Component constructor. Note, this does not happen if the bind calls in the above example are swapped: fruit::createComponent().bind<X, Y>().bindInstance<Y, Y>(y)
Diffstat (limited to 'include')
-rw-r--r--include/fruit/component.h2
-rw-r--r--include/fruit/impl/component.defn.h10
-rw-r--r--include/fruit/impl/component_functors.defn.h4
-rw-r--r--include/fruit/impl/component_storage/partial_component_storage.defn.h8
-rw-r--r--include/fruit/impl/injector.defn.h2
-rw-r--r--include/fruit/impl/injector/injector_storage.defn.h106
-rw-r--r--include/fruit/impl/injector/injector_storage.h8
-rw-r--r--include/fruit/impl/meta/basics.h9
-rw-r--r--include/fruit/impl/meta/errors.h2
9 files changed, 97 insertions, 54 deletions
diff --git a/include/fruit/component.h b/include/fruit/component.h
index d2360ab..dcb6a78 100644
--- a/include/fruit/component.h
+++ b/include/fruit/component.h
@@ -55,7 +55,7 @@ class Component {
* This is usually called implicitly when returning a component from a function. See PartialComponent for an example.
*/
template<typename... Bindings>
- Component(PartialComponent<Bindings...> component);
+ Component(PartialComponent<Bindings...>&& component);
private:
// Do not use. Use fruit::createComponent() instead.
diff --git a/include/fruit/impl/component.defn.h b/include/fruit/impl/component.defn.h
index e8d0240..86ab8cc 100644
--- a/include/fruit/impl/component.defn.h
+++ b/include/fruit/impl/component.defn.h
@@ -50,7 +50,7 @@ struct OpForComponent {
template <typename... Params>
template <typename... Bindings>
-inline Component<Params...>::Component(PartialComponent<Bindings...> component)
+inline Component<Params...>::Component(PartialComponent<Bindings...>&& partial_component)
: storage() {
(void)typename fruit::impl::meta::CheckIfError<Comp>::type();
@@ -62,12 +62,14 @@ inline Component<Params...>::Component(PartialComponent<Bindings...> component)
(void)typename fruit::impl::meta::CheckIfError<fruit::impl::meta::Eval<fruit::impl::meta::CheckNoLoopInDeps(typename Op::Result)>>::type();
#endif // !FRUIT_NO_LOOP_CHECK
- std::size_t num_entries = component.storage.numBindings() + Op().numEntries();
+ std::size_t num_entries = partial_component.storage.numBindings() + Op().numEntries();
fruit::impl::FixedSizeVector<fruit::impl::ComponentStorageEntry> entries(num_entries);
Op()(entries);
- component.storage.addBindings(entries);
+ // addBindings may modify the storage member of PartialComponent.
+ // Therefore, it should not be used after this operation.
+ partial_component.storage.addBindings(entries);
// TODO: re-enable this check somehow.
// component.component.already_converted_to_component = true;
@@ -292,7 +294,7 @@ PartialComponent<Bindings...>::install(fruit::Component<OtherComponentParams...>
template <typename... Bindings>
template <typename... OtherComponentParams, typename... FormalArgs, typename... Args>
-inline PartialComponent<Bindings...>::PartialComponentWithReplacementInProgress<fruit::Component<OtherComponentParams...>, FormalArgs...>
+inline typename PartialComponent<Bindings...>::PartialComponentWithReplacementInProgress<fruit::Component<OtherComponentParams...>, FormalArgs...>
PartialComponent<Bindings...>::replace(fruit::Component<OtherComponentParams...>(*getReplacedComponent)(FormalArgs...), Args&&... args) {
using IntCollector = int[];
(void)IntCollector{0, checkAcceptableComponentInstallArg<FormalArgs>()...};
diff --git a/include/fruit/impl/component_functors.defn.h b/include/fruit/impl/component_functors.defn.h
index 846a5a8..da651df 100644
--- a/include/fruit/impl/component_functors.defn.h
+++ b/include/fruit/impl/component_functors.defn.h
@@ -951,7 +951,7 @@ struct AutoRegisterFactoryHelper {
void operator()(FixedSizeVector<ComponentStorageEntry>& entries) {
using NakedC = UnwrapType<Eval<C>>;
auto provider = [](const UnwrapType<Eval<CFunctor>>& fun) {
- return UnwrapType<Eval<IFunctor>>([=](UnwrapType<Args>... args) {
+ return UnwrapType<Eval<IFunctor>>([=](typename TypeUnwrapper<Args>::type... args) {
NakedC* c = fun(args...).release();
NakedI* i = static_cast<NakedI*>(c);
return std::unique_ptr<NakedI>(i);
@@ -1014,7 +1014,7 @@ struct AutoRegisterFactoryHelper {
using Result = Eval<GetResult(R)>;
void operator()(FixedSizeVector<ComponentStorageEntry>& entries) {
auto provider = [](const UnwrapType<Eval<CFunctor>>& fun) {
- return UnwrapType<Eval<CUniquePtrFunctor>>([=](UnwrapType<Args>... args) {
+ return UnwrapType<Eval<CUniquePtrFunctor>>([=](typename TypeUnwrapper<Args>::type... args) {
NakedC* c = new NakedC(fun(args...));
return std::unique_ptr<NakedC>(c);
});
diff --git a/include/fruit/impl/component_storage/partial_component_storage.defn.h b/include/fruit/impl/component_storage/partial_component_storage.defn.h
index 6c9b4a0..774551e 100644
--- a/include/fruit/impl/component_storage/partial_component_storage.defn.h
+++ b/include/fruit/impl/component_storage/partial_component_storage.defn.h
@@ -328,7 +328,7 @@ public:
: previous_storage(previous_storage) {
}
- void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) {
+ void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) const {
previous_storage.addBindings(entries);
}
@@ -371,7 +371,7 @@ public:
fun(fun1) {
}
- void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) {
+ void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) const {
entries.push_back(ComponentStorageEntry::LazyComponentWithNoArgs::create(fun));
previous_storage.addBindings(entries);
}
@@ -423,7 +423,7 @@ public:
fun(fun1) {
}
- void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) {
+ void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) const {
entries.push_back(ComponentStorageEntry::LazyComponentWithNoArgs::createReplacedComponentEntry(fun));
previous_storage.addBindings(entries);
}
@@ -485,7 +485,7 @@ public:
fun(fun1) {
}
- void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) {
+ void addBindings(FixedSizeVector<ComponentStorageEntry>& entries) const {
entries.push_back(ComponentStorageEntry::LazyComponentWithNoArgs::createReplacementComponentEntry(fun));
previous_storage.addBindings(entries);
}
diff --git a/include/fruit/impl/injector.defn.h b/include/fruit/impl/injector.defn.h
index 6d9d89f..c24097d 100644
--- a/include/fruit/impl/injector.defn.h
+++ b/include/fruit/impl/injector.defn.h
@@ -120,7 +120,7 @@ inline Injector<P...>::Injector(const NormalizedComponent<NormalizedComponentPar
template <typename... P>
template <typename T>
-inline Injector<P...>::RemoveAnnotations<T> Injector<P...>::get() {
+inline typename Injector<P...>::RemoveAnnotations<T> Injector<P...>::get() {
using E = typename fruit::impl::meta::InjectorImplHelper<P...>::template CheckGet<T>::type;
(void)typename fruit::impl::meta::CheckIfError<E>::type();
diff --git a/include/fruit/impl/injector/injector_storage.defn.h b/include/fruit/impl/injector/injector_storage.defn.h
index 7de8e48..9a9626d 100644
--- a/include/fruit/impl/injector/injector_storage.defn.h
+++ b/include/fruit/impl/injector/injector_storage.defn.h
@@ -4,9 +4,9 @@
* 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.
@@ -64,7 +64,7 @@ inline TypeId InjectorStorage::BindingDataNodeIter::getId() {
FruitAssert(itr->kind != ComponentStorageEntry::Kind::REPLACEMENT_LAZY_COMPONENT_WITH_ARGS);
return itr->type_id;
}
-
+
inline NormalizedBinding InjectorStorage::BindingDataNodeIter::getValue() {
return NormalizedBinding(*itr);
}
@@ -315,30 +315,30 @@ inline std::shared_ptr<char> InjectorStorage::createMultibindingVector(InjectorS
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;
}
@@ -438,35 +438,43 @@ template <typename AnnotatedSignature, typename Lambda, typename AnnotatedT, typ
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, InjectorStorage::RemoveAnnotations<fruit::impl::meta::UnwrapType<AnnotatedArgs>>...>(
- 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;
+ 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, InjectorStorage::RemoveAnnotations<fruit::impl::meta::UnwrapType<AnnotatedArgs>>...>(
- GetSecondStage<InjectorStorage::RemoveAnnotations<fruit::impl::meta::UnwrapType<AnnotatedArgs>>>()(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
@@ -475,8 +483,8 @@ struct InvokeLambdaWithInjectedArgVector<AnnotatedSignature, Lambda, true /* lam
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;
+ // `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)
...);
@@ -487,7 +495,7 @@ struct InvokeLambdaWithInjectedArgVector<AnnotatedSignature, Lambda, true /* lam
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;
@@ -495,13 +503,13 @@ struct InvokeLambdaWithInjectedArgVector<AnnotatedSignature, Lambda, true /* lam
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;
}
};
@@ -509,14 +517,19 @@ struct InvokeLambdaWithInjectedArgVector<AnnotatedSignature, Lambda, true /* lam
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, InjectorStorage::RemoveAnnotations<fruit::impl::meta::UnwrapType<AnnotatedArgs>>&&...>(
- injector.get<fruit::impl::meta::UnwrapType<AnnotatedArgs>>()...));
+ 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
@@ -527,9 +540,20 @@ struct InvokeLambdaWithInjectedArgVector<AnnotatedSignature, Lambda, false /* la
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, InjectorStorage::RemoveAnnotations<fruit::impl::meta::UnwrapType<AnnotatedArgs>>...>(
- GetSecondStage<InjectorStorage::RemoveAnnotations<fruit::impl::meta::UnwrapType<AnnotatedArgs>>>()(getFirstStageResults)
- ...));
+ 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
@@ -550,10 +574,10 @@ struct InvokeLambdaWithInjectedArgVector<AnnotatedSignature, Lambda, false /* la
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,
@@ -652,7 +676,7 @@ struct InvokeConstructorWithInjectedArgVector<AnnotatedC(AnnotatedArgs...), frui
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, InjectorStorage::RemoveAnnotations<AnnotatedArgs>&&...>(
+ return allocator.constructObject<AnnotatedC, typename InjectorStorage::AnnotationRemover<AnnotatedArgs>::type&&...>(
GetSecondStage<InjectorStorage::RemoveAnnotations<AnnotatedArgs>>()(getFirstStageResults)
...);
}
diff --git a/include/fruit/impl/injector/injector_storage.h b/include/fruit/impl/injector/injector_storage.h
index e4b94f6..1b36f91 100644
--- a/include/fruit/impl/injector/injector_storage.h
+++ b/include/fruit/impl/injector/injector_storage.h
@@ -50,6 +50,14 @@ public:
using RemoveAnnotations = fruit::impl::meta::UnwrapType<fruit::impl::meta::Eval<
fruit::impl::meta::RemoveAnnotations(fruit::impl::meta::Type<AnnotatedT>)
>>;
+
+ // MSVC 14 has trouble specializing alias templates using expanded pack elements.
+ // This is a known issue: https://stackoverflow.com/questions/43411542/metaprogramming-failed-to-specialize-alias-template
+ // The workaround is just to use a struct directly.
+ template <typename AnnotatedT>
+ struct AnnotationRemover {
+ using type = RemoveAnnotations<AnnotatedT>;
+ };
template <typename T>
using NormalizeType = fruit::impl::meta::UnwrapType<fruit::impl::meta::Eval<
diff --git a/include/fruit/impl/meta/basics.h b/include/fruit/impl/meta/basics.h
index 10876f1..1494575 100644
--- a/include/fruit/impl/meta/basics.h
+++ b/include/fruit/impl/meta/basics.h
@@ -72,6 +72,15 @@ struct Call {
template <typename WrappedType>
using UnwrapType = typename WrappedType::type;
+// MSVC 14 has trouble specializing alias templates using expanded pack elements.
+// This is a known issue: https://stackoverflow.com/questions/43411542/metaprogramming-failed-to-specialize-alias-template
+// The workaround is just to use a struct directly.
+// typename TypeUnwrapper<Type<T>>::type is T.
+template <typename WrappedType>
+struct TypeUnwrapper {
+ using type = UnwrapType<WrappedType>;
+};
+
// Logical And with short-circuit evaluation.
struct And {
template <typename... MetaExprs>
diff --git a/include/fruit/impl/meta/errors.h b/include/fruit/impl/meta/errors.h
index 1ccbf23..57044c2 100644
--- a/include/fruit/impl/meta/errors.h
+++ b/include/fruit/impl/meta/errors.h
@@ -42,7 +42,7 @@ struct ConstructError {
#ifdef FRUIT_DEEP_TEMPLATE_INSTANTIATION_STACKTRACES_FOR_ERRORS
static_assert(true || sizeof(typename CheckIfError<Error<ErrorTag, UnwrapType<Args>...>>::type), "");
#endif
- using type = Error<ErrorTag, UnwrapType<Args>...>;
+ using type = Error<ErrorTag, typename TypeUnwrapper<Args>::type...>;
};
};