diff --git a/include/flow/graph_builder.hpp b/include/flow/graph_builder.hpp index 24f0a2dd..7bde6491 100644 --- a/include/flow/graph_builder.hpp +++ b/include/flow/graph_builder.hpp @@ -149,7 +149,14 @@ struct graph_builder { constexpr auto built = build(v); static_assert(built.has_value(), "Topological sort failed: cycle in flow"); - return *built; + + constexpr auto functionPtrs = built->functionPtrs; + constexpr auto size = functionPtrs.size(); + constexpr auto name = built->name; + + return [&](std::index_sequence) { + return detail::inlined_func_list{}; + }(std::make_index_sequence{}); } constexpr static auto run() { built()(); } diff --git a/include/flow/impl.hpp b/include/flow/impl.hpp index 56df34e7..203ee0ac 100644 --- a/include/flow/impl.hpp +++ b/include/flow/impl.hpp @@ -13,11 +13,6 @@ #include namespace flow { -// NOLINTNEXTLINE(cppcoreguidelines-virtual-class-destructor) -struct interface { - virtual auto operator()() const -> void {} -}; - /** * flow::impl is a constant representation of a series of Milestones and actions * to be executed in a specific order. @@ -35,8 +30,7 @@ struct interface { * * @see flow::builder */ -template -class impl : public interface { +template class impl { private: constexpr static bool loggingEnabled = not Name.empty(); @@ -48,12 +42,14 @@ class impl : public interface { } }(); + public: stdx::cx_vector functionPtrs{}; - public: using node_t = rt_node; constexpr static bool active = capacity > 0; + constexpr static auto name = Name; + /** * Create a new flow::impl of Milestones. * @@ -77,24 +73,29 @@ class impl : public interface { [](auto const &milestone) { return milestone.run; }); } } +}; + +namespace detail { +template struct inlined_func_list { + constexpr static auto active = sizeof...(FuncPtrs) > 0; + + __attribute__((flatten, always_inline)) auto operator()() const -> void { + constexpr static bool loggingEnabled = not Name.empty(); - /** - * Execute the entire flow in order. - */ - __attribute__((flatten)) auto operator()() const -> void final { constexpr auto name = stdx::ct_string_to_type(); + if constexpr (loggingEnabled) { CIB_TRACE("flow.start({})", name); } - [this](std::index_sequence) { - (functionPtrs[Is](), ...); - }(std::make_index_sequence{}); + (FuncPtrs(), ...); if constexpr (loggingEnabled) { CIB_TRACE("flow.end({})", name); } } }; +} // namespace detail + } // namespace flow diff --git a/include/flow/step.hpp b/include/flow/step.hpp index e365f516..56483beb 100644 --- a/include/flow/step.hpp +++ b/include/flow/step.hpp @@ -27,16 +27,20 @@ template struct ct_node : rt_node { decltype(stdx::ct_string_to_type()); }; +template +static void log_name_func() { + CIB_TRACE("flow.{}({})", + stdx::ct_string_to_type(), + stdx::ct_string_to_type()); +} + namespace detail { template [[nodiscard]] constexpr auto make_node() { - return ct_node{ - {.run = F{}, .log_name = [] { - CIB_TRACE("flow.{}({})", - stdx::ct_string_to_type(), - stdx::ct_string_to_type()); - }}}; + return ct_node{{.run = F{}, .log_name = log_name_func}}; } + +constexpr auto empty_func = []() {}; } // namespace detail template @@ -54,7 +58,7 @@ template [[nodiscard]] constexpr auto step() { } template [[nodiscard]] constexpr auto milestone() { - return detail::make_node(); + return detail::make_node(); } inline namespace literals { diff --git a/test/flow/graph_builder.cpp b/test/flow/graph_builder.cpp index a3131abe..5feeb342 100644 --- a/test/flow/graph_builder.cpp +++ b/test/flow/graph_builder.cpp @@ -19,94 +19,112 @@ constexpr auto d = flow::action<"d">([] { actual += "d"; }); using builder = flow::graph_builder; } // namespace +struct empty_flow { + constexpr static auto value = flow::graph<>{}; +}; TEST_CASE("build and run empty flow", "[graph_builder]") { - auto g = flow::graph<>{}; - auto const flow = builder::build(g); - REQUIRE(flow.has_value()); - flow.value()(); + constexpr auto f = builder::render(); + f(); } +struct single_action { + constexpr static auto value = flow::graph<>{}.add(a); +}; TEST_CASE("add single action", "[graph_builder]") { actual.clear(); - auto g = flow::graph<>{}.add(a); - auto const flow = builder::build(g); - REQUIRE(flow.has_value()); - flow.value()(); + constexpr auto f = builder::render(); + f(); CHECK(actual == "a"); } +struct two_milestone_linear_before { + constexpr static auto value = flow::graph<>{}.add(a >> milestone0); +}; TEST_CASE("two milestone linear before dependency", "[graph_builder]") { actual.clear(); - auto g = flow::graph<>{}.add(a >> milestone0); - auto const flow = builder::build(g); - REQUIRE(flow.has_value()); - flow.value()(); + constexpr auto f = builder::render(); + f(); CHECK(actual == "a"); } +struct actions_get_executed_once { + constexpr static auto value = flow::graph<>{} + .add(a >> milestone0) + .add(a >> milestone1) + .add(milestone0 >> milestone1); +}; TEST_CASE("actions get executed once", "[graph_builder]") { actual.clear(); - auto g = flow::graph<>{} - .add(a >> milestone0) - .add(a >> milestone1) - .add(milestone0 >> milestone1); - auto const flow = builder::build(g); - flow.value()(); + constexpr auto f = builder::render(); + f(); CHECK(actual == "a"); } +struct two_milestone_linear_after_dependency { + constexpr static auto value = flow::graph<>{} + .add(a >> milestone0) + .add(milestone0 >> milestone1) + .add(milestone0 >> b >> milestone1); +}; TEST_CASE("two milestone linear after dependency", "[graph_builder]") { actual.clear(); - auto g = flow::graph<>{} - .add(a >> milestone0) - .add(milestone0 >> milestone1) - .add(milestone0 >> b >> milestone1); - auto const flow = builder::build(g); - flow.value()(); + constexpr auto f = builder::render(); + f(); CHECK(actual == "ab"); } +struct three_milestone_linear_before_and_after_dependency { + constexpr static auto value = flow::graph<>{}.add(a >> b >> c); +}; TEST_CASE("three milestone linear before and after dependency", "[graph_builder]") { actual.clear(); - auto g = flow::graph<>{}.add(a >> b >> c); - auto const flow = builder::build(g); - flow.value()(); + constexpr auto f = + builder::render(); + f(); CHECK(actual == "abc"); } +struct just_two_actions_in_order { + constexpr static auto value = flow::graph<>{}.add(a >> b); +}; TEST_CASE("just two actions in order", "[graph_builder]") { actual.clear(); - auto g = flow::graph<>{}.add(a >> b); - auto const flow = builder::build(g); - flow.value()(); + constexpr auto f = builder::render(); + f(); CHECK(actual == "ab"); } +struct insert_action_between_two_actions { + constexpr static auto value = flow::graph<>{}.add(a >> c).add(a >> b >> c); +}; TEST_CASE("insert action between two actions", "[graph_builder]") { actual.clear(); - auto g = flow::graph<>{}.add(a >> c).add(a >> b >> c); - auto const flow = builder::build(g); - flow.value()(); + constexpr auto f = builder::render(); + f(); CHECK(actual == "abc"); } +struct add_single_parallel_2 { + constexpr static auto value = flow::graph<>{}.add(a && b); +}; TEST_CASE("add single parallel 2", "[graph_builder]") { actual.clear(); - auto g = flow::graph<>{}.add(a && b); - auto const flow = builder::build(g); - flow.value()(); + constexpr auto f = builder::render(); + f(); CHECK(actual.find('a') != std::string::npos); CHECK(actual.find('b') != std::string::npos); CHECK(actual.size() == 2); } +struct add_single_parallel_3 { + constexpr static auto value = flow::graph<>{}.add(a && b && c); +}; TEST_CASE("add single parallel 3", "[graph_builder]") { actual.clear(); - auto g = flow::graph<>{}.add(a && b && c); - auto const flow = builder::build(g); - flow.value()(); + constexpr auto f = builder::render(); + f(); CHECK(actual.find('a') != std::string::npos); CHECK(actual.find('b') != std::string::npos); @@ -114,11 +132,14 @@ TEST_CASE("add single parallel 3", "[graph_builder]") { CHECK(actual.size() == 3); } +struct add_single_parallel_3_with_later_dependency_1 { + constexpr static auto value = flow::graph<>{}.add(a && b && c).add(c >> a); +}; TEST_CASE("add single parallel 3 with later dependency 1", "[graph_builder]") { actual.clear(); - auto g = flow::graph<>{}.add(a && b && c).add(c >> a); - auto const flow = builder::build(g); - flow.value()(); + constexpr auto f = + builder::render(); + f(); CHECK(actual.find('a') != std::string::npos); CHECK(actual.find('b') != std::string::npos); @@ -127,11 +148,14 @@ TEST_CASE("add single parallel 3 with later dependency 1", "[graph_builder]") { CHECK(actual.size() == 3); } +struct add_single_parallel_3_with_later_dependency_2 { + constexpr static auto value = flow::graph<>{}.add(a && b && c).add(a >> c); +}; TEST_CASE("add single parallel 3 with later dependency 2", "[graph_builder]") { actual.clear(); - auto g = flow::graph<>{}.add(a && b && c).add(a >> c); - auto const flow = builder::build(g); - flow.value()(); + constexpr auto f = + builder::render(); + f(); CHECK(actual.find('a') != std::string::npos); CHECK(actual.find('b') != std::string::npos); @@ -140,11 +164,13 @@ TEST_CASE("add single parallel 3 with later dependency 2", "[graph_builder]") { CHECK(actual.size() == 3); } +struct add_parallel_rhs { + constexpr static auto value = flow::graph<>{}.add(a >> (b && c)); +}; TEST_CASE("add parallel rhs", "[graph_builder]") { actual.clear(); - auto g = flow::graph<>{}.add(a >> (b && c)); - auto const flow = builder::build(g); - flow.value()(); + constexpr auto f = builder::render(); + f(); CHECK(actual.find('a') != std::string::npos); CHECK(actual.find('b') != std::string::npos); @@ -154,11 +180,13 @@ TEST_CASE("add parallel rhs", "[graph_builder]") { CHECK(actual.size() == 3); } +struct add_parallel_lhs { + constexpr static auto value = flow::graph<>{}.add((a && b) >> c); +}; TEST_CASE("add parallel lhs", "[graph_builder]") { actual.clear(); - auto g = flow::graph<>{}.add((a && b) >> c); - auto const flow = builder::build(g); - flow.value()(); + constexpr auto f = builder::render(); + f(); CHECK(actual.find('a') != std::string::npos); CHECK(actual.find('b') != std::string::npos); @@ -168,11 +196,13 @@ TEST_CASE("add parallel lhs", "[graph_builder]") { CHECK(actual.size() == 3); } +struct add_parallel_in_the_middle { + constexpr static auto value = flow::graph<>{}.add(a >> (b && c) >> d); +}; TEST_CASE("add parallel in the middle", "[graph_builder]") { actual.clear(); - auto g = flow::graph<>{}.add(a >> (b && c) >> d); - auto const flow = builder::build(g); - flow.value()(); + constexpr auto f = builder::render(); + f(); CHECK(actual.find('a') != std::string::npos); CHECK(actual.find('b') != std::string::npos); @@ -188,11 +218,13 @@ TEST_CASE("add parallel in the middle", "[graph_builder]") { CHECK(actual.size() == 4); } +struct add_dependency_lhs { + constexpr static auto value = flow::graph<>{}.add((a >> b) && c); +}; TEST_CASE("add dependency lhs", "[graph_builder]") { actual.clear(); - auto g = flow::graph<>{}.add((a >> b) && c); - auto const flow = builder::build(g); - flow.value()(); + constexpr auto f = builder::render(); + f(); CHECK(actual.find('a') != std::string::npos); CHECK(actual.find('b') != std::string::npos); @@ -203,11 +235,13 @@ TEST_CASE("add dependency lhs", "[graph_builder]") { CHECK(actual.size() == 3); } +struct add_dependency_rhs { + constexpr static auto value = flow::graph<>{}.add(a && (b >> c)); +}; TEST_CASE("add dependency rhs", "[graph_builder]") { actual.clear(); - auto g = flow::graph<>{}.add(a && (b >> c)); - auto const flow = builder::build(g); - flow.value()(); + constexpr auto f = builder::render(); + f(); CHECK(actual.find('a') != std::string::npos); CHECK(actual.find('b') != std::string::npos);