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
26 changes: 16 additions & 10 deletions include/flow/graph_builder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,13 @@
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,
Expand Down Expand Up @@ -56,12 +62,14 @@ template <stdx::ct_string Name,
template <stdx::ct_string, std::size_t> typename Impl>
struct graph_builder {
// NOLINTBEGIN(readability-function-cognitive-complexity)
template <typename Node, std::size_t N, std::size_t E>
template <typename Output, std::size_t N, std::size_t E>
[[nodiscard]] constexpr static auto make_graph(auto const &nodes,
auto const &edges) {
using graph_t = stdx::cx_multimap<Node, Node, N, E>;
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([&](auto const &node) { g.put(node); }, nodes);
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(
Expand Down Expand Up @@ -95,7 +103,7 @@ struct graph_builder {
},
node_ps);

g.put(lhs, rhs);
g.put(Output::create_node(lhs), Output::create_node(rhs));
},
edges);
return g;
Expand Down Expand Up @@ -212,18 +220,16 @@ struct graph_builder {
constexpr auto edge_capacity = edge_size(node_set, edges);

using output_t = Impl<Graph::name, node_capacity>;
using rt_node_t = typename output_t::node_t;

static_assert(
all_of(
[]<typename N>(N const &) {
return std::is_convertible_v<N, rt_node_t>;
return detail::is_output_compatible<N, output_t>;
},
node_set),
"Output node type is not compatible with given input nodes");

auto g = make_graph<rt_node_t, node_capacity, edge_capacity>(node_set,
edges);
auto g =
make_graph<output_t, node_capacity, edge_capacity>(node_set, edges);
return topo_sort<output_t>(g);
}

Expand Down
59 changes: 48 additions & 11 deletions include/flow/impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <flow/step.hpp>
#include <log/log.hpp>

#include <stdx/ct_string.hpp>
#include <stdx/cx_vector.hpp>
#include <stdx/span.hpp>

Expand All @@ -13,6 +14,31 @@
#include <type_traits>

namespace flow {
namespace detail {
template <typename CTNode> constexpr auto run_func() -> void {
if (CTNode::condition) {
typename CTNode::func_t{}();
}
}

template <typename Flow, typename CTNode> constexpr auto log_func() -> void {
if (CTNode::condition) {
using log_spec_t = decltype(get_log_spec<CTNode, Flow>());
CIB_LOG(typename log_spec_t::flavor, log_spec_t::level, "flow.{}({})",
typename CTNode::type_t{}, typename CTNode::name_t{});
}
}
} // namespace detail

struct rt_node {
FunctionPtr run{};
FunctionPtr log_name{};

private:
friend constexpr auto operator==(rt_node const &,
rt_node const &) -> bool = default;
};

/**
* flow::impl is a constant representation of a series of Milestones and actions
* to be executed in a specific order.
Expand Down Expand Up @@ -45,11 +71,18 @@ template <stdx::ct_string Name, std::size_t NumSteps> class impl {
public:
stdx::cx_vector<FunctionPtr, capacity> functionPtrs{};

using node_t = rt_node;
constexpr static bool active = capacity > 0;

constexpr static auto name = Name;

using node_t = rt_node;

template <typename CTNode>
constexpr static auto create_node(CTNode) -> node_t {
constexpr auto rf = detail::run_func<CTNode>;
constexpr auto lf = detail::log_func<log_spec_id_t<Name>, CTNode>;
return node_t{rf, lf};
}

/**
* Create a new flow::impl of Milestones.
*
Expand All @@ -60,24 +93,24 @@ template <stdx::ct_string Name, std::size_t NumSteps> class impl {
*
* @see flow::builder
*/
constexpr explicit(true)
impl(stdx::span<node_t const, NumSteps> newMilestones) {
constexpr explicit(true) impl(stdx::span<node_t const, NumSteps> steps) {
if constexpr (loggingEnabled) {
for (auto const &milestone : newMilestones) {
functionPtrs.push_back(milestone.log_name);
functionPtrs.push_back(milestone.run);
for (auto const &step : steps) {
functionPtrs.push_back(step.log_name);
functionPtrs.push_back(step.run);
}
} else {
std::transform(std::cbegin(newMilestones), std::cend(newMilestones),
std::transform(std::cbegin(steps), std::cend(steps),
std::back_inserter(functionPtrs),
[](auto const &milestone) { return milestone.run; });
[](auto const &step) { return step.run; });
}
}
};

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();
Expand All @@ -86,13 +119,17 @@ template <stdx::ct_string Name, auto... FuncPtrs> struct inlined_func_list {
stdx::ct_string_to_type<Name, sc::string_constant>();

if constexpr (loggingEnabled) {
CIB_TRACE("flow.start({})", name);
using log_spec_t = decltype(get_log_spec<inlined_func_list>());
CIB_LOG(typename log_spec_t::flavor, log_spec_t::level,
"flow.start({})", name);
}

(FuncPtrs(), ...);

if constexpr (loggingEnabled) {
CIB_TRACE("flow.end({})", name);
using log_spec_t = decltype(get_log_spec<inlined_func_list>());
CIB_LOG(typename log_spec_t::flavor, log_spec_t::level,
"flow.end({})", name);
}
}
};
Expand Down
36 changes: 36 additions & 0 deletions include/flow/log.hpp
Original file line number Diff line number Diff line change
@@ -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
31 changes: 3 additions & 28 deletions include/flow/step.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
#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 <log/log.hpp>
#include <sc/string_constant.hpp>

#include <stdx/compiler.hpp>
Expand All @@ -14,45 +14,20 @@
#include <type_traits>

namespace flow {
struct rt_node {
FunctionPtr run;
FunctionPtr log_name;

private:
friend constexpr auto operator==(rt_node const &,
rt_node const &) -> bool = default;
};

template <stdx::ct_string Type, stdx::ct_string Name,
subgraph_identity Identity, typename Cond, typename F>
struct ct_node : rt_node {
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 static auto run_func = [] {
if (condition) {
F{}();
}
};

constexpr static auto log_func = [] {
if (condition) {
CIB_TRACE("flow.{}({})", type_t{}, name_t{});
}
};

constexpr ct_node() : rt_node{run_func, log_func} {}

constexpr auto operator*() const {
if constexpr (Identity == subgraph_identity::REFERENCE) {
return ct_node<Type, Name, subgraph_identity::VALUE, Cond, F>{};
Expand Down
5 changes: 5 additions & 0 deletions include/seq/impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ template <stdx::ct_string, std::size_t NumSteps> struct impl {

using node_t = rt_step;

template <typename CTNode>
constexpr static auto create_node(CTNode n) -> node_t {
return n;
}

constexpr explicit(true) impl(stdx::span<node_t const, NumSteps> steps) {
for (auto const &step : steps) {
_forward_steps.push_back(step.forward_ptr);
Expand Down
6 changes: 3 additions & 3 deletions test/flow/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
add_unit_test(
"flow_flow_test"
"flow_separate_actions_test"
CATCH2
FILES
"flow.cpp"
"flow_cib_func.cpp"
"separate_actions.cpp"
LIBRARIES
warnings
cib)

add_tests(graph graph_builder)
add_tests(flow graph graph_builder logging log_levels custom_log_levels)

add_subdirectory(fail)
96 changes: 96 additions & 0 deletions test/flow/custom_log_levels.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#include <cib/cib.hpp>
#include <flow/flow.hpp>

#include <catch2/catch_test_macros.hpp>

#include <string>
#include <vector>

namespace {
using namespace flow::literals;

template <auto... Cs> struct wrapper {
struct inner {
constexpr static auto config = cib::config(Cs...);
};
constexpr static auto n = cib::nexus<inner>{};

wrapper() { n.init(); }

template <typename S> static auto run() -> void { n.template service<S>(); }
};

std::vector<logging::level> log_calls{};

template <typename S, auto... Cs>
constexpr auto run_flow = []() -> void {
log_calls.clear();
wrapper<Cs...>::template run<S>();
};

struct TestFlowA : public flow::service<"A"> {};
struct TestFlowB : public flow::service<"B"> {};
struct TestFlowC : public flow::service<"C"> {};

constexpr auto msA = flow::milestone<"msA">();
constexpr auto msB = flow::milestone<"msB">();

struct log_config {
struct log_handler {
template <logging::level Level, typename ModuleId,
typename FilenameStringType, typename LineNumberType,
typename MsgType>
auto log(FilenameStringType, LineNumberType, MsgType const &) -> void {
log_calls.push_back(Level);
}
};
log_handler logger;
};
} // namespace

template <> inline auto logging::config<> = log_config{};

struct user1_log_spec : flow::default_log_spec {
constexpr static auto level = logging::level::USER1;
};
struct user2_log_spec : flow::default_log_spec {
constexpr static auto level = logging::level::USER2;
};
struct info_log_spec : flow::default_log_spec {
constexpr static auto level = logging::level::INFO;
};
template <> constexpr auto flow::log_spec<"default"> = info_log_spec{};

TEST_CASE("override default log level", "[flow_custom_log_levels]") {
run_flow<TestFlowA, cib::exports<TestFlowA>,
cib::extend<TestFlowA>(*msA)>();

REQUIRE(not log_calls.empty());
std::for_each(std::begin(log_calls), std::end(log_calls),
[](auto level) { CHECK(level == logging::level::INFO); });
}

template <> constexpr auto flow::log_spec<"B"> = user1_log_spec{};
template <> constexpr auto flow::log_spec<"msB"> = user2_log_spec{};

TEST_CASE("override log level by name", "[flow_custom_log_levels]") {
run_flow<TestFlowB, cib::exports<TestFlowB>,
cib::extend<TestFlowB>(*msB)>();

REQUIRE(log_calls.size() == 3);
CHECK(log_calls[0] == logging::level::USER1);
CHECK(log_calls[1] == logging::level::USER2);
CHECK(log_calls[2] == logging::level::USER1);
}

template <> constexpr auto flow::log_spec<"C"> = user1_log_spec{};

TEST_CASE("default log spec for step will use overridden log spec for flow",
"[flow_custom_log_levels]") {
run_flow<TestFlowC, cib::exports<TestFlowC>,
cib::extend<TestFlowC>(*msA)>();

REQUIRE(log_calls.size() == 3);
std::for_each(std::begin(log_calls), std::end(log_calls),
[](auto level) { CHECK(level == logging::level::USER1); });
}
Loading