rename chapters
This commit is contained in:
27
Chapter17/cib/libs/flow/builder.hpp
Normal file
27
Chapter17/cib/libs/flow/builder.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <flow/common.hpp>
|
||||
#include <flow/graph_builder.hpp>
|
||||
#include <flow/impl.hpp>
|
||||
|
||||
#include <stdx/compiler.hpp>
|
||||
#include <stdx/ct_string.hpp>
|
||||
#include <stdx/panic.hpp>
|
||||
|
||||
namespace flow {
|
||||
template <stdx::ct_string Name = "">
|
||||
using builder = graph<Name, graph_builder<Name, impl>>;
|
||||
|
||||
template <stdx::ct_string Name = ""> struct service {
|
||||
using builder_t = builder<Name>;
|
||||
using interface_t = FunctionPtr;
|
||||
|
||||
CONSTEVAL static auto uninitialized() -> interface_t {
|
||||
return [] {
|
||||
using namespace stdx::literals;
|
||||
stdx::panic<"Attempting to run flow ("_cts + Name +
|
||||
") before it is initialized"_cts>();
|
||||
};
|
||||
}
|
||||
};
|
||||
} // namespace flow
|
||||
5
Chapter17/cib/libs/flow/common.hpp
Normal file
5
Chapter17/cib/libs/flow/common.hpp
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace flow {
|
||||
using FunctionPtr = auto (*)() -> void;
|
||||
} // namespace flow
|
||||
72
Chapter17/cib/libs/flow/detail/par.hpp
Normal file
72
Chapter17/cib/libs/flow/detail/par.hpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#include <flow/detail/walk.hpp>
|
||||
#include <flow/subgraph_identity.hpp>
|
||||
|
||||
#include <stdx/tuple_algorithms.hpp>
|
||||
|
||||
namespace flow::dsl {
|
||||
template <subgraph Lhs, subgraph Rhs,
|
||||
subgraph_identity Identity = subgraph_identity::REFERENCE>
|
||||
struct par {
|
||||
Lhs lhs;
|
||||
Rhs rhs;
|
||||
|
||||
using is_subgraph = void;
|
||||
|
||||
constexpr auto operator*() const {
|
||||
return par<Lhs, Rhs, subgraph_identity::VALUE>{Lhs{}, Rhs{}};
|
||||
}
|
||||
|
||||
private:
|
||||
friend constexpr auto tag_invoke(get_initials_t, par const &p) {
|
||||
return stdx::tuple_cat(get_initials(p.lhs), get_initials(p.rhs));
|
||||
}
|
||||
|
||||
friend constexpr auto tag_invoke(get_finals_t, par const &p) {
|
||||
return stdx::tuple_cat(get_finals(p.lhs), get_finals(p.rhs));
|
||||
}
|
||||
|
||||
friend constexpr auto tag_invoke(get_nodes_t, par const &p) {
|
||||
if constexpr (Identity == subgraph_identity::VALUE) {
|
||||
auto all_nodes = stdx::to_unsorted_set(
|
||||
stdx::tuple_cat(get_all_mentioned_nodes(p.lhs),
|
||||
get_all_mentioned_nodes(p.rhs)));
|
||||
|
||||
return stdx::transform([](auto const &n) { return *n; }, all_nodes);
|
||||
|
||||
} else {
|
||||
return stdx::tuple_cat(get_nodes(p.lhs), get_nodes(p.rhs));
|
||||
}
|
||||
}
|
||||
|
||||
friend constexpr auto tag_invoke(get_all_mentioned_nodes_t, par const &p) {
|
||||
return stdx::tuple_cat(get_all_mentioned_nodes(p.lhs),
|
||||
get_all_mentioned_nodes(p.rhs));
|
||||
}
|
||||
|
||||
friend constexpr auto tag_invoke(get_edges_t, par const &p) {
|
||||
return stdx::tuple_cat(get_edges(p.lhs), get_edges(p.rhs));
|
||||
}
|
||||
};
|
||||
|
||||
template <subgraph Lhs, subgraph Rhs> par(Lhs, Rhs) -> par<Lhs, Rhs>;
|
||||
} // namespace flow::dsl
|
||||
|
||||
template <flow::dsl::subgraph Lhs, flow::dsl::subgraph Rhs>
|
||||
[[nodiscard]] constexpr auto operator&&(Lhs const &lhs, Rhs const &rhs) {
|
||||
return flow::dsl::par{lhs, rhs};
|
||||
}
|
||||
|
||||
template <typename Cond, flow::dsl::subgraph Lhs, flow::dsl::subgraph Rhs,
|
||||
flow::subgraph_identity Identity>
|
||||
constexpr auto make_runtime_conditional(Cond,
|
||||
flow::dsl::par<Lhs, Rhs, Identity>) {
|
||||
auto lhs = make_runtime_conditional(Cond{}, Lhs{});
|
||||
auto rhs = make_runtime_conditional(Cond{}, Rhs{});
|
||||
|
||||
using lhs_t = decltype(lhs);
|
||||
using rhs_t = decltype(rhs);
|
||||
|
||||
return flow::dsl::par<lhs_t, rhs_t, Identity>{lhs, rhs};
|
||||
}
|
||||
86
Chapter17/cib/libs/flow/detail/seq.hpp
Normal file
86
Chapter17/cib/libs/flow/detail/seq.hpp
Normal file
@@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
|
||||
#include <cib/detail/runtime_conditional.hpp>
|
||||
#include <flow/detail/walk.hpp>
|
||||
#include <flow/subgraph_identity.hpp>
|
||||
|
||||
#include <stdx/tuple_algorithms.hpp>
|
||||
|
||||
namespace flow::dsl {
|
||||
template <subgraph Lhs, subgraph Rhs,
|
||||
subgraph_identity Identity = subgraph_identity::REFERENCE,
|
||||
typename Cond = cib::detail::always_condition_t>
|
||||
struct seq {
|
||||
Lhs lhs;
|
||||
Rhs rhs;
|
||||
|
||||
using is_subgraph = void;
|
||||
|
||||
constexpr auto operator*() const {
|
||||
return seq<Lhs, Rhs, subgraph_identity::VALUE, Cond>{Lhs{}, Rhs{}};
|
||||
}
|
||||
|
||||
private:
|
||||
friend constexpr auto tag_invoke(get_initials_t, seq const &s) {
|
||||
return get_initials(s.lhs);
|
||||
}
|
||||
|
||||
friend constexpr auto tag_invoke(get_finals_t, seq const &s) {
|
||||
return get_finals(s.rhs);
|
||||
}
|
||||
|
||||
friend constexpr auto tag_invoke(get_nodes_t, seq const &s) {
|
||||
if constexpr (Identity == subgraph_identity::VALUE) {
|
||||
auto all_nodes = stdx::to_unsorted_set(
|
||||
stdx::tuple_cat(get_all_mentioned_nodes(s.lhs),
|
||||
get_all_mentioned_nodes(s.rhs)));
|
||||
|
||||
return stdx::transform([](auto const &n) { return *n; }, all_nodes);
|
||||
|
||||
} else {
|
||||
return stdx::tuple_cat(get_nodes(s.lhs), get_nodes(s.rhs));
|
||||
}
|
||||
}
|
||||
|
||||
friend constexpr auto tag_invoke(get_all_mentioned_nodes_t, seq const &s) {
|
||||
return stdx::tuple_cat(get_all_mentioned_nodes(s.lhs),
|
||||
get_all_mentioned_nodes(s.rhs));
|
||||
}
|
||||
|
||||
friend constexpr auto tag_invoke(get_edges_t, seq const &s) {
|
||||
auto is = get_initials(s.rhs);
|
||||
auto fs = get_finals(s.lhs);
|
||||
|
||||
return stdx::tuple_cat(
|
||||
get_edges(s.lhs), get_edges(s.rhs),
|
||||
transform(
|
||||
[]<typename P>(P const &) {
|
||||
return edge<stdx::tuple_element_t<0, P>,
|
||||
stdx::tuple_element_t<1, P>, Cond>{};
|
||||
},
|
||||
cartesian_product_copy(fs, is)));
|
||||
}
|
||||
};
|
||||
|
||||
template <subgraph Lhs, subgraph Rhs> seq(Lhs, Rhs) -> seq<Lhs, Rhs>;
|
||||
|
||||
} // namespace flow::dsl
|
||||
|
||||
template <flow::dsl::subgraph Lhs, flow::dsl::subgraph Rhs>
|
||||
[[nodiscard]] constexpr auto operator>>(Lhs const &lhs, Rhs const &rhs) {
|
||||
return flow::dsl::seq{lhs, rhs};
|
||||
}
|
||||
|
||||
template <typename Cond, flow::dsl::subgraph Lhs, flow::dsl::subgraph Rhs,
|
||||
flow::subgraph_identity Identity, typename EdgeCond>
|
||||
constexpr auto
|
||||
make_runtime_conditional(Cond, flow::dsl::seq<Lhs, Rhs, Identity, EdgeCond>) {
|
||||
auto lhs = make_runtime_conditional(Cond{}, Lhs{});
|
||||
auto rhs = make_runtime_conditional(Cond{}, Rhs{});
|
||||
|
||||
using lhs_t = decltype(lhs);
|
||||
using rhs_t = decltype(rhs);
|
||||
using cond_t = decltype(EdgeCond{} and Cond{});
|
||||
|
||||
return flow::dsl::seq<lhs_t, rhs_t, Identity, cond_t>{lhs, rhs};
|
||||
}
|
||||
103
Chapter17/cib/libs/flow/detail/walk.hpp
Normal file
103
Chapter17/cib/libs/flow/detail/walk.hpp
Normal file
@@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
|
||||
#include <flow/subgraph_identity.hpp>
|
||||
|
||||
#include <stdx/concepts.hpp>
|
||||
#include <stdx/tuple.hpp>
|
||||
#include <stdx/type_traits.hpp>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace flow::dsl {
|
||||
template <typename T>
|
||||
concept subgraph = requires { typename stdx::remove_cvref_t<T>::is_subgraph; };
|
||||
|
||||
template <typename Source, typename Dest, typename Cond> struct edge {
|
||||
using source_t = Source;
|
||||
using dest_t = Dest;
|
||||
using cond_t = Cond;
|
||||
};
|
||||
|
||||
constexpr inline class get_initials_t {
|
||||
template <subgraph N>
|
||||
friend constexpr auto tag_invoke(get_initials_t, N &&n) {
|
||||
return stdx::make_tuple(std::forward<N>(n));
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename... Ts>
|
||||
constexpr auto operator()(Ts &&...ts) const
|
||||
noexcept(noexcept(tag_invoke(std::declval<get_initials_t>(),
|
||||
std::forward<Ts>(ts)...)))
|
||||
-> decltype(tag_invoke(*this, std::forward<Ts>(ts)...)) {
|
||||
return tag_invoke(*this, std::forward<Ts>(ts)...);
|
||||
}
|
||||
} get_initials{};
|
||||
|
||||
constexpr inline class get_finals_t {
|
||||
template <subgraph N>
|
||||
friend constexpr auto tag_invoke(get_finals_t, N &&n) {
|
||||
return stdx::make_tuple(std::forward<N>(n));
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename... Ts>
|
||||
constexpr auto operator()(Ts &&...ts) const
|
||||
noexcept(noexcept(tag_invoke(std::declval<get_finals_t>(),
|
||||
std::forward<Ts>(ts)...)))
|
||||
-> decltype(tag_invoke(*this, std::forward<Ts>(ts)...)) {
|
||||
return tag_invoke(*this, std::forward<Ts>(ts)...);
|
||||
}
|
||||
} get_finals{};
|
||||
|
||||
constexpr inline class get_nodes_t {
|
||||
template <subgraph N> friend constexpr auto tag_invoke(get_nodes_t, N &&n) {
|
||||
if constexpr (std::remove_cvref_t<N>::identity ==
|
||||
subgraph_identity::REFERENCE) {
|
||||
return stdx::tuple{};
|
||||
} else {
|
||||
return stdx::make_tuple(std::forward<N>(n));
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename... Ts>
|
||||
constexpr auto operator()(Ts &&...ts) const
|
||||
noexcept(noexcept(tag_invoke(std::declval<get_nodes_t>(),
|
||||
std::forward<Ts>(ts)...)))
|
||||
-> decltype(tag_invoke(*this, std::forward<Ts>(ts)...)) {
|
||||
return tag_invoke(*this, std::forward<Ts>(ts)...);
|
||||
}
|
||||
} get_nodes{};
|
||||
|
||||
constexpr inline class get_all_mentioned_nodes_t {
|
||||
template <subgraph N>
|
||||
friend constexpr auto tag_invoke(get_all_mentioned_nodes_t, N &&n) {
|
||||
return stdx::make_tuple(std::forward<N>(n));
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename... Ts>
|
||||
constexpr auto operator()(Ts &&...ts) const
|
||||
noexcept(noexcept(tag_invoke(std::declval<get_all_mentioned_nodes_t>(),
|
||||
std::forward<Ts>(ts)...)))
|
||||
-> decltype(tag_invoke(*this, std::forward<Ts>(ts)...)) {
|
||||
return tag_invoke(*this, std::forward<Ts>(ts)...);
|
||||
}
|
||||
} get_all_mentioned_nodes{};
|
||||
|
||||
constexpr inline class get_edges_t {
|
||||
friend constexpr auto tag_invoke(get_edges_t, subgraph auto const &) {
|
||||
return stdx::tuple{};
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename... Ts>
|
||||
constexpr auto operator()(Ts &&...ts) const
|
||||
noexcept(noexcept(tag_invoke(std::declval<get_edges_t>(),
|
||||
std::forward<Ts>(ts)...)))
|
||||
-> decltype(tag_invoke(*this, std::forward<Ts>(ts)...)) {
|
||||
return tag_invoke(*this, std::forward<Ts>(ts)...);
|
||||
}
|
||||
} get_edges{};
|
||||
} // namespace flow::dsl
|
||||
9
Chapter17/cib/libs/flow/flow.hpp
Normal file
9
Chapter17/cib/libs/flow/flow.hpp
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <flow/builder.hpp>
|
||||
#include <flow/common.hpp>
|
||||
#include <flow/detail/par.hpp>
|
||||
#include <flow/detail/seq.hpp>
|
||||
#include <flow/impl.hpp>
|
||||
#include <flow/run.hpp>
|
||||
#include <flow/step.hpp>
|
||||
296
Chapter17/cib/libs/flow/graph_builder.hpp
Normal file
296
Chapter17/cib/libs/flow/graph_builder.hpp
Normal file
@@ -0,0 +1,296 @@
|
||||
#pragma once
|
||||
|
||||
#include <flow/common.hpp>
|
||||
#include <flow/detail/walk.hpp>
|
||||
#include <flow/impl.hpp>
|
||||
|
||||
#include <stdx/ct_string.hpp>
|
||||
#include <stdx/cx_multimap.hpp>
|
||||
#include <stdx/cx_set.hpp>
|
||||
#include <stdx/cx_vector.hpp>
|
||||
#include <stdx/span.hpp>
|
||||
#include <stdx/static_assert.hpp>
|
||||
#include <stdx/tuple_algorithms.hpp>
|
||||
#include <stdx/type_traits.hpp>
|
||||
#include <stdx/utility.hpp>
|
||||
|
||||
#include <boost/mp11/set.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
namespace flow {
|
||||
namespace detail {
|
||||
template <typename T> using is_duplicated = std::bool_constant<(T::size() > 1)>;
|
||||
|
||||
template <typename CTNode, typename Output>
|
||||
concept is_output_compatible = requires(CTNode n) {
|
||||
{ Output::create_node(n) } -> std::same_as<typename Output::node_t>;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <typename T> 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(
|
||||
[&]<typename N>(N const &) {
|
||||
return edges.fold_left(
|
||||
std::size_t{}, []<typename E>(auto acc, E const &) {
|
||||
if constexpr (std::is_same_v<name_for<typename E::source_t>,
|
||||
name_for<N>> or
|
||||
std::is_same_v<name_for<typename E::dest_t>,
|
||||
name_for<N>>) {
|
||||
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 <stdx::ct_string Name,
|
||||
template <stdx::ct_string, std::size_t> typename Impl>
|
||||
struct graph_builder {
|
||||
// NOLINTBEGIN(readability-function-cognitive-complexity)
|
||||
template <typename Output, std::size_t N, std::size_t E>
|
||||
[[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<output_node_t, output_node_t, N, E>;
|
||||
graph_t g{};
|
||||
for_each([&]<typename Node>(Node n) { g.put(Output::create_node(n)); },
|
||||
nodes);
|
||||
|
||||
auto const named_nodes = stdx::apply_indices<name_for>(nodes);
|
||||
for_each(
|
||||
[&]<typename Lhs, typename Rhs, typename Cond>(
|
||||
dsl::edge<Lhs, Rhs, Cond> const &) {
|
||||
auto lhs = get<name_for<Lhs>>(named_nodes);
|
||||
auto rhs = get<name_for<Rhs>>(named_nodes);
|
||||
|
||||
using lhs_t = std::remove_cvref_t<decltype(lhs)>;
|
||||
using rhs_t = std::remove_cvref_t<decltype(rhs)>;
|
||||
using lhs_cond_t = std::remove_cvref_t<decltype(lhs.condition)>;
|
||||
using rhs_cond_t = std::remove_cvref_t<decltype(rhs.condition)>;
|
||||
|
||||
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(
|
||||
[&]<typename P>(P const &) {
|
||||
STATIC_ASSERT(
|
||||
(stdx::contains_type<edge_ps_t, P>),
|
||||
"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 <typename Node, typename Graph>
|
||||
[[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 <typename Graph>
|
||||
[[nodiscard]] constexpr static auto get_sources(Graph const &g)
|
||||
-> stdx::cx_set<typename Graph::key_type, Graph::capacity()> {
|
||||
stdx::cx_set<typename Graph::key_type, Graph::capacity()> 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 <typename Output, typename Graph>
|
||||
[[nodiscard]] constexpr static auto
|
||||
topo_sort(Graph &g) -> std::optional<Output> {
|
||||
stdx::cx_vector<typename Graph::key_type, Graph::capacity()>
|
||||
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<typename Graph::key_type const, Graph::capacity()>;
|
||||
return std::optional<Output>{std::in_place, span_t{ordered_list}};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
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 = []<typename N>(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<mentioned_node_names_t,
|
||||
node_names_t>;
|
||||
STATIC_ASSERT(
|
||||
(std::is_same_v<node_names_t, mentioned_node_names_t>),
|
||||
"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<missing_nodes_t>));
|
||||
|
||||
constexpr auto duplicates = stdx::transform(
|
||||
[](auto e) { return stdx::get<0>(e); },
|
||||
stdx::filter<detail::is_duplicated>(stdx::gather(node_names)));
|
||||
using duplicate_nodes_t = decltype(duplicates);
|
||||
STATIC_ASSERT(
|
||||
stdx::tuple_size_v<duplicate_nodes_t> == 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<duplicate_nodes_t>));
|
||||
}
|
||||
|
||||
template <typename Graph>
|
||||
[[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<decltype(node_set)>;
|
||||
constexpr auto edge_capacity = edge_size(node_set, edges);
|
||||
|
||||
using output_t = Impl<Graph::name, node_capacity>;
|
||||
static_assert(
|
||||
all_of(
|
||||
[]<typename N>(N const &) {
|
||||
return detail::is_output_compatible<N, output_t>;
|
||||
},
|
||||
node_set),
|
||||
"Output node type is not compatible with given input nodes");
|
||||
|
||||
auto g =
|
||||
make_graph<output_t, node_capacity, edge_capacity>(node_set, edges);
|
||||
return topo_sort<output_t>(g);
|
||||
}
|
||||
|
||||
template <typename Initialized> 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::size_t... Is>(std::index_sequence<Is...>) {
|
||||
return detail::inlined_func_list<name, functionPtrs[Is]...>{};
|
||||
}(std::make_index_sequence<size>{});
|
||||
}
|
||||
|
||||
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 <typename Initialized>
|
||||
[[nodiscard]] constexpr static auto render() -> built_flow<Initialized> {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
template <stdx::ct_string Name = "",
|
||||
typename Renderer = graph_builder<Name, impl>,
|
||||
flow::dsl::subgraph... Fragments>
|
||||
class graph {
|
||||
template <typename Tag>
|
||||
friend constexpr auto tag_invoke(Tag, graph const &g) {
|
||||
return g.fragments.apply([](auto const &...frags) {
|
||||
return stdx::tuple_cat(Tag{}(frags)...);
|
||||
});
|
||||
}
|
||||
|
||||
public:
|
||||
template <flow::dsl::subgraph... Ns>
|
||||
[[nodiscard]] constexpr auto add(Ns &&...ns) {
|
||||
return fragments.apply([&](auto &...frags) {
|
||||
return graph<Name, Renderer, Fragments...,
|
||||
stdx::remove_cvref_t<Ns>...>{
|
||||
{frags..., std::forward<Ns>(ns)...}};
|
||||
});
|
||||
}
|
||||
|
||||
template <typename BuilderValue>
|
||||
[[nodiscard]] constexpr static auto build() {
|
||||
return Renderer::template render<BuilderValue>();
|
||||
}
|
||||
|
||||
constexpr static auto name = Name;
|
||||
stdx::tuple<Fragments...> fragments;
|
||||
};
|
||||
} // namespace flow
|
||||
77
Chapter17/cib/libs/flow/graphviz_builder.hpp
Normal file
77
Chapter17/cib/libs/flow/graphviz_builder.hpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include <flow/detail/walk.hpp>
|
||||
|
||||
#include <stdx/tuple_algorithms.hpp>
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace flow {
|
||||
using VizFunctionPtr = auto (*)() -> std::string;
|
||||
|
||||
struct graphviz_builder {
|
||||
template <typename Graph>
|
||||
[[nodiscard]] static auto build(Graph const &input) {
|
||||
auto const nodes = flow::dsl::get_nodes(input);
|
||||
auto const edges = flow::dsl::get_edges(input);
|
||||
|
||||
using nodes_t = std::set<std::string_view>;
|
||||
nodes_t sources{};
|
||||
nodes_t sinks{};
|
||||
for_each(
|
||||
[&]<typename Node>(Node const &) {
|
||||
sources.insert(Node::name_t::value);
|
||||
sinks.insert(Node::name_t::value);
|
||||
},
|
||||
nodes);
|
||||
for_each(
|
||||
[&]<typename Edge>(Edge const &) {
|
||||
sinks.erase(Edge::source_t::name_t::value);
|
||||
sources.erase(Edge::dest_t::name_t::value);
|
||||
},
|
||||
edges);
|
||||
|
||||
std::string output{"digraph "};
|
||||
output += std::string_view{Graph::name};
|
||||
output += " {\n";
|
||||
for (auto const &node : sources) {
|
||||
output += "start -> " + std::string{node} + '\n';
|
||||
}
|
||||
for_each(
|
||||
[&]<typename Edge>(Edge const &) {
|
||||
output += std::string{Edge::source_t::name_t::value};
|
||||
output += " -> ";
|
||||
output += std::string{Edge::dest_t::name_t::value};
|
||||
output += '\n';
|
||||
},
|
||||
edges);
|
||||
for (auto const &node : sinks) {
|
||||
output += std::string{node} + " -> end\n";
|
||||
}
|
||||
output += "}";
|
||||
return output;
|
||||
}
|
||||
|
||||
template <typename Initialized> class built_flow {
|
||||
static auto built() {
|
||||
auto const v = Initialized::value;
|
||||
return build(v);
|
||||
}
|
||||
|
||||
static auto run() -> std::string { return built(); }
|
||||
|
||||
public:
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
constexpr explicit(false) operator VizFunctionPtr() const {
|
||||
return run;
|
||||
}
|
||||
auto operator()() const -> std::string { return run(); }
|
||||
constexpr static bool active = true;
|
||||
};
|
||||
|
||||
template <typename Initialized>
|
||||
[[nodiscard]] constexpr static auto render() -> built_flow<Initialized> {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
} // namespace flow
|
||||
79
Chapter17/cib/libs/flow/impl.hpp
Normal file
79
Chapter17/cib/libs/flow/impl.hpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
#include <flow/common.hpp>
|
||||
#include <flow/log.hpp>
|
||||
#include <log/env.hpp>
|
||||
#include <log/level.hpp>
|
||||
#include <log/log.hpp>
|
||||
|
||||
#include <stdx/ct_string.hpp>
|
||||
#include <stdx/span.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
|
||||
namespace flow {
|
||||
namespace detail {
|
||||
template <stdx::ct_string FlowName, typename CTNode>
|
||||
constexpr auto run_func() -> void {
|
||||
if (CTNode::condition) {
|
||||
if constexpr (not FlowName.empty()) {
|
||||
using log_spec_t =
|
||||
decltype(get_log_spec<CTNode, log_spec_id_t<FlowName>>());
|
||||
CIB_LOG_ENV(logging::get_level, log_spec_t::level);
|
||||
CIB_LOG(typename log_spec_t::flavor, "flow.{}({})",
|
||||
typename CTNode::type_t{}, typename CTNode::name_t{});
|
||||
}
|
||||
typename CTNode::func_t{}();
|
||||
}
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template <stdx::ct_string Name, std::size_t NumSteps> struct impl {
|
||||
using node_t = FunctionPtr;
|
||||
std::array<FunctionPtr, NumSteps> functionPtrs{};
|
||||
|
||||
constexpr static auto name = Name;
|
||||
|
||||
template <typename CTNode>
|
||||
constexpr static auto create_node(CTNode) -> node_t {
|
||||
constexpr auto fp = detail::run_func<Name, CTNode>;
|
||||
return fp;
|
||||
}
|
||||
|
||||
constexpr explicit(true) impl(stdx::span<node_t const, NumSteps> steps) {
|
||||
std::copy(std::cbegin(steps), std::cend(steps),
|
||||
std::begin(functionPtrs));
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template <stdx::ct_string Name, auto... FuncPtrs> struct inlined_func_list {
|
||||
constexpr static auto active = sizeof...(FuncPtrs) > 0;
|
||||
constexpr static auto ct_name = Name;
|
||||
|
||||
__attribute__((flatten, always_inline)) auto operator()() const -> void {
|
||||
constexpr static bool loggingEnabled = not Name.empty();
|
||||
|
||||
constexpr auto name =
|
||||
stdx::ct_string_to_type<Name, sc::string_constant>();
|
||||
|
||||
if constexpr (loggingEnabled) {
|
||||
using log_spec_t = decltype(get_log_spec<inlined_func_list>());
|
||||
CIB_LOG_ENV(logging::get_level, log_spec_t::level);
|
||||
CIB_LOG(typename log_spec_t::flavor, "flow.start({})", name);
|
||||
}
|
||||
|
||||
(FuncPtrs(), ...);
|
||||
|
||||
if constexpr (loggingEnabled) {
|
||||
using log_spec_t = decltype(get_log_spec<inlined_func_list>());
|
||||
CIB_LOG_ENV(logging::get_level, log_spec_t::level);
|
||||
CIB_LOG(typename log_spec_t::flavor, "flow.end({})", name);
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
} // namespace flow
|
||||
36
Chapter17/cib/libs/flow/log.hpp
Normal file
36
Chapter17/cib/libs/flow/log.hpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <log/log.hpp>
|
||||
|
||||
#include <stdx/ct_string.hpp>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace flow {
|
||||
struct default_log_spec {
|
||||
using flavor = logging::default_flavor_t;
|
||||
constexpr static auto level = logging::level::TRACE;
|
||||
};
|
||||
template <stdx::ct_string, typename...>
|
||||
constexpr auto log_spec = default_log_spec{};
|
||||
|
||||
template <stdx::ct_string Name> struct log_spec_id_t {
|
||||
constexpr static auto ct_name = Name;
|
||||
};
|
||||
|
||||
template <typename T, typename Fallback = log_spec_id_t<"default">,
|
||||
typename... DummyArgs>
|
||||
requires(sizeof...(DummyArgs) == 0)
|
||||
constexpr static auto get_log_spec() {
|
||||
using log_spec_t = decltype(log_spec<T::ct_name, DummyArgs...>);
|
||||
if constexpr (std::is_same_v<log_spec_t, default_log_spec const>) {
|
||||
if constexpr (Fallback::ct_name == stdx::ct_string{"default"}) {
|
||||
return log_spec<Fallback::ct_name, DummyArgs...>;
|
||||
} else {
|
||||
return get_log_spec<Fallback>();
|
||||
}
|
||||
} else {
|
||||
return log_spec_t{};
|
||||
}
|
||||
}
|
||||
} // namespace flow
|
||||
14
Chapter17/cib/libs/flow/run.hpp
Normal file
14
Chapter17/cib/libs/flow/run.hpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <cib/built.hpp>
|
||||
#include <flow/common.hpp>
|
||||
|
||||
namespace flow {
|
||||
/**
|
||||
* Run the flow given by 'Tag'.
|
||||
*
|
||||
* @tparam Tag Type of the flow to be ran. This is the name of the flow::builder
|
||||
* used to declare and build the flow.
|
||||
*/
|
||||
template <typename Tag> FunctionPtr &run = cib::service<Tag>;
|
||||
} // namespace flow
|
||||
95
Chapter17/cib/libs/flow/step.hpp
Normal file
95
Chapter17/cib/libs/flow/step.hpp
Normal file
@@ -0,0 +1,95 @@
|
||||
#pragma once
|
||||
|
||||
#include <cib/detail/runtime_conditional.hpp>
|
||||
#include <cib/func_decl.hpp>
|
||||
#include <flow/common.hpp>
|
||||
#include <flow/log.hpp>
|
||||
#include <flow/subgraph_identity.hpp>
|
||||
#include <sc/string_constant.hpp>
|
||||
|
||||
#include <stdx/compiler.hpp>
|
||||
#include <stdx/ct_string.hpp>
|
||||
#include <stdx/type_traits.hpp>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace flow {
|
||||
template <stdx::ct_string Type, stdx::ct_string Name,
|
||||
subgraph_identity Identity, typename Cond, typename F>
|
||||
struct ct_node {
|
||||
using is_subgraph = void;
|
||||
using type_t =
|
||||
decltype(stdx::ct_string_to_type<Type, sc::string_constant>());
|
||||
using name_t =
|
||||
decltype(stdx::ct_string_to_type<Name, sc::string_constant>());
|
||||
using func_t = F;
|
||||
|
||||
constexpr static auto ct_name = Name;
|
||||
constexpr static auto identity = Identity;
|
||||
constexpr static auto condition = Cond{};
|
||||
|
||||
constexpr auto operator*() const {
|
||||
if constexpr (Identity == subgraph_identity::REFERENCE) {
|
||||
return ct_node<Type, Name, subgraph_identity::VALUE, Cond, F>{};
|
||||
} else {
|
||||
return ct_node{};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template <stdx::ct_string Type, stdx::ct_string Name, typename F>
|
||||
[[nodiscard]] constexpr auto make_node() {
|
||||
return ct_node<Type, Name, subgraph_identity::REFERENCE,
|
||||
cib::detail::always_condition_t, F>{};
|
||||
}
|
||||
|
||||
constexpr auto empty_func = []() {};
|
||||
} // namespace detail
|
||||
|
||||
template <stdx::ct_string Name, typename F>
|
||||
requires(stdx::is_function_object_v<F> and std::is_empty_v<F>)
|
||||
[[nodiscard]] constexpr auto action(F const &) {
|
||||
return detail::make_node<"action", Name, F>();
|
||||
}
|
||||
|
||||
template <stdx::ct_string Name> [[nodiscard]] constexpr auto action() {
|
||||
return action<Name>(cib::func_decl<Name>);
|
||||
}
|
||||
|
||||
template <stdx::ct_string Name> [[nodiscard]] constexpr auto step() {
|
||||
return action<Name>(cib::func_decl<Name>);
|
||||
}
|
||||
|
||||
template <stdx::ct_string Name> [[nodiscard]] constexpr auto milestone() {
|
||||
return detail::make_node<"milestone", Name, decltype(detail::empty_func)>();
|
||||
}
|
||||
|
||||
inline namespace literals {
|
||||
template <stdx::ct_string S> [[nodiscard]] constexpr auto operator""_action() {
|
||||
return action<S>();
|
||||
}
|
||||
|
||||
template <stdx::ct_string S> [[nodiscard]] constexpr auto operator""_step() {
|
||||
return action<S>();
|
||||
}
|
||||
|
||||
template <stdx::ct_string S>
|
||||
[[nodiscard]] constexpr auto operator""_milestone() {
|
||||
return milestone<S>();
|
||||
}
|
||||
} // namespace literals
|
||||
|
||||
template <typename Cond, stdx::ct_string Type, stdx::ct_string Name,
|
||||
subgraph_identity Identity, typename NodeCond, typename F>
|
||||
constexpr auto
|
||||
make_runtime_conditional(Cond, ct_node<Type, Name, Identity, NodeCond, F>) {
|
||||
if constexpr (Identity == subgraph_identity::VALUE) {
|
||||
return ct_node<Type, Name, Identity, decltype(NodeCond{} and Cond{}),
|
||||
F>{};
|
||||
} else {
|
||||
return ct_node<Type, Name, Identity, NodeCond, F>{};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace flow
|
||||
5
Chapter17/cib/libs/flow/subgraph_identity.hpp
Normal file
5
Chapter17/cib/libs/flow/subgraph_identity.hpp
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace flow {
|
||||
enum class subgraph_identity { VALUE, REFERENCE };
|
||||
}
|
||||
Reference in New Issue
Block a user