observers.hpp array concepts cstddef exception functional tuple type_traits utility format string_view serif/utils/dependencies/detail/schema.hpp src/include/serif/utils/dependencies/computation_cache.hpp src/include/serif/utils/dependencies/dependencies.hpp src/include/serif/utils/dependencies/timing.hpp serif::utils::dependencies::NodeObserver serif::utils::dependencies::AllNodesObserver serif::utils::dependencies::EdgeObserver serif::utils::dependencies::detail::NodeObservation serif::utils::dependencies::Observers serif::utils::dependencies::NoGraphObservers serif::utils::dependencies::detail::ValidObservers serif::utils::dependencies::detail::ValidObservers< Observers< Registrations... >, GraphT, ContextT, true > std::formatter< serif::utils::dependencies::NodeOutcome > serif serif::utils serif::utils::dependencies serif::utils::dependencies::detail #pragmaonce #include<array> #include<concepts> #include<cstddef> #include<exception> #include<functional> #include<tuple> #include<type_traits> #include<utility> #include<format> #include<string_view> #include"serif/utils/dependencies/detail/schema.hpp" namespaceserif::utils::dependencies{ enumclassNodeOutcome{ completed, rejected, exception }; template<typenameBeforeT,typenameAfterT> conceptNodeObserverCallbacks=std::invocable<BeforeT&>&& std::is_object_v<std::invoke_result_t<BeforeT&>>&&std::move_constructible<std::invoke_result_t<BeforeT&>>&& requires(AfterT&after,std::invoke_result_t<BeforeT&>&state,NodeOutcomeoutcome){ {std::invoke(after,state,outcome)}noexcept->std::same_as<void>; }; template<typenameKeyT,typenameBeforeT,typenameAfterT>requiresNodeObserverCallbacks<BeforeT, AfterT> structNodeObserver{ BeforeTbefore; AfterTafter; }; template<typenameBeforeT,typenameAfterT,typenameKeyT> conceptKeyedNodeObserverCallbacks=std::invocable<BeforeT&,std::type_identity<KeyT>>&& std::is_object_v<std::invoke_result_t<BeforeT&,std::type_identity<KeyT>>>&& std::move_constructible<std::invoke_result_t<BeforeT&,std::type_identity<KeyT>>>&& requires(AfterT&after,std::invoke_result_t<BeforeT&,std::type_identity<KeyT>>&state,NodeOutcomeoutcome){ {std::invoke(after,state,outcome)}noexcept->std::same_as<void>; }; template<typenameBeforeT,typenameAfterT> structAllNodesObserver{ BeforeTbefore; AfterTafter; }; template<typenameInputT,typenameConsumerT,typenameFunctionT> structEdgeObserver{ FunctionTfunction; }; namespacedetail{ template<typenameT> inlineconstexprboolis_observer_v=false; template<typenameKeyT,typenameBeforeT,typenameAfterT> inlineconstexprboolis_observer_v<NodeObserver<KeyT, BeforeT, AfterT>>=true; template<typenameBeforeT,typenameAfterT> inlineconstexprboolis_observer_v<AllNodesObserver<BeforeT, AfterT>>=true; template<typenameInputT,typenameConsumerT,typenameFunctionT> inlineconstexprboolis_observer_v<EdgeObserver<InputT, ConsumerT, FunctionT>>=true; template<typenameObserverT,typenameKeyT> inlineconstexprboolobserves_node_v=false; template<typenameKeyT,typenameBeforeT,typenameAfterT> inlineconstexprboolobserves_node_v<NodeObserver<KeyT, BeforeT, AfterT>,KeyT>=true; template<typenameBeforeT,typenameAfterT,typenameKeyT> inlineconstexprboolobserves_node_v<AllNodesObserver<BeforeT, AfterT>,KeyT>=true; template<typenameKeyT,typenameBeforeT,typenameAfterT> [[nodiscard]]decltype(auto)begin_node(NodeObserver<KeyT, BeforeT, AfterT>&observer){ returnstd::invoke(observer.before); } template<typenameKeyT,typenameBeforeT,typenameAfterT> [[nodiscard]]decltype(auto)begin_node(AllNodesObserver<BeforeT,AfterT>&observer){ returnstd::invoke(observer.before,std::type_identity<KeyT>{}); } template<typenameInputT,typenameConsumerT,typenameObserverT,typenameValueT> voidobserve_input(ObserverT&,constValueT&){} template<typenameInputT,typenameConsumerT,typenameFunctionT,typenameValueT> voidobserve_input(EdgeObserver<InputT,ConsumerT,FunctionT>&observer,constValueT&value){ std::invoke(observer.function,value); } template<typenameBeforeT,typenameAfterT> classNodeObservation{ public: NodeObservation(BeforeT&before,AfterT&after): m_after(after),m_state(std::invoke(before)),m_exceptions(std::uncaught_exceptions()){} NodeObservation(constNodeObservation&)=delete; NodeObservation&operator=(constNodeObservation&)=delete; ~NodeObservation()noexcept{ constNodeOutcomeoutcome=std::uncaught_exceptions()>m_exceptions?NodeOutcome::exception:m_outcome; std::invoke(m_after,m_state,outcome); } template<typenameResultT> voidreturned(constResultT&result)noexcept{ m_outcome=NodeOutcome::completed; ifconstexpr(Result<ResultT>::fallible){ if(!result){ m_outcome=NodeOutcome::rejected; } } } private: AfterT&m_after; std::invoke_result_t<BeforeT&>m_state; intm_exceptions; NodeOutcomem_outcome{NodeOutcome::exception}; }; } template<typename...Registrations>requires(detail::is_observer_v<Registrations>&&...) classObservers{ public: explicitObservers(Registrations...observers):m_observers(std::move(observers)...){} template<typenameKeyT,std::size_tPosition=0,typenameFunctionT> [[nodiscard]]autoevaluate(FunctionT&&function){ ifconstexpr(Position==sizeof...(Registrations)){ returnstd::invoke(function); }else{ auto&observer=std::get<Position>(m_observers); ifconstexpr(detail::observes_node_v<std::remove_cvref_t<decltype(observer)>,KeyT>){ autobefore=[&observer]()->decltype(auto){returndetail::begin_node<KeyT>(observer);}; detail::NodeObservationobservation(before,observer.after); autoresult=evaluate<KeyT, Position + 1>(function); observation.returned(result); returnresult; }else{ returnevaluate<KeyT, Position + 1>(function); } } } template<typenameInputT,typenameConsumerT,typenameValueT> voidedge(constValueT&value){ std::apply([&](auto&...observers){ (detail::observe_input<InputT, ConsumerT>(observers,value),...); },m_observers); } private: std::tuple<Registrations...>m_observers; }; template<typenameKeyT,typenameBeforeT,typenameAfterT> requiresNodeObserverCallbacks<std::decay_t<BeforeT>,std::decay_t<AfterT>>&& std::constructible_from<std::decay_t<BeforeT>,BeforeT>&&std::constructible_from<std::decay_t<AfterT>,AfterT> [[nodiscard]]autoobserve_node(BeforeT&&before,AfterT&&after){ returnNodeObserver<KeyT, std::decay_t<BeforeT>,std::decay_t<AfterT>>{ .before=std::forward<BeforeT>(before),.after=std::forward<AfterT>(after)}; } template<typenameBeforeT,typenameAfterT> requiresstd::constructible_from<std::decay_t<BeforeT>,BeforeT>&&std::constructible_from<std::decay_t<AfterT>,AfterT> [[nodiscard]]autoobserve_nodes(BeforeT&&before,AfterT&&after){ returnAllNodesObserver<std::decay_t<BeforeT>,std::decay_t<AfterT>>{ .before=std::forward<BeforeT>(before),.after=std::forward<AfterT>(after)}; } template<typenameInputT,typenameConsumerT,typenameFunctionT> requiresstd::constructible_from<std::decay_t<FunctionT>,FunctionT> [[nodiscard]]autoobserve_edge(FunctionT&&function){ returnEdgeObserver<InputT, ConsumerT, std::decay_t<FunctionT>>{.function=std::forward<FunctionT>(function)}; } template<typename...Registrations> requires(detail::is_observer_v<std::remove_cvref_t<Registrations>>&&...)&& (std::constructible_from<std::remove_cvref_t<Registrations>,Registrations>&&...) [[nodiscard]]automake_observers(Registrations&&...observers){ returnObservers<std::remove_cvref_t<Registrations>...>(std::forward<Registrations>(observers)...); } structNoGraphObservers{ template<typenameGraphT> [[nodiscard]]autooperator()(std::type_identity<GraphT>)const{ returnObservers<>{}; } }; namespacedetail{ template<typenameGraphT,typenameBindingsT,typenameKeyT,typenameBeforeT,typenameAfterT> [[nodiscard]]constevalboolvalid_observer(NodeObserver<KeyT, BeforeT, AfterT>*){ returnContainsKey<KeyT, typename GraphT::Results>; } template<typenameBeforeT,typenameAfterT,typename...Keys> [[nodiscard]]constevalboolvalid_all_nodes(KeyList<Keys...>){ constexprstd::array<bool,sizeof...(Keys)>valid{KeyedNodeObserverCallbacks<BeforeT,AfterT,Keys>...}; for(constboolentry:valid){ if(!entry){returnfalse;} } returntrue; } template<typenameGraphT,typenameBindingsT,typenameBeforeT,typenameAfterT> [[nodiscard]]constevalboolvalid_observer(AllNodesObserver<BeforeT,AfterT>*){ returnvalid_all_nodes<BeforeT, AfterT>(typenameGraphT::Results{}); } template<typenameGraphT,typenameBindingsT,typenameInputT,typenameConsumerT,typenameFunctionT> [[nodiscard]]constevalboolvalid_observer(EdgeObserver<InputT,ConsumerT,FunctionT>*){ ifconstexpr(!ContainsKey<ConsumerT,typenameGraphT::Results>){ returnfalse; }else{ usingConsumer=GraphT::templateComputationAt<GraphT::templateindex<ConsumerT>()>; ifconstexpr(!ContainsKey<InputT,typenameConsumer::Inputs>){ returnfalse; }else{ returnrequires(FunctionT&function,constbound_type_t<BindingsT, InputT>&value){ {std::invoke(function,value)}->std::same_as<void>; }; } } } template<typenameObserversT,typenameGraphT,typenameContextT,boolValid=ComputationGraphFor<GraphT,ContextT>> structValidObservers:std::false_type{}; template<typename...Registrations,typenameGraphT,typenameContextT> structValidObservers<Observers<Registrations...>,GraphT,ContextT,true>:std::bool_constant< (valid_observer<GraphT,typenameComputationGraphSchema<GraphT,ContextT>::ResolvedBindings>(static_cast<Registrations*>(nullptr))&&...)>{}; } template<typenameObserversT,typenameGraphT,typenameContextT> conceptObserversFor=detail::ValidObservers<std::remove_cvref_t<ObserversT>,std::remove_cvref_t<GraphT>,std::remove_cvref_t<ContextT>>::value; } template<> structstd::formatter<serif::utils::dependencies::NodeOutcome>:std::formatter<std::string_view>{ autoformat(constserif::utils::dependencies::NodeOutcome&outcome,auto&ctx)const{ std::string_viewname="unknown"; switch(outcome){ caseserif::utils::dependencies::NodeOutcome::completed:name="completed";break; caseserif::utils::dependencies::NodeOutcome::rejected:name="rejected";break; caseserif::utils::dependencies::NodeOutcome::exception:name="exception";break; } returnstd::formatter<std::string_view>::format(name,ctx); } };