computation_graph.cpp serif/utils/dependencies/computation_cache.hpp serif/utils/dependencies/timing.hpp array concepts expected memory string utility #include"serif/utils/dependencies/computation_cache.hpp" #include"serif/utils/dependencies/timing.hpp" #include<array> #include<concepts> #include<expected> #include<memory> #include<string> #include<utility> namespace{ namespacedependencies=serif::utils::dependencies; structSource{}; structFirst{}; structSecond{}; structMissing{}; structUnused{}; structUntrackable{}; usingDependencyContext=decltype(dependencies::make_dependency_context(dependencies::own<Source>(1),dependencies::own<Unused>(Untrackable{}))); usingEmptyContext=dependencies::DependencyContext<>; usingFirstCalculation=decltype(dependencies::make_computation<First,dependencies::InputList<Source>>([](intvalue){returnvalue+1;})); usingSecondCalculation=decltype(dependencies::make_computation<Second,dependencies::InputList<First>>([](intvalue){return2*value;})); usingComputationGraph=dependencies::ComputationGraph<SecondCalculation,FirstCalculation>; static_assert(dependencies::CompatibleComputations<FirstCalculation,SecondCalculation>); static_assert(!dependencies::CompatibleComputations<FirstCalculation,FirstCalculation>); static_assert(!dependencies::CompatibleComputations<int>); static_assert(dependencies::ComputationGraphFor<ComputationGraph,DependencyContext>); static_assert(dependencies::ComputationGraphFor<constComputationGraph&,constDependencyContext&>); static_assert(!dependencies::ComputationGraphFor<ComputationGraph,EmptyContext>); static_assert(!dependencies::ComputationGraphFor<int,DependencyContext>&&!dependencies::ComputationGraphFor<ComputationGraph,int>); static_assert(dependencies::ComputationGraphFor<dependencies::ComputationGraph<>,EmptyContext>); static_assert(ComputationGraph::Dependencies::order==std::array<std::size_t,2>{1,0}); static_assert(ComputationGraph::Dependencies::required(dependencies::KeyList<Second>{})==std::array<bool,2>{true,true}); static_assert(ComputationGraph::Dependencies::required(dependencies::KeyList<First>{})==std::array<bool,2>{false,true}); static_assert(ComputationGraph::Dependencies::required(dependencies::KeyList<>{})==std::array<bool,2>{false,false}); usingCyclicFirst=decltype(dependencies::make_computation<First,dependencies::InputList<Second>>([](intvalue){returnvalue;})); usingSelfCycle=decltype(dependencies::make_computation<First,dependencies::InputList<First>>([](intvalue){returnvalue;})); static_assert(!dependencies::CompatibleComputations<CyclicFirst,SecondCalculation>); static_assert(!dependencies::CompatibleComputations<SelfCycle>); usingDuplicateProducer=decltype(dependencies::make_dependency_context(dependencies::own<Source>(1),dependencies::own<First>(2))); static_assert(!dependencies::ComputationGraphFor<ComputationGraph,DuplicateProducer>); template<typenameFunctionT,typenameInputsT=dependencies::InputList<Source>> usingSingleGraph=dependencies::ComputationGraph<dependencies::Computation<First,InputsT,FunctionT>>; usingMutableInput=decltype([](int&value){return++value;}); usingMutableCallable=decltype([value=0](int)mutable{return++value;}); usingWrongArity=decltype([](int,int){return0;}); usingWrongType=decltype([](conststd::string&){return0;}); usingVoidResult=decltype([](int){}); usingReferenceResult=decltype([](constint&value)->constint&{returnvalue;}); usingMoveOnlyResult=decltype([](intvalue){returnstd::make_unique<int>(value);}); usingVoidExpected=decltype([](int)->std::expected<void,std::string>{return{};}); usingUnary=decltype([](intvalue){returnvalue;}); static_assert(!dependencies::ComputationGraphFor<SingleGraph<MutableInput>,DependencyContext>); static_assert(!dependencies::ComputationGraphFor<SingleGraph<MutableCallable>,DependencyContext>); static_assert(!dependencies::ComputationGraphFor<SingleGraph<WrongArity>,DependencyContext>); static_assert(!dependencies::ComputationGraphFor<SingleGraph<WrongType>,DependencyContext>); static_assert(!dependencies::ComputationGraphFor<SingleGraph<VoidResult>,DependencyContext>); static_assert(!dependencies::ComputationGraphFor<SingleGraph<ReferenceResult>,DependencyContext>); static_assert(!dependencies::ComputationGraphFor<SingleGraph<VoidExpected>,DependencyContext>); static_assert(!dependencies::ComputationGraphFor<SingleGraph<Unary,dependencies::InputList<Missing>>,DependencyContext>); static_assert(dependencies::ComputationGraphFor<SingleGraph<MoveOnlyResult>,DependencyContext>); usingFallible=decltype([](intvalue)->std::expected<int,int>{returnvalue;}); usingConstFallible=decltype([](intvalue)->conststd::expected<int,int>{returnvalue;}); static_assert(dependencies::CanPrepare<ComputationGraph,DependencyContext,std::string>);//Unusedresourcesneednotrackingpolicy. static_assert(dependencies::CanPrepare<SingleGraph<Fallible>,DependencyContext,int>); static_assert(!dependencies::CanPrepare<SingleGraph<Fallible>,DependencyContext,std::string>); static_assert(dependencies::CanPrepare<SingleGraph<ConstFallible>,DependencyContext,int>); static_assert(!dependencies::CanPrepare<SingleGraph<ConstFallible>,DependencyContext,std::string>); static_assert(dependencies::CanPrepare<SingleGraph<MoveOnlyResult>,DependencyContext,std::string>); static_assert(!dependencies::CanPrepare<ComputationGraph,DependencyContext,void>); static_assert(!dependencies::CanPrepare<ComputationGraph,DependencyContext,int&>); static_assert(!dependencies::CanPrepare<int,DependencyContext,int>); usingReadUntrackable=decltype([](constUntrackable&){return1;}); usingUntrackableGraph=SingleGraph<ReadUntrackable,dependencies::InputList<Unused>>; static_assert(dependencies::ComputationGraphFor<UntrackableGraph,DependencyContext>); static_assert(!dependencies::CanPrepare<UntrackableGraph,DependencyContext,std::string>); usingComputationCache=dependencies::ComputationCache<ComputationGraph,DependencyContext>; template<typenameRequestedT> conceptCanRequest=requires(ComputationCache&cache,constDependencyContext&context,RequestedTrequest){ cache.try_prepare(context,request); }; static_assert(CanRequest<dependencies::RequestedResults<>>); static_assert(CanRequest<dependencies::RequestedResults<First>>); static_assert(CanRequest<dependencies::RequestedResults<Second, First>>); static_assert(!CanRequest<dependencies::InputList<Source>>); static_assert(!CanRequest<dependencies::InputList<Missing>>); static_assert(!CanRequest<dependencies::RequestedResults<First, First>>); static_assert(!CanRequest<int>); static_assert(!std::copy_constructible<ComputationCache>); static_assert(std::move_constructible<ComputationCache>); static_assert(std::same_as<decltype(std::declval<constComputationCache&>().get<First>()),constint&>); usingInputs=dependencies::InputList<Source>; structWrongIdentity:dependencies::ValueSnapshot{ std::stringidentity(constint&)const; }; static_assert(!dependencies::SnapshotPolicyFor<WrongIdentity,int>); template<typenameFunctionT,typenameInputsT=dependencies::InputList<Source>> conceptCanMakeComputation=requires(FunctionT&&function){ dependencies::make_computation<First,InputsT>(std::forward<FunctionT>(function)); }; template<typenameContextT,typename...Computations> conceptCanMakeCache=requires(constContextT&context,Computations&&...computations){ dependencies::make_computation_cache(context,std::forward<Computations>(computations)...); }; template<typenameErrorT,typename...Computations> conceptCanMakeFallibleCache=requires(constDependencyContext&context,Computations&&...computations){ dependencies::make_computation_cache<ErrorT>(context,std::forward<Computations>(computations)...); }; static_assert(CanMakeComputation<Unary>); static_assert(!CanMakeComputation<Unary, int>); static_assert(!CanMakeComputation<Unary, dependencies::InputList<Source, Source>>); static_assert(CanMakeCache<DependencyContext, const SecondCalculation&, const FirstCalculation&>); static_assert(CanMakeCache<EmptyContext>); static_assert(!CanMakeCache<int, FirstCalculation>); static_assert(!CanMakeCache<DependencyContext, int>); static_assert(!CanMakeCache<EmptyContext, FirstCalculation>); static_assert(!CanMakeCache<DependencyContext, FirstCalculation, FirstCalculation>); static_assert(!CanMakeCache<DependencyContext, CyclicFirst, SecondCalculation>); static_assert(!CanMakeCache<DependencyContext, SelfCycle>); static_assert(!CanMakeCache<DuplicateProducer, FirstCalculation>); static_assert(!CanMakeCache<DependencyContext, dependencies::Computation<First, Inputs, WrongType>>); static_assert(CanMakeFallibleCache<int, dependencies::Computation<First, Inputs, Fallible>>); static_assert(!CanMakeFallibleCache<std::string, dependencies::Computation<First, Inputs, Fallible>>); usingMoveOnlyCallable=decltype([value=std::make_unique<int>(2)](intinput){returninput+*value;}); usingMoveOnlyComputation=dependencies::Computation<First,Inputs,MoveOnlyCallable>; static_assert(CanMakeComputation<MoveOnlyCallable>); static_assert(!CanMakeComputation<MoveOnlyCallable&>); static_assert(CanMakeCache<DependencyContext,MoveOnlyComputation>); static_assert(!CanMakeCache<DependencyContext,MoveOnlyComputation&>); usingOwningCalculation=decltype(dependencies::make_owning_computation<Second,dependencies::InputList<First>>([](std::shared_ptr<constint>value) { returnvalue; })); usingOwningGraph=dependencies::ComputationGraph<OwningCalculation,FirstCalculation>; static_assert(dependencies::CanPrepare<OwningGraph,DependencyContext,std::string>); static_assert(CanMakeCache<DependencyContext,OwningCalculation,FirstCalculation>); static_assert(!dependencies::CompatibleComputations<OwningCalculation>); static_assert(!CanMakeCache<DependencyContext,OwningCalculation>); static_assert(!CanMakeCache<DuplicateProducer,OwningCalculation>); usingOwnsSource=decltype(dependencies::make_owning_computation<Second,dependencies::InputList<Source>>([](std::shared_ptr<constint>value) { returnvalue; })); static_assert(!CanMakeCache<DependencyContext,OwnsSource,FirstCalculation>); usingMutableOwnedInput=decltype(dependencies::make_owning_computation<Second,dependencies::InputList<First>>([](std::shared_ptr<int>value) { return*value; })); usingMutableOwnedGraph=dependencies::ComputationGraph<FirstCalculation,MutableOwnedInput>; static_assert(!dependencies::CanPrepare<MutableOwnedGraph,DependencyContext,std::string>); usingEmptyOwningCalculation=decltype(dependencies::make_owning_computation<First,dependencies::InputList<>>([]() { return3; })); static_assert(CanMakeCache<EmptyContext,EmptyOwningCalculation>); } namespace{ template<std::size_tIndex> structChainKey{}; template<std::size_tIndex> structReverseChainNode{ usingKey=ChainKey<Index>; usingInputs=dependencies::KeyList<ChainKey<Index+1>>; }; template<std::size_t...Indices> [[nodiscard]]constevalautoreverse_chain(std::index_sequence<Indices...>){ returndependencies::detail::Graph<ReverseChainNode<Indices>...>{}; } usingLongChain=decltype(reverse_chain(std::make_index_sequence<144>{})); [[nodiscard]]constevalboollong_chain_is_ordered(){ for(std::size_tposition=0;position<LongChain::size;++position){ if(LongChain::order[position]!=LongChain::size-position-1){ returnfalse; } } returntrue; } template<std::size_tIndex,std::size_t...Prerequisites> structOrderingNode{ usingKey=ChainKey<Index>; usingInputs=dependencies::KeyList<ChainKey<Prerequisites>...>; }; //Nodes1and2becomereadytogether.After1,newlyreadynode0mustprecedealready-readynode2. usingStableDiamond=dependencies::detail::Graph<OrderingNode<0,1>,OrderingNode<1,3>,OrderingNode<2,3>, OrderingNode<3>,OrderingNode<4,0,2>>; usingPartialCycle=dependencies::detail::Graph<OrderingNode<0,1>,OrderingNode<1,0>,OrderingNode<2>>; static_assert(LongChain::acyclic()); static_assert(long_chain_is_ordered()); static_assert(StableDiamond::order==std::array<std::size_t,5>{3,1,0,2,4}); static_assert(StableDiamond::acyclic()); static_assert(PartialCycle::order==std::array<std::size_t,3>{2,3,3}); static_assert(!PartialCycle::acyclic()); } namespace{ usingNodeOutcome=dependencies::NodeOutcome; usingObserverBefore=decltype([]{returnstd::make_unique<int>(0);}); usingObserverAfter=decltype([](std::unique_ptr<int>&,NodeOutcome)noexcept{}); template<typenameBeforeT,typenameAfterT> conceptCanObserveNode=requires(BeforeT&&before,AfterT&&after){ dependencies::observe_node<First>(std::forward<BeforeT>(before),std::forward<AfterT>(after)); }; usingVoidBefore=decltype([]{}); usingReferenceBefore=decltype([]()->int&{staticintstate=0;returnstate;}); usingWrongStateAfter=decltype([](int&,NodeOutcome)noexcept{}); usingThrowingAfter=decltype([](std::unique_ptr<int>&,NodeOutcome){}); usingNonvoidAfter=decltype([](std::unique_ptr<int>&,NodeOutcome)noexcept{return1;}); static_assert(CanObserveNode<ObserverBefore,ObserverAfter>); static_assert(!CanObserveNode<VoidBefore,ObserverAfter>); static_assert(!CanObserveNode<ReferenceBefore,WrongStateAfter>); static_assert(!CanObserveNode<ObserverBefore,WrongStateAfter>); static_assert(!CanObserveNode<ObserverBefore,ThrowingAfter>); static_assert(!CanObserveNode<ObserverBefore,NonvoidAfter>); usingNodeObserver=decltype(dependencies::observe_node<First>(ObserverBefore{},ObserverAfter{})); usingSourceObserver=decltype(dependencies::observe_node<Source>(ObserverBefore{},ObserverAfter{})); usingMissingObserver=decltype(dependencies::observe_node<Missing>(ObserverBefore{},ObserverAfter{})); usingInputObserver=decltype([](constint&){}); usingSourceEdge=decltype(dependencies::observe_edge<Source,First>(InputObserver{})); usingInternalEdge=decltype(dependencies::observe_edge<First,Second>(InputObserver{})); usingMissingEdge=decltype(dependencies::observe_edge<Missing,First>(InputObserver{})); usingReversedEdge=decltype(dependencies::observe_edge<Second,First>(InputObserver{})); usingIndirectEdge=decltype(dependencies::observe_edge<Source,Second>(InputObserver{})); usingMissingConsumer=decltype(dependencies::observe_edge<Source,Missing>(InputObserver{})); usingMutableEdge=decltype(dependencies::observe_edge<Source,First>([](int&){})); usingWrongTypeEdge=decltype(dependencies::observe_edge<Source,First>([](conststd::string&){})); usingNonvoidEdge=decltype(dependencies::observe_edge<Source,First>([](constint&){return1;})); template<typename...Observers> usingObserverSet=decltype(dependencies::make_observers(std::declval<Observers>()...)); static_assert(dependencies::ObserversFor<ObserverSet<>,ComputationGraph,DependencyContext>); static_assert(dependencies::ObserversFor<ObserverSet<NodeObserver,NodeObserver,SourceEdge,InternalEdge>,ComputationGraph,DependencyContext>); static_assert(dependencies::ObserversFor<ObserverSet<InternalEdge>,OwningGraph,DependencyContext>); static_assert(!dependencies::ObserversFor<ObserverSet<SourceObserver>,ComputationGraph,DependencyContext>); static_assert(!dependencies::ObserversFor<ObserverSet<MissingObserver>,ComputationGraph,DependencyContext>); static_assert(!dependencies::ObserversFor<ObserverSet<MissingEdge>,ComputationGraph,DependencyContext>); static_assert(!dependencies::ObserversFor<ObserverSet<ReversedEdge>,ComputationGraph,DependencyContext>); static_assert(!dependencies::ObserversFor<ObserverSet<IndirectEdge>,ComputationGraph,DependencyContext>); static_assert(!dependencies::ObserversFor<ObserverSet<MissingConsumer>,ComputationGraph,DependencyContext>); static_assert(!dependencies::ObserversFor<ObserverSet<MutableEdge>,ComputationGraph,DependencyContext>); static_assert(!dependencies::ObserversFor<ObserverSet<WrongTypeEdge>,ComputationGraph,DependencyContext>); static_assert(!dependencies::ObserversFor<ObserverSet<NonvoidEdge>,ComputationGraph,DependencyContext>); static_assert(!dependencies::ObserversFor<int,ComputationGraph,DependencyContext>); static_assert(!dependencies::ObserversFor<ObserverSet<>,int,DependencyContext>); static_assert(!dependencies::ObserversFor<ObserverSet<>,ComputationGraph,int>); template<typenameObserversT> conceptCanMakeObservedCache=requires(constComputationGraph&graph,constDependencyContext&context,ObserversT&&observers){ dependencies::make_computation_cache(graph,context,std::forward<ObserversT>(observers)); }; static_assert(CanMakeObservedCache<ObserverSet<NodeObserver, SourceEdge>>); static_assert(!CanMakeObservedCache<ObserverSet<MissingObserver>>); static_assert(!CanMakeObservedCache<ObserverSet<MutableEdge>>); usingMoveOnlyBefore=decltype([state=std::make_unique<int>(0)]{return++*state;}); usingCountAfter=decltype([](int&,NodeOutcome)noexcept{}); usingMoveOnlyObserver=decltype(dependencies::observe_node<First>(std::declval<MoveOnlyBefore>(),CountAfter{})); static_assert(CanObserveNode<MoveOnlyBefore,CountAfter>); static_assert(!CanObserveNode<MoveOnlyBefore&,CountAfter>); static_assert(CanMakeObservedCache<ObserverSet<MoveOnlyObserver>>); static_assert(!CanMakeObservedCache<ObserverSet<MoveOnlyObserver>&>); } namespace{ usingAllBefore=decltype([]<typenameKey>(std::type_identity<Key>){return0;}); usingAllAfter=decltype([](int&,NodeOutcome)noexcept{}); usingAllObserver=decltype(dependencies::observe_nodes(AllBefore{},AllAfter{})); usingSomeBefore=decltype([](std::type_identity<First>){return0;}); usingSomeObserver=decltype(dependencies::observe_nodes(SomeBefore{},AllAfter{})); usingThrowingAllAfter=decltype([](int&,NodeOutcome){}); usingUnsafeAllObserver=decltype(dependencies::observe_nodes(AllBefore{},ThrowingAllAfter{})); usingReferenceAllBefore=decltype([]<typenameKey>(std::type_identity<Key>)->int&{staticintstate=0;returnstate;}); usingReferenceAllObserver=decltype(dependencies::observe_nodes(ReferenceAllBefore{},AllAfter{})); static_assert(dependencies::ObserversFor<ObserverSet<AllObserver>,ComputationGraph,DependencyContext>); static_assert(dependencies::ObserversFor<ObserverSet<AllObserver,NodeObserver,SourceEdge>,ComputationGraph,DependencyContext>); static_assert(!dependencies::ObserversFor<ObserverSet<SomeObserver>,ComputationGraph,DependencyContext>); static_assert(!dependencies::ObserversFor<ObserverSet<UnsafeAllObserver>,ComputationGraph,DependencyContext>); static_assert(!dependencies::ObserversFor<ObserverSet<ReferenceAllObserver>,ComputationGraph,DependencyContext>); static_assert(CanMakeObservedCache<ObserverSet<AllObserver>>); static_assert(!CanMakeObservedCache<ObserverSet<UnsafeAllObserver>>); static_assert(dependencies::NodeTimingClock<dependencies::SteadyNodeClock>); static_assert(!dependencies::NodeTimingClock<decltype([]{returnstd::chrono::steady_clock::now();})>); static_assert(!dependencies::NodeTimingClock<decltype([]()noexcept{return1.0;})>); }