#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace flow { namespace detail { template using is_duplicated = std::bool_constant<(T::size() > 1)>; template concept is_output_compatible = requires(CTNode n) { { Output::create_node(n) } -> std::same_as; }; } // namespace detail template using name_for = typename T::name_t; [[nodiscard]] constexpr auto edge_size(auto const &nodes, auto const &edges) -> std::size_t { auto const edge_capacities = transform( [&](N const &) { return edges.fold_left( std::size_t{}, [](auto acc, E const &) { if constexpr (std::is_same_v, name_for> or std::is_same_v, name_for>) { return ++acc; } else { return acc; } }); }, nodes); return edge_capacities.fold_left(std::size_t{1}, [](auto acc, auto next) { return std::max(acc, next); }); } template typename Impl> struct graph_builder { // NOLINTBEGIN(readability-function-cognitive-complexity) template [[nodiscard]] constexpr static auto make_graph(auto const &nodes, auto const &edges) { using output_node_t = typename Output::node_t; using graph_t = stdx::cx_multimap; graph_t g{}; for_each([&](Node n) { g.put(Output::create_node(n)); }, nodes); auto const named_nodes = stdx::apply_indices(nodes); for_each( [&]( dsl::edge const &) { auto lhs = get>(named_nodes); auto rhs = get>(named_nodes); using lhs_t = std::remove_cvref_t; using rhs_t = std::remove_cvref_t; using lhs_cond_t = std::remove_cvref_t; using rhs_cond_t = std::remove_cvref_t; using edge_ps_t = decltype(Cond::predicates); auto node_ps = stdx::to_unsorted_set(stdx::tuple_cat( lhs_t::condition.predicates, rhs_t::condition.predicates)); stdx::for_each( [&](P const &) { STATIC_ASSERT( (stdx::contains_type), "The conditions on the sequence ({} >> {})[{}] are " "weaker than those on {}[{}] or {}[{}]. " "Specifically, the sequence is missing the " "predicate: {}", CX_VALUE(lhs_t::ct_name), CX_VALUE(rhs_t::ct_name), CX_VALUE(Cond::ct_name), CX_VALUE(lhs_t::ct_name), CX_VALUE(lhs_cond_t::ct_name), CX_VALUE(rhs_t::ct_name), CX_VALUE(rhs_cond_t::ct_name), CX_VALUE(P)); }, node_ps); g.put(Output::create_node(lhs), Output::create_node(rhs)); }, edges); return g; } // NOLINTEND(readability-function-cognitive-complexity) template [[nodiscard]] constexpr static auto is_source_of(Node const &node, Graph const &g) -> bool { return std::find_if(g.begin(), g.end(), [&](auto const &entry) { return entry.value.contains(node); }) == g.end(); } template [[nodiscard]] constexpr static auto get_sources(Graph const &g) -> stdx::cx_set { stdx::cx_set s; for (auto const &entry : g) { s.insert(entry.key); } for (auto const &entry : g) { for (auto const &dst : entry.value) { s.erase(dst); } } return s; } template [[nodiscard]] constexpr static auto topo_sort(Graph &g) -> std::optional { stdx::cx_vector ordered_list{}; auto sources = get_sources(g); while (not sources.empty()) { auto n = sources.pop_back(); ordered_list.push_back(n); if (g.contains(n)) { auto ms = g.get(n); if (ms.empty()) { g.erase(n); } else { for (auto const &entry : ms) { g.erase(n, entry); if (is_source_of(entry, g)) { sources.insert(entry); } } } } } if (not g.empty()) { return {}; } using span_t = stdx::span; return std::optional{std::in_place, span_t{ordered_list}}; } template constexpr static auto error_steps = stdx::transform([](auto N) { return stdx::ct_string_from_type(N); }, T{}) .join(stdx::ct_string{""}, [](auto x, auto y) { using namespace stdx::literals; return x + ", "_cts + y; }); constexpr static void check_for_missing_nodes(auto nodes, auto mentioned_nodes) { constexpr auto get_name = [](N) -> typename N::name_t { return {}; }; auto node_names = stdx::transform(get_name, nodes); auto mentioned_node_names = stdx::transform(get_name, mentioned_nodes); using node_names_t = decltype(stdx::to_sorted_set(node_names)); using mentioned_node_names_t = decltype(stdx::to_sorted_set(mentioned_node_names)); using missing_nodes_t = boost::mp11::mp_set_difference; STATIC_ASSERT( (std::is_same_v), "One or more steps are referenced in the flow ({}) but not " "explicitly added with the * operator. The missing steps are: {}.", CX_VALUE(Name), CX_VALUE(error_steps)); constexpr auto duplicates = stdx::transform( [](auto e) { return stdx::get<0>(e); }, stdx::filter(stdx::gather(node_names))); using duplicate_nodes_t = decltype(duplicates); STATIC_ASSERT( stdx::tuple_size_v == 0, "One or more steps in the flow ({}) are explicitly added more than " "once using the * operator. The duplicate steps are: {}.", CX_VALUE(Name), CX_VALUE(error_steps)); } template [[nodiscard]] constexpr static auto build(Graph const &input) { auto nodes = flow::dsl::get_nodes(input); auto mentioned_nodes = flow::dsl::get_all_mentioned_nodes(input); check_for_missing_nodes(nodes, mentioned_nodes); auto node_set = stdx::to_unsorted_set(nodes); auto edges = stdx::to_unsorted_set(flow::dsl::get_edges(input)); constexpr auto node_capacity = stdx::tuple_size_v; constexpr auto edge_capacity = edge_size(node_set, edges); using output_t = Impl; static_assert( all_of( [](N const &) { return detail::is_output_compatible; }, node_set), "Output node type is not compatible with given input nodes"); auto g = make_graph(node_set, edges); return topo_sort(g); } template class built_flow { constexpr static auto built() { constexpr auto v = Initialized::value; constexpr auto built = build(v); static_assert(built.has_value(), "Topological sort failed: cycle in flow"); constexpr auto functionPtrs = built->functionPtrs; constexpr auto size = std::size(functionPtrs); constexpr auto name = built->name; return [&](std::index_sequence) { return detail::inlined_func_list{}; }(std::make_index_sequence{}); } constexpr static auto run() { built()(); } public: // NOLINTNEXTLINE(google-explicit-constructor) constexpr explicit(false) operator FunctionPtr() const { return run; } constexpr auto operator()() const -> void { run(); } constexpr static bool active = decltype(built())::active; }; template [[nodiscard]] constexpr static auto render() -> built_flow { return {}; } }; template , flow::dsl::subgraph... Fragments> class graph { template friend constexpr auto tag_invoke(Tag, graph const &g) { return g.fragments.apply([](auto const &...frags) { return stdx::tuple_cat(Tag{}(frags)...); }); } public: template [[nodiscard]] constexpr auto add(Ns &&...ns) { return fragments.apply([&](auto &...frags) { return graph...>{ {frags..., std::forward(ns)...}}; }); } template [[nodiscard]] constexpr static auto build() { return Renderer::template render(); } constexpr static auto name = Name; stdx::tuple fragments; }; } // namespace flow