Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion include/flow/graph_builder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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::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()(); }
Expand Down
31 changes: 16 additions & 15 deletions include/flow/impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,6 @@
#include <type_traits>

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.
Expand All @@ -35,8 +30,7 @@ struct interface {
*
* @see flow::builder
*/
template <stdx::ct_string Name, std::size_t NumSteps>
class impl : public interface {
template <stdx::ct_string Name, std::size_t NumSteps> class impl {
private:
constexpr static bool loggingEnabled = not Name.empty();

Expand All @@ -48,12 +42,14 @@ class impl : public interface {
}
}();

public:
stdx::cx_vector<FunctionPtr, capacity> 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.
*
Expand All @@ -77,24 +73,29 @@ class impl : public interface {
[](auto const &milestone) { return milestone.run; });
}
}
};

namespace detail {
template <stdx::ct_string Name, auto... FuncPtrs> 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<Name, sc::string_constant>();

if constexpr (loggingEnabled) {
CIB_TRACE("flow.start({})", name);
}

[this]<std::size_t... Is>(std::index_sequence<Is...>) {
(functionPtrs[Is](), ...);
}(std::make_index_sequence<capacity>{});
(FuncPtrs(), ...);

if constexpr (loggingEnabled) {
CIB_TRACE("flow.end({})", name);
}
}
};
} // namespace detail

} // namespace flow
18 changes: 11 additions & 7 deletions include/flow/step.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,20 @@ template <stdx::ct_string Name> struct ct_node : rt_node {
decltype(stdx::ct_string_to_type<Name, sc::string_constant>());
};

template <stdx::ct_string Name, stdx::ct_string Type>
static void log_name_func() {
CIB_TRACE("flow.{}({})",
stdx::ct_string_to_type<Type, sc::string_constant>(),
stdx::ct_string_to_type<Name, sc::string_constant>());
}

namespace detail {
template <stdx::ct_string Name, stdx::ct_string Type, typename F>
[[nodiscard]] constexpr auto make_node() {
return ct_node<Name>{
{.run = F{}, .log_name = [] {
CIB_TRACE("flow.{}({})",
stdx::ct_string_to_type<Type, sc::string_constant>(),
stdx::ct_string_to_type<Name, sc::string_constant>());
}}};
return ct_node<Name>{{.run = F{}, .log_name = log_name_func<Name, Type>}};
}

constexpr auto empty_func = []() {};
} // namespace detail

template <stdx::ct_string Name, typename F>
Expand All @@ -54,7 +58,7 @@ template <stdx::ct_string Name> [[nodiscard]] constexpr auto step() {
}

template <stdx::ct_string Name> [[nodiscard]] constexpr auto milestone() {
return detail::make_node<Name, "milestone", decltype([] {})>();
return detail::make_node<Name, "milestone", decltype(detail::empty_func)>();
}

inline namespace literals {
Expand Down
154 changes: 94 additions & 60 deletions test/flow/graph_builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,106 +19,127 @@ constexpr auto d = flow::action<"d">([] { actual += "d"; });
using builder = flow::graph_builder<flow::impl>;
} // 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<empty_flow>();
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<single_action>();
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<two_milestone_linear_before>();
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<actions_get_executed_once>();
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<two_milestone_linear_after_dependency>();
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<three_milestone_linear_before_and_after_dependency>();
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<just_two_actions_in_order>();
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<insert_action_between_two_actions>();
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<add_single_parallel_2>();
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<add_single_parallel_3>();
f();

CHECK(actual.find('a') != std::string::npos);
CHECK(actual.find('b') != std::string::npos);
CHECK(actual.find('c') != std::string::npos);
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<add_single_parallel_3_with_later_dependency_1>();
f();

CHECK(actual.find('a') != std::string::npos);
CHECK(actual.find('b') != std::string::npos);
Expand All @@ -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<add_single_parallel_3_with_later_dependency_2>();
f();

CHECK(actual.find('a') != std::string::npos);
CHECK(actual.find('b') != std::string::npos);
Expand All @@ -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<add_parallel_rhs>();
f();

CHECK(actual.find('a') != std::string::npos);
CHECK(actual.find('b') != std::string::npos);
Expand All @@ -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<add_parallel_lhs>();
f();

CHECK(actual.find('a') != std::string::npos);
CHECK(actual.find('b') != std::string::npos);
Expand All @@ -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<add_parallel_in_the_middle>();
f();

CHECK(actual.find('a') != std::string::npos);
CHECK(actual.find('b') != std::string::npos);
Expand All @@ -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<add_dependency_lhs>();
f();

CHECK(actual.find('a') != std::string::npos);
CHECK(actual.find('b') != std::string::npos);
Expand All @@ -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<add_dependency_rhs>();
f();

CHECK(actual.find('a') != std::string::npos);
CHECK(actual.find('b') != std::string::npos);
Expand Down