Skip to content

Commit

Permalink
add runtime_conditional and flow unary * operator
Browse files Browse the repository at this point in the history
  • Loading branch information
lukevalenty committed Sep 4, 2024
1 parent 8bcedee commit 3e78b7b
Show file tree
Hide file tree
Showing 24 changed files with 934 additions and 306 deletions.
73 changes: 62 additions & 11 deletions docs/flows.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@ Actions are added to the flow inside a component's `cib::config`.
struct morning {
constexpr auto config = cib::config(
cib::extend<MorningRoutine>(
WAKE_UP >>
selfcare::SHOWER >>
selfcare::GET_DRESSED >>
food::MAKE_COFFEE >>
food::DRINK_COFFEE));
*WAKE_UP >>
*selfcare::SHOWER >>
*selfcare::GET_DRESSED >>
*food::MAKE_COFFEE >>
*food::DRINK_COFFEE));
};
----

Expand All @@ -58,6 +58,17 @@ a shower. The flow library will order actions in a flow to respect these
dependencies. The actions will be executed in an order that respects all given
dependencies.

The `*` operator is used to explicitly add an action to the
flow. Without the `*` operator an action is just a reference.
A compile-time error will be triggered if an action is referenced without ever
being explicitly added to the flow. If an action is added under a compile-time
or runtime conditional, and the conditional is false, then it is as if the
action was never added at all.

The behavior of the `*` operator ensures that merely referencing an
action to create an ordering dependency doesn't unintentionally add the action
to the flow.

If we only use the `morning` component in our project, the `MorningRoutine` flow
graph would look like the following:

Expand Down Expand Up @@ -93,11 +104,11 @@ struct childcare {
constexpr auto config = cib::config(
cib::extend<MorningRoutine>(
food::MAKE_COFFEE >> // this step exists in the MorningRoutine flow
PACK_SCHOOL_LUNCHES >> // new
*PACK_SCHOOL_LUNCHES >> // new
food::DRINK_COFFEE >> // existing
food::MAKE_BREAKFAST >> // new
food::EAT_BREAKFAST >> // new
SEND_KIDS_TO_SCHOOL)); // new
*food::MAKE_BREAKFAST >> // new
*food::EAT_BREAKFAST >> // new
*SEND_KIDS_TO_SCHOOL)); // new
};
----

Expand Down Expand Up @@ -140,7 +151,7 @@ struct exercise {
constexpr auto config = cib::config(
cib::extend<MorningRoutine>(
morning::WAKE_UP >>
RIDE_STATIONARY_BIKE >>
*RIDE_STATIONARY_BIKE >>
selfcare::SHOWER));
};
----
Expand Down Expand Up @@ -321,7 +332,47 @@ namespace example_component {
constexpr auto config = cib::config(
cib::extend<MyFlow>(
// no order requirement between these actions
SOME_ACTION && SOME_OTHER_ACTION));
*SOME_ACTION && *SOME_OTHER_ACTION));
}
----

==== `operator*`

Explicitly add an action to the flow. Actions used in flow extensions without
the `*` will be treated as references only and will not be added to the
flow at that location. It is a compilation error if an action is not added
with a `*` in exactly one location in the overall config.

Actions can be added and ordered all at once:

[source,cpp]
----
namespace example_component {
constexpr auto config = cib::config(
cib::extend<MyFlow>(
// Add both actions and create an ordering between them.
*SOME_ACTION >> *SOME_OTHER_ACTION));
}
----

Actions can also be added and ordered seperately:

[source,cpp]
----
namespace other_component {
constexpr auto INIT_SOMETHING = ...
constexpr auto config = cib::config(
cib::extend<MyFlow>(*INIT_SOMETHING));
}
namespace example_component {
constexpr auto DO_A_THING = ...
constexpr auto config = cib::config(
cib::extend<MyFlow>(
other_component::INIT_SOMETHING >>
*DO_A_THING));
}
----

Expand Down
32 changes: 12 additions & 20 deletions examples/flow_daily_routine/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,10 @@ struct self_care_component_t {
// Extend flow services
constexpr static auto config = cib::config(

cib::extend<morning_routine_t>(self_care_component_t::WAKE_UP >>
self_care_component_t::EXERCISE >>
self_care_component_t::TAKE_BATH),
cib::extend<morning_routine_t>(*WAKE_UP >> *EXERCISE >> *TAKE_BATH),

cib::extend<evening_routine_t>(self_care_component_t::EXERCISE >>
self_care_component_t::TAKE_BATH >>
self_care_component_t::RELAX >>
self_care_component_t::GO_TO_BED));
cib::extend<evening_routine_t>(*EXERCISE >> *TAKE_BATH >> *RELAX >>
*GO_TO_BED));
};

struct food_component_t {
Expand All @@ -119,10 +115,10 @@ struct food_component_t {
constexpr static auto config = cib::config(

cib::extend<morning_routine_t>(self_care_component_t::TAKE_BATH >>
food_component_t::BREAKFAST),
*BREAKFAST),

cib::extend<evening_routine_t>(self_care_component_t::RELAX >>
food_component_t::DINNER >>
*DINNER >>
self_care_component_t::GO_TO_BED));
};

Expand All @@ -141,16 +137,13 @@ struct dress_up_component_t {
constexpr static auto config = cib::config(

cib::extend<morning_routine_t>(
self_care_component_t::WAKE_UP >>
dress_up_component_t::GET_READY_FOR_EXERCISE >>
self_care_component_t::WAKE_UP >> *GET_READY_FOR_EXERCISE >>
self_care_component_t::EXERCISE >>
self_care_component_t::TAKE_BATH >>
dress_up_component_t::GET_READY_TO_WORK >>
self_care_component_t::TAKE_BATH >> *GET_READY_TO_WORK >>
food_component_t::BREAKFAST),

cib::extend<evening_routine_t>(
dress_up_component_t::GET_READY_FOR_EXERCISE >>
self_care_component_t::EXERCISE));
cib::extend<evening_routine_t>(*GET_READY_FOR_EXERCISE >>
self_care_component_t::EXERCISE));
};

struct commute_component_t {
Expand All @@ -167,11 +160,10 @@ struct commute_component_t {
constexpr static auto config = cib::config(

cib::extend<morning_routine_t>(food_component_t::BREAKFAST >>
commute_component_t::GO_TO_OFFICE),
*GO_TO_OFFICE),

cib::extend<evening_routine_t>(
commute_component_t::RETURN_HOME >>
dress_up_component_t::GET_READY_FOR_EXERCISE));
*RETURN_HOME >> dress_up_component_t::GET_READY_FOR_EXERCISE));
};

struct daily_routine_component_t {
Expand Down Expand Up @@ -206,7 +198,7 @@ struct daily_routine_component_t {

// we need to extend the MainLoop as cib::top implements
// MainLoop service
cib::extend<cib::MainLoop>(DAILY_ROUTINES));
cib::extend<cib::MainLoop>(*DAILY_ROUTINES));
};

struct person_routine_proj {
Expand Down
8 changes: 8 additions & 0 deletions include/cib/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <cib/detail/config_item.hpp>
#include <cib/detail/exports.hpp>
#include <cib/detail/extend.hpp>
#include <cib/detail/runtime_conditional.hpp>

#include <stdx/compiler.hpp>

Expand All @@ -22,6 +23,7 @@ namespace cib {
* @see cib::extend
* @see cib::exports
* @see cib::conditional
* @see cib::runtime_conditional
*/
template <typename... Configs>
[[nodiscard]] CONSTEVAL auto config(Configs const &...configs) {
Expand Down Expand Up @@ -72,4 +74,10 @@ template <typename Predicate, typename... Configs>
Configs const &...configs) {
return detail::conditional<Predicate, Configs...>{configs...};
}

template <stdx::ct_string Name>
constexpr auto runtime_condition = []<typename P>(P) {
static_assert(std::is_default_constructible_v<P>);
return detail::runtime_condition<Name, P>{};
};
} // namespace cib
81 changes: 81 additions & 0 deletions include/cib/detail/runtime_conditional.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#pragma once

#include <cib/detail/config_details.hpp>
#include <cib/detail/config_item.hpp>
#include <cib/detail/extend.hpp>

#include <stdx/compiler.hpp>
#include <stdx/ct_format.hpp>
#include <stdx/tuple.hpp>
#include <stdx/tuple_algorithms.hpp>

namespace cib::detail {
template <typename Cond, typename... Configs>
struct runtime_conditional : config_item {
detail::config<Configs...> body;

CONSTEVAL explicit runtime_conditional(Configs const &...configs)
: body{configs...} {}

[[nodiscard]] constexpr auto extends_tuple() const {
return stdx::transform(
[]<typename E>(E e) {
auto args_tuple = stdx::transform(
[]<typename Arg>(Arg) {
return make_runtime_conditional(Cond{}, Arg{});
},
e.args_tuple);

return stdx::apply(
[]<typename... Args>(Args...) {
return extend<typename E::service_type, Args...>{
Args{}...};
},
args_tuple);
},
body.extends_tuple());
}

[[nodiscard]] constexpr auto exports_tuple() const {
return body.exports_tuple();
}
};

template <stdx::ct_string Name, typename... Ps> // FIXME: concept for Ps
struct runtime_condition {
constexpr static auto predicates = stdx::make_tuple(Ps{}...);

constexpr static auto ct_name = Name;

template <typename... Configs>
[[nodiscard]] CONSTEVAL auto operator()(Configs const &...configs) const {
return detail::runtime_conditional<runtime_condition<Name, Ps...>,
Configs...>{configs...};
}

explicit operator bool() const { return (Ps{}() and ...); }

template <stdx::ct_string RhsName, typename... RhsPs>
[[nodiscard]] constexpr auto
operator and(runtime_condition<RhsName, RhsPs...> rhs) const {
if constexpr ((sizeof...(Ps) + sizeof...(RhsPs)) == 0) {
return runtime_condition<"always">{};

} else if constexpr (sizeof...(Ps) == 0) {
return rhs;

} else if constexpr (sizeof...(RhsPs) == 0) {
return *this;

} else {
constexpr auto name =
stdx::ct_format<"{} and {}">(CX_VALUE(Name), CX_VALUE(RhsName));

return runtime_condition<name, Ps..., RhsPs...>{};
}
}
};

using always_condition_t = runtime_condition<"always">;
constexpr auto always_condition = always_condition_t{};
} // namespace cib::detail
45 changes: 40 additions & 5 deletions include/flow/detail/par.hpp
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
#pragma once

#include <flow/detail/walk.hpp>
#include <flow/subgraph_identity.hpp>

#include <stdx/tuple_algorithms.hpp>

namespace flow::dsl {
template <node Lhs, node Rhs> struct par {
template <subgraph Lhs, subgraph Rhs,
subgraph_identity Identity = subgraph_identity::REFERENCE>
struct par {
Lhs lhs;
Rhs rhs;

using is_node = void;
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) {
Expand All @@ -21,18 +28,46 @@ template <node Lhs, node Rhs> struct par {
}

friend constexpr auto tag_invoke(get_nodes_t, par const &p) {
return stdx::tuple_cat(get_nodes(p.lhs), get_nodes(p.rhs));
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([]<typename N>(N const &) { 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 <node Lhs, node Rhs> par(Lhs, Rhs) -> par<Lhs, Rhs>;
template <subgraph Lhs, subgraph Rhs> par(Lhs, Rhs) -> par<Lhs, Rhs>;
} // namespace flow::dsl

template <flow::dsl::node Lhs, flow::dsl::node Rhs>
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};
}
Loading

0 comments on commit 3e78b7b

Please sign in to comment.