/* * 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_META_COMPONENT_H #define FRUIT_META_COMPONENT_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace fruit { namespace impl { namespace meta { //******************************************************************************************************************************** // Part 1: Simple type functors (no ConsComp involved). //******************************************************************************************************************************** // Given a type T, returns the class that should be injected to ensure that T is provided at runtime (if any). struct GetClassForType { // General case, if none of the following apply. // When adding a specialization here, make sure that the ComponentStorage // can actually get<> the specified type when the class was registered. template struct apply; template struct apply> { using type = Type; }; template struct apply> { using type = Type; }; template struct apply> { using type = Type; }; template struct apply> { using type = Type; }; template struct apply> { using type = Type; }; template struct apply> { using type = Type; }; template struct apply>> { using type = Type; }; template struct apply>> { using type = None; }; template struct apply>> { using type = Type; }; template struct apply>> { using type = Type; }; template struct apply>> { using type = Type; }; }; struct GetClassForTypeVector { template struct apply { using type = TransformVector(V, GetClassForType); }; }; // Given a type T, returns the type in the injection graph that corresponds to T. struct NormalizeType { // When adding a specialization here, make sure that the ComponentStorage // can actually get<> the specified type when the class was registered. template struct apply; template struct apply> { using type = Type; }; template struct apply> { using type = Type; }; template struct apply> { using type = Type; }; template struct apply> { using type = Type; }; template struct apply> { using type = Type; }; template struct apply> { using type = Type; }; template struct apply>> { using type = Type; }; template struct apply>> { using type = None; }; template struct apply>> { using type = Type; }; template struct apply>> { using type = Type; }; template struct apply>> { using type = Type)>>>>; }; }; struct NormalizeUntilStable { template struct apply { using type = If(IsSame(NormalizeType(T), T), T, NormalizeUntilStable(NormalizeType(T))); }; }; struct NormalizeTypeVector { template struct apply { using type = TransformVector(V, NormalizeType); }; }; struct TypeInjectionRequiresNonConstBinding { template struct apply; template struct apply> { using type = Bool; }; template struct apply> { using type = Bool; }; template struct apply> { using type = Bool; }; template struct apply> { using type = Bool; }; template struct apply> { using type = Bool; }; template struct apply> { using type = Bool; }; template struct apply>> { using type = Bool; }; template struct apply>> { using type = Bool; }; template struct apply>> { using type = Bool; }; template struct apply>> { using type = Bool; }; template struct apply>> { using type = TypeInjectionRequiresNonConstBinding(Type); }; }; // Returns U wrapped in the same annotations in AnnotatedT (if any). struct CopyAnnotation { template struct apply; template struct apply { using type = U; }; template struct apply>, Type> { using type = Type>; }; }; struct IsValidSignature { template struct apply { using type = Bool; }; template struct apply> { using type = Bool; }; }; // Removes the Annotation (if any) wrapping a type T. struct RemoveAnnotations { template struct apply; template struct apply> { using type = Type; }; template struct apply>> { using type = Type; }; }; // Removes the Annotation(s) (if any) wrapping the types in AnnotatedSignature. struct RemoveAnnotationsFromSignature { template struct apply { using type = ConstructError(NotASignatureErrorTag, AnnotatedSignature); }; template struct apply> { using type = ConsSignature(RemoveAnnotations(Type), Id)>...); }; }; // Removes the Annotation(s) (if any) wrapping the types in the Vector V. struct RemoveAnnotationsFromVector { template struct apply { using type = TransformVector(V, RemoveAnnotations); }; }; // Maps T->T* in a possibly-annotated type. struct AddPointerInAnnotatedType { template struct apply; template struct apply> { using type = Type; }; template struct apply>> { using type = Type>; }; }; // TODO: This also does UnlabelAssisted<>. Consider renaming and/or removing that logic (and // letting callers do the unlabeling when desired). struct RemoveNonAssisted { template struct apply { struct Helper { // Non-assisted case template struct apply { using type = CurrentResult; }; template struct apply>> { using type = PushBack(CurrentResult, Type); }; }; using type = FoldVector(V, Helper, Vector<>); }; }; struct RemoveAssisted { template struct apply { struct Helper { // Non-assisted case template struct apply { using type = PushBack(CurrentResult, T); }; // Assisted case template struct apply>> { using type = CurrentResult; }; }; using type = FoldVector(V, Helper, Vector<>); }; }; struct UnlabelAssistedSingleType { template struct apply; template struct apply> { using type = Type; }; template struct apply>> { using type = Type; }; }; struct UnlabelAssisted { template struct apply { using type = TransformVector(V, UnlabelAssistedSingleType); }; }; struct RequiredLambdaArgsForAssistedFactory { template struct apply { using type = RemoveAnnotationsFromVector(UnlabelAssisted(SignatureArgs(AnnotatedSignature))); }; }; struct RequiredLambdaSignatureForAssistedFactory { template struct apply { using type = ConsSignatureWithVector(RemoveAnnotations(SignatureType(AnnotatedSignature)), RequiredLambdaArgsForAssistedFactory(AnnotatedSignature)); }; }; struct InjectedFunctionArgsForAssistedFactory { template struct apply { using type = RemoveNonAssisted(SignatureArgs(AnnotatedSignature)); }; }; struct InjectedSignatureForAssistedFactory { template struct apply { using type = ConsSignatureWithVector(RemoveAnnotations(SignatureType(AnnotatedSignature)), InjectedFunctionArgsForAssistedFactory(AnnotatedSignature)); }; }; struct IsAssisted { template struct apply { using type = Bool; }; template struct apply>> { using type = Bool; }; }; struct NumAssisted { template struct apply; template struct apply> { using type = SumAll(typename IsAssisted::apply::type...); }; }; // Counts the number of Assisted<> types in V before the given index. struct NumAssistedBefore { template struct apply; template struct apply, V> { using type = Int<0>; }; template struct apply, V> { using N = Int; using type = Minus(NumAssisted(V), NumAssisted(VectorRemoveFirstN(V, N))); }; }; // Checks whether C is auto-injectable thanks to an Inject typedef. struct HasInjectAnnotation { template struct apply; template struct apply> { template static Bool test(typename C1::Inject*); template static Bool test(...); using type = decltype(test(nullptr)); }; }; struct DoGetInjectAnnotation { template struct apply; template struct apply> { using type = Type; }; }; struct GetInjectAnnotation { template struct apply { using C = RemoveAnnotations(AnnotatedC); using DecoratedS = DoGetInjectAnnotation(C); using SResult = SignatureType(DecoratedS); using AnnotatedSArgs = SignatureArgs(DecoratedS); using SArgs = RemoveAnnotationsFromVector(UnlabelAssisted(AnnotatedSArgs)); // We replace the non-annotated return type with the potentially-annotated AnnotatedC. using AnnotatedDecoratedS = ConsSignatureWithVector(AnnotatedC, AnnotatedSArgs); using type = If(IsAbstract(C), ConstructError(CannotConstructAbstractClassErrorTag, C), If(Not(IsValidSignature(DecoratedS)), ConstructError(InjectTypedefNotASignatureErrorTag, C, DecoratedS), If(Not(IsSame(SResult, RemoveAnnotations(SResult))), ConstructError(InjectTypedefWithAnnotationErrorTag, C), If(Not(IsSame(C, SResult)), ConstructError(InjectTypedefForWrongClassErrorTag, C, SResult), If(Not(IsConstructibleWithVector(C, SArgs)), ConstructError(NoConstructorMatchingInjectSignatureErrorTag, C, ConsSignatureWithVector(SResult, SArgs)), AnnotatedDecoratedS))))); }; }; //******************************************************************************************************************************** // Part 2: Type functors involving at least one ConsComp. //******************************************************************************************************************************** template struct Comp { // The actual set of requirements is SetDifference(RsSuperset, Ps) // We don't store Rs explicitly because we'd need to remove elements very often (and that's slow). using RsSuperset = RsSupersetParam; using Ps = PsParam; // This is a set of normalized types. // - If a type is in SetDifference(RsSuperset, Ps) and not here: it's required as const only // - If a type is in SetDifference(RsSuperset, Ps) and also here: it's required as non-const // - If a type is in Ps and not here: it's provided as const only // - If a type is in Ps and also here: it's provided as non-const using NonConstRsPs = NonConstRsPsParam; #if !FRUIT_NO_LOOP_CHECK using Deps = DepsParam; #endif using InterfaceBindings = InterfaceBindingsParam; using DeferredBindingFunctors = DeferredBindingFunctorsParam; // Invariants: // * all types appearing as arguments of Deps are in Rs // * all types in Ps are at the head of one (and only one) Dep. // (note that the types in Rs can appear in deps any number of times, 0 is also ok) // * Deps is of the form Vector with each Dep of the form T(Args...) and where Vector is a set (no // repetitions). // * Bindings is a proof tree forest, with injected classes as formulas. // * Each element X of the list DeferredBindingFunctors has: // - a default-constructible X::apply type // - a void X::apply::operator(ComponentStorage&) // - an X::apply::Result type // * Each element of NonConstRsPs is in RsSuperset or in Ps (or both) }; // Using ConsComp instead of Comp<...> in a meta-expression allows the types to be evaluated. // See ConsVector for more details. struct ConsComp { template struct apply { using type = Comp; }; }; struct GetComponentDeps { template struct apply { using type = typename Comp::Deps; }; }; struct GetComponentPs { template struct apply { using type = typename Comp::Ps; }; }; struct GetComponentRsSuperset { template struct apply { using type = typename Comp::RsSuperset; }; }; struct GetComponentNonConstRsPs { template struct apply { using type = typename Comp::NonConstRsPs; }; }; struct IsInjectableBareType { template struct apply; template struct apply> { using type = Bool::value || std::is_class::value || std::is_enum::value>; }; template struct apply>> { using type = Bool; }; template struct apply>> { using type = Bool; }; }; // Checks if T is a (non-annotated) injectable type. struct IsInjectableType { template struct apply { using type = IsInjectableBareType(NormalizeType(T)); }; }; // Checks that T is a (non-annotated) injectable type. If it isn't this returns an error, otherwise it returns None. struct CheckInjectableType { template struct apply { using type = If(Not(IsInjectableType(T)), ConstructError(NonInjectableTypeErrorTag, T), None); }; }; // Checks that Types... are (non-annotated) injectable types. If they have an annotation or they are not injectable it // an appropriate error is returned. // Otherwise this returns None. struct CheckInjectableTypeVector { struct Helper { template struct apply { using type = PropagateError(CheckInjectableType(T), CurrentResult); }; }; template struct apply { using type = FoldVector(V, Helper, None); }; }; // Checks that Types... are normalized and injectable types. If not it returns an appropriate error. // If they are all normalized types this returns Result. struct CheckNormalizedTypes { template struct apply; template struct apply...>> { struct Helper { template struct apply { using NormalizedType = NormalizeType(T); using type = PropagateError(CheckInjectableType(RemoveAnnotations(NormalizeUntilStable(T))), If(Not(IsSame(NormalizeType(T), T)), ConstructError(NonClassTypeErrorTag, RemoveAnnotations(T), RemoveAnnotations(NormalizeUntilStable(T))), CurrentResult)); }; }; using type = Fold(Helper, None, Type...); }; }; // Checks that Types... are not annotated types. If they have an annotation it returns an appropriate error. // If none of them is annotated, this returns None. struct CheckNotAnnotatedTypes { template struct apply; template struct apply...>> { struct Helper { template struct apply { using TypeWithoutAnnotations = RemoveAnnotations(T); using type = If(Not(IsSame(TypeWithoutAnnotations, T)), ConstructError(AnnotatedTypeErrorTag, T, TypeWithoutAnnotations), CurrentResult); }; }; using type = Fold(Helper, None, Type...); }; }; // Check that there are no fruit::Required<> types in Component/NormalizedComponent's arguments. // If there aren't any, this returns None. struct CheckNoRequiredTypesInComponentArguments { template struct apply; template struct apply> { using type = None; }; template struct apply, OtherTypes...>> { using type = CheckNoRequiredTypesInComponentArguments(Vector); }; template struct apply>, OtherTypes...>> { using type = ConstructError(RequiredTypesInComponentArgumentsErrorTag, Type>); }; }; // Check that there are no fruit::Required<> types in Injector's arguments. // If there aren't any, this returns None. struct CheckNoRequiredTypesInInjectorArguments { template struct apply { using type = None; }; template struct apply { using type = CheckNoRequiredTypesInInjectorArguments(Types...); }; template struct apply>, Types...> { using type = ConstructError(InjectorWithRequirementsErrorTag, Type...); }; }; // Checks that there are no repetitions in Types. If there are, it returns an appropriate error. // If there are no repetitions it returns None. struct CheckNoRepeatedTypes { template struct apply; template struct apply> { using type = If(HasDuplicates(Vector), ConstructError(RepeatedTypesErrorTag, Types...), None); }; }; struct RemoveConstFromType { template struct apply; template struct apply> { using type = Type; }; template struct apply> { using type = Type; }; template struct apply>> { using type = Type>; }; template struct apply>> { using type = Type>; }; }; struct RemoveConstFromTypes { template struct apply; template struct apply> { using type = ConsVector(Id...); }; }; struct RemoveConstTypes { struct Helper { template struct apply; template struct apply, Type> { using type = Vector; }; template struct apply, Type> { using type = Vector>; }; template struct apply, Type>> { using type = Vector; }; template struct apply, Type>> { using type = Vector>>; }; }; template struct apply { using type = FoldVector(V, Helper, Vector<>); }; }; // From a vector of injected types, this filters out the types that only require const bindings and then normalizes // the types in the result. struct NormalizedNonConstTypesIn { struct Helper { template struct apply { using type = If(TypeInjectionRequiresNonConstBinding(T), PushBack(Acc, NormalizeType(T)), Acc); }; }; template struct apply { using type = FoldVector(V, Helper, Vector<>); }; }; struct ConstructComponentImpl { // Non-specialized case: no requirements. template struct apply { using type = PropagateError( CheckNoRepeatedTypes(RemoveConstFromTypes(Vector)), PropagateError(CheckNormalizedTypes(RemoveConstFromTypes(Vector)), PropagateError(CheckNoRequiredTypesInComponentArguments(Vector), ConsComp(EmptySet, VectorToSetUnchecked(RemoveConstFromTypes(Vector)), RemoveConstTypes(Vector), #if !FRUIT_NO_LOOP_CHECK Vector>...>, #endif Vector<>, EmptyList)))); }; // With requirements. template struct apply>, Ps...> { using type1 = PropagateError( CheckNoRepeatedTypes(RemoveConstFromTypes(Vector..., Ps...>)), PropagateError(CheckNormalizedTypes(RemoveConstFromTypes(Vector..., Ps...>)), PropagateError(CheckNoRequiredTypesInComponentArguments(Vector), ConsComp(VectorToSetUnchecked(RemoveConstFromTypes(Vector...>)), VectorToSetUnchecked(RemoveConstFromTypes(Vector)), RemoveConstTypes(Vector..., Ps...>), #if !FRUIT_NO_LOOP_CHECK Vector...>>...>, #endif Vector<>, EmptyList)))); #if !FRUIT_NO_LOOP_CHECK && FRUIT_EXTRA_DEBUG using Loop = ProofForestFindLoop(GetComponentDeps(type1)); using type = If(IsNone(Loop), type1, ConstructErrorWithArgVector(SelfLoopErrorTag, Loop)); #else // FRUIT_NO_LOOP_CHECK || !FRUIT_EXTRA_DEBUG using type = type1; #endif // FRUIT_NO_LOOP_CHECK || !FRUIT_EXTRA_DEBUG }; }; struct CheckTypesNotProvidedAsConst { template struct apply { struct Helper { template struct apply { using type = If(And(IsInSet(T, typename Comp::Ps), Not(IsInSet(T, typename Comp::NonConstRsPs))), ConstructError(NonConstBindingRequiredButConstBindingProvidedErrorTag, T), Acc); }; }; using type = FoldVector(V, Helper, None); }; }; // Adds the types in NewRequirementsVector to the requirements (unless they are already provided/required). // The caller must convert the types to the corresponding class type and expand any Provider<>s. struct AddRequirements { template struct apply { using Comp1 = ConsComp(FoldVector(NewRequirementsVector, AddToSet, typename Comp::RsSuperset), typename Comp::Ps, FoldVector(NewNonConstRequirementsVector, AddToSet, typename Comp::NonConstRsPs), #if !FRUIT_NO_LOOP_CHECK typename Comp::Deps, #endif typename Comp::InterfaceBindings, typename Comp::DeferredBindingFunctors); using type = PropagateError(CheckTypesNotProvidedAsConst(Comp, NewNonConstRequirementsVector), Comp1); }; }; // Similar to AddProvidedType, but doesn't report an error if a Bind was present. struct AddProvidedTypeIgnoringInterfaceBindings { template struct apply { using Comp1 = ConsComp( FoldVector(CRequirements, AddToSet, typename Comp::RsSuperset), AddToSetUnchecked(typename Comp::Ps, C), If(IsNonConst, AddToSetUnchecked(FoldVector(CNonConstRequirements, AddToSet, typename Comp::NonConstRsPs), C), FoldVector(CNonConstRequirements, AddToSet, typename Comp::NonConstRsPs)), #if !FRUIT_NO_LOOP_CHECK PushFront(typename Comp::Deps, Pair), #endif typename Comp::InterfaceBindings, typename Comp::DeferredBindingFunctors); using type = If(IsInSet(C, typename Comp::Ps), ConstructError(TypeAlreadyBoundErrorTag, C), PropagateError(CheckTypesNotProvidedAsConst(Comp, CNonConstRequirements), Comp1)); }; }; // Adds C to the provides and removes it from the requirements (if it was there at all). // Also checks that it wasn't already provided. // Moreover, adds the requirements of C to the requirements, unless they were already provided/required. // The caller must convert the types to the corresponding class type and expand any Provider<>s. struct AddProvidedType { template struct apply { using type = If(Not(IsNone(FindInMap(typename Comp::InterfaceBindings, C))), ConstructError(TypeAlreadyBoundErrorTag, C), AddProvidedTypeIgnoringInterfaceBindings(Comp, C, IsNonConst, CRequirements, CNonConstRequirements)); }; }; struct AddDeferredBinding { template struct apply { using new_DeferredBindingFunctors = Cons; using type = ConsComp(typename Comp::RsSuperset, typename Comp::Ps, typename Comp::NonConstRsPs, #if !FRUIT_NO_LOOP_CHECK typename Comp::Deps, #endif typename Comp::InterfaceBindings, new_DeferredBindingFunctors); }; }; struct CheckNoLoopInDeps { template struct apply { using Loop = ProofForestFindLoop(typename Comp::Deps); using type = If(IsNone(Loop), Bool, ConstructErrorWithArgVector(SelfLoopErrorTag, Loop)); }; }; #if FRUIT_EXTRA_DEBUG || FRUIT_IN_META_TEST struct CheckComponentEntails { template struct apply { using CompRs = SetDifference(typename Comp::RsSuperset, typename Comp::Ps); using EntailedCompRs = SetDifference(typename EntailedComp::RsSuperset, typename EntailedComp::Ps); using CommonRs = SetIntersection(CompRs, EntailedCompRs); using CommonPs = SetIntersection(typename Comp::Ps, typename EntailedComp::Ps); using type = If(Not(IsContained(typename EntailedComp::Ps, typename Comp::Ps)), ConstructErrorWithArgVector(ComponentDoesNotEntailDueToProvidesErrorTag, SetToVector(SetDifference(typename EntailedComp::Ps, typename Comp::Ps))), If(Not(IsVectorContained(typename EntailedComp::InterfaceBindings, typename Comp::InterfaceBindings)), ConstructErrorWithArgVector(ComponentDoesNotEntailDueToInterfaceBindingsErrorTag, SetToVector(SetDifference(typename EntailedComp::InterfaceBindings, typename Comp::InterfaceBindings))), If(Not(IsContained(CompRs, EntailedCompRs)), ConstructErrorWithArgVector(ComponentDoesNotEntailDueToRequirementsErrorTag, SetToVector(SetDifference(CompRs, EntailedCompRs))), If(Not(IsContained(SetIntersection(CommonRs, typename Comp::NonConstRsPs), typename EntailedComp::NonConstRsPs)), ConstructErrorWithArgVector(ComponentDoesNotEntailDueToDifferentConstnessOfRequirementsErrorTag, SetToVector(SetDifference(SetIntersection(CommonRs, typename Comp::NonConstRsPs), typename EntailedComp::NonConstRsPs))), If(Not(IsContained(SetIntersection(CommonPs, typename EntailedComp::NonConstRsPs), typename Comp::NonConstRsPs)), ConstructErrorWithArgVector( ComponentDoesNotEntailDueToDifferentConstnessOfProvidesErrorTag, SetToVector(SetDifference(SetIntersection(CommonPs, typename EntailedComp::NonConstRsPs), typename Comp::NonConstRsPs))), Bool))))); static_assert(true || sizeof(typename CheckIfError>::type), ""); }; }; #endif // FRUIT_EXTRA_DEBUG || FRUIT_IN_META_TEST // This calls ConstructError(NoBindingFoundErrorTag, ...) or // ConstructError(NoBindingFoundForAbstractClassErrorTag, ...) as appropriate. // Call this when we're unable to auto-inject a type AnnotatedC and we're giving up. struct ConstructNoBindingFoundError { template struct apply { using type = If(IsAbstract(RemoveAnnotations(AnnotatedC)), ConstructError(NoBindingFoundForAbstractClassErrorTag, AnnotatedC, RemoveAnnotations(AnnotatedC)), ConstructError(NoBindingFoundErrorTag, AnnotatedC)); }; }; } // namespace meta } // namespace impl } // namespace fruit #endif // FRUIT_META_COMPONENT_H