Skip to content

Commit aba9e20

Browse files
committed
add runtime_conditional and flow unary * operator
1 parent 8bcedee commit aba9e20

26 files changed

+952
-312
lines changed

docs/flows.adoc

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@ Actions are added to the flow inside a component's `cib::config`.
4444
struct morning {
4545
constexpr auto config = cib::config(
4646
cib::extend<MorningRoutine>(
47-
WAKE_UP >>
48-
selfcare::SHOWER >>
49-
selfcare::GET_DRESSED >>
50-
food::MAKE_COFFEE >>
51-
food::DRINK_COFFEE));
47+
*WAKE_UP >>
48+
*selfcare::SHOWER >>
49+
*selfcare::GET_DRESSED >>
50+
*food::MAKE_COFFEE >>
51+
*food::DRINK_COFFEE));
5252
};
5353
----
5454

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

61+
The `*` operator is used to explicitly add an action to the
62+
flow. Without the `*` operator an action is just a reference.
63+
A compile-time error will be triggered if an action is referenced without ever
64+
being explicitly added to the flow. If an action is added under a compile-time
65+
or runtime conditional, and the conditional is false, then it is as if the
66+
action was never added at all.
67+
68+
The behavior of the `*` operator ensures that merely referencing an
69+
action to create an ordering dependency doesn't unintentionally add the action
70+
to the flow.
71+
6172
If we only use the `morning` component in our project, the `MorningRoutine` flow
6273
graph would look like the following:
6374

@@ -93,11 +104,11 @@ struct childcare {
93104
constexpr auto config = cib::config(
94105
cib::extend<MorningRoutine>(
95106
food::MAKE_COFFEE >> // this step exists in the MorningRoutine flow
96-
PACK_SCHOOL_LUNCHES >> // new
107+
*PACK_SCHOOL_LUNCHES >> // new
97108
food::DRINK_COFFEE >> // existing
98-
food::MAKE_BREAKFAST >> // new
99-
food::EAT_BREAKFAST >> // new
100-
SEND_KIDS_TO_SCHOOL)); // new
109+
*food::MAKE_BREAKFAST >> // new
110+
*food::EAT_BREAKFAST >> // new
111+
*SEND_KIDS_TO_SCHOOL)); // new
101112
};
102113
----
103114

@@ -140,7 +151,7 @@ struct exercise {
140151
constexpr auto config = cib::config(
141152
cib::extend<MorningRoutine>(
142153
morning::WAKE_UP >>
143-
RIDE_STATIONARY_BIKE >>
154+
*RIDE_STATIONARY_BIKE >>
144155
selfcare::SHOWER));
145156
};
146157
----
@@ -321,7 +332,47 @@ namespace example_component {
321332
constexpr auto config = cib::config(
322333
cib::extend<MyFlow>(
323334
// no order requirement between these actions
324-
SOME_ACTION && SOME_OTHER_ACTION));
335+
*SOME_ACTION && *SOME_OTHER_ACTION));
336+
}
337+
----
338+
339+
==== `operator*`
340+
341+
Explicitly add an action to the flow. Actions used in flow extensions without
342+
the `*` will be treated as references only and will not be added to the
343+
flow at that location. It is a compilation error if an action is not added
344+
with a `*` in exactly one location in the overall config.
345+
346+
Actions can be added and ordered all at once:
347+
348+
[source,cpp]
349+
----
350+
namespace example_component {
351+
constexpr auto config = cib::config(
352+
cib::extend<MyFlow>(
353+
// Add both actions and create an ordering between them.
354+
*SOME_ACTION >> *SOME_OTHER_ACTION));
355+
}
356+
----
357+
358+
Actions can also be added and ordered seperately:
359+
360+
[source,cpp]
361+
----
362+
namespace other_component {
363+
constexpr auto INIT_SOMETHING = ...
364+
365+
constexpr auto config = cib::config(
366+
cib::extend<MyFlow>(*INIT_SOMETHING));
367+
}
368+
369+
namespace example_component {
370+
constexpr auto DO_A_THING = ...
371+
372+
constexpr auto config = cib::config(
373+
cib::extend<MyFlow>(
374+
other_component::INIT_SOMETHING >>
375+
*DO_A_THING));
325376
}
326377
----
327378

examples/flow_daily_routine/main.cpp

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,10 @@ struct self_care_component_t {
9595
// Extend flow services
9696
constexpr static auto config = cib::config(
9797

98-
cib::extend<morning_routine_t>(self_care_component_t::WAKE_UP >>
99-
self_care_component_t::EXERCISE >>
100-
self_care_component_t::TAKE_BATH),
98+
cib::extend<morning_routine_t>(*WAKE_UP >> *EXERCISE >> *TAKE_BATH),
10199

102-
cib::extend<evening_routine_t>(self_care_component_t::EXERCISE >>
103-
self_care_component_t::TAKE_BATH >>
104-
self_care_component_t::RELAX >>
105-
self_care_component_t::GO_TO_BED));
100+
cib::extend<evening_routine_t>(*EXERCISE >> *TAKE_BATH >> *RELAX >>
101+
*GO_TO_BED));
106102
};
107103

108104
struct food_component_t {
@@ -119,10 +115,10 @@ struct food_component_t {
119115
constexpr static auto config = cib::config(
120116

121117
cib::extend<morning_routine_t>(self_care_component_t::TAKE_BATH >>
122-
food_component_t::BREAKFAST),
118+
*BREAKFAST),
123119

124120
cib::extend<evening_routine_t>(self_care_component_t::RELAX >>
125-
food_component_t::DINNER >>
121+
*DINNER >>
126122
self_care_component_t::GO_TO_BED));
127123
};
128124

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

143139
cib::extend<morning_routine_t>(
144-
self_care_component_t::WAKE_UP >>
145-
dress_up_component_t::GET_READY_FOR_EXERCISE >>
140+
self_care_component_t::WAKE_UP >> *GET_READY_FOR_EXERCISE >>
146141
self_care_component_t::EXERCISE >>
147-
self_care_component_t::TAKE_BATH >>
148-
dress_up_component_t::GET_READY_TO_WORK >>
142+
self_care_component_t::TAKE_BATH >> *GET_READY_TO_WORK >>
149143
food_component_t::BREAKFAST),
150144

151-
cib::extend<evening_routine_t>(
152-
dress_up_component_t::GET_READY_FOR_EXERCISE >>
153-
self_care_component_t::EXERCISE));
145+
cib::extend<evening_routine_t>(*GET_READY_FOR_EXERCISE >>
146+
self_care_component_t::EXERCISE));
154147
};
155148

156149
struct commute_component_t {
@@ -167,11 +160,10 @@ struct commute_component_t {
167160
constexpr static auto config = cib::config(
168161

169162
cib::extend<morning_routine_t>(food_component_t::BREAKFAST >>
170-
commute_component_t::GO_TO_OFFICE),
163+
*GO_TO_OFFICE),
171164

172165
cib::extend<evening_routine_t>(
173-
commute_component_t::RETURN_HOME >>
174-
dress_up_component_t::GET_READY_FOR_EXERCISE));
166+
*RETURN_HOME >> dress_up_component_t::GET_READY_FOR_EXERCISE));
175167
};
176168

177169
struct daily_routine_component_t {
@@ -206,7 +198,7 @@ struct daily_routine_component_t {
206198

207199
// we need to extend the MainLoop as cib::top implements
208200
// MainLoop service
209-
cib::extend<cib::MainLoop>(DAILY_ROUTINES));
201+
cib::extend<cib::MainLoop>(*DAILY_ROUTINES));
210202
};
211203

212204
struct person_routine_proj {

include/cib/config.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <cib/detail/config_item.hpp>
88
#include <cib/detail/exports.hpp>
99
#include <cib/detail/extend.hpp>
10+
#include <cib/detail/runtime_conditional.hpp>
1011

1112
#include <stdx/compiler.hpp>
1213

@@ -22,6 +23,7 @@ namespace cib {
2223
* @see cib::extend
2324
* @see cib::exports
2425
* @see cib::conditional
26+
* @see cib::runtime_conditional
2527
*/
2628
template <typename... Configs>
2729
[[nodiscard]] CONSTEVAL auto config(Configs const &...configs) {
@@ -72,4 +74,10 @@ template <typename Predicate, typename... Configs>
7274
Configs const &...configs) {
7375
return detail::conditional<Predicate, Configs...>{configs...};
7476
}
77+
78+
template <stdx::ct_string Name>
79+
constexpr auto runtime_condition = []<typename P>(P) {
80+
static_assert(std::is_default_constructible_v<P>);
81+
return detail::runtime_condition<Name, P>{};
82+
};
7583
} // namespace cib
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#pragma once
2+
3+
#include <cib/detail/config_details.hpp>
4+
#include <cib/detail/config_item.hpp>
5+
#include <cib/detail/extend.hpp>
6+
7+
#include <stdx/compiler.hpp>
8+
#include <stdx/ct_format.hpp>
9+
#include <stdx/tuple.hpp>
10+
#include <stdx/tuple_algorithms.hpp>
11+
12+
namespace cib::detail {
13+
namespace poison {
14+
template <typename... Ts>
15+
constexpr auto make_runtime_conditional(Ts &&...) = delete;
16+
}
17+
18+
template <typename Cond, typename... Configs>
19+
struct runtime_conditional : config_item {
20+
detail::config<Configs...> body;
21+
22+
CONSTEVAL explicit runtime_conditional(Configs const &...configs)
23+
: body{configs...} {}
24+
25+
[[nodiscard]] constexpr auto extends_tuple() const {
26+
return stdx::transform(
27+
[]<typename E>(E e) {
28+
auto args_tuple = stdx::transform(
29+
[]<typename Arg>(Arg) {
30+
using poison::make_runtime_conditional;
31+
return make_runtime_conditional(Cond{}, Arg{});
32+
},
33+
e.args_tuple);
34+
35+
return stdx::apply(
36+
[]<typename... Args>(Args...) {
37+
return extend<typename E::service_type, Args...>{
38+
Args{}...};
39+
},
40+
args_tuple);
41+
},
42+
body.extends_tuple());
43+
}
44+
45+
[[nodiscard]] constexpr auto exports_tuple() const {
46+
return body.exports_tuple();
47+
}
48+
};
49+
50+
template <stdx::ct_string Name, typename... Ps> // FIXME: concept for Ps
51+
struct runtime_condition {
52+
constexpr static auto predicates = stdx::make_tuple(Ps{}...);
53+
54+
constexpr static auto ct_name = Name;
55+
56+
template <typename... Configs>
57+
[[nodiscard]] CONSTEVAL auto operator()(Configs const &...configs) const {
58+
return detail::runtime_conditional<runtime_condition<Name, Ps...>,
59+
Configs...>{configs...};
60+
}
61+
62+
explicit operator bool() const { return (Ps{}() and ...); }
63+
};
64+
65+
template <stdx::ct_string LhsName, typename... LhsPs, stdx::ct_string RhsName,
66+
typename... RhsPs>
67+
[[nodiscard]] constexpr auto
68+
operator and(runtime_condition<LhsName, LhsPs...> const &lhs,
69+
runtime_condition<RhsName, RhsPs...> const &rhs) {
70+
if constexpr ((sizeof...(LhsPs) + sizeof...(RhsPs)) == 0) {
71+
return runtime_condition<"always">{};
72+
73+
} else if constexpr (sizeof...(LhsPs) == 0) {
74+
return rhs;
75+
76+
} else if constexpr (sizeof...(RhsPs) == 0) {
77+
return lhs;
78+
79+
} else {
80+
constexpr auto name =
81+
stdx::ct_format<"{} and {}">(CX_VALUE(LhsName), CX_VALUE(RhsName));
82+
83+
return runtime_condition<name, LhsPs..., RhsPs...>{};
84+
}
85+
}
86+
87+
using always_condition_t = runtime_condition<"always">;
88+
constexpr auto always_condition = always_condition_t{};
89+
} // namespace cib::detail

include/flow/builder.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
namespace flow {
1111
template <stdx::ct_string Name = "">
12-
using builder = graph<Name, graph_builder<impl>>;
12+
using builder = graph<Name, graph_builder<Name, impl>>;
1313

1414
template <stdx::ct_string Name = "">
1515
struct service : cib::builder_meta<builder<Name>, FunctionPtr> {};

include/flow/detail/par.hpp

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
11
#pragma once
22

33
#include <flow/detail/walk.hpp>
4+
#include <flow/subgraph_identity.hpp>
45

56
#include <stdx/tuple_algorithms.hpp>
67

78
namespace flow::dsl {
8-
template <node Lhs, node Rhs> struct par {
9+
template <subgraph Lhs, subgraph Rhs,
10+
subgraph_identity Identity = subgraph_identity::REFERENCE>
11+
struct par {
912
Lhs lhs;
1013
Rhs rhs;
1114

12-
using is_node = void;
15+
using is_subgraph = void;
16+
17+
constexpr auto operator*() const {
18+
return par<Lhs, Rhs, subgraph_identity::VALUE>{Lhs{}, Rhs{}};
19+
}
1320

1421
private:
1522
friend constexpr auto tag_invoke(get_initials_t, par const &p) {
@@ -21,18 +28,45 @@ template <node Lhs, node Rhs> struct par {
2128
}
2229

2330
friend constexpr auto tag_invoke(get_nodes_t, par const &p) {
24-
return stdx::tuple_cat(get_nodes(p.lhs), get_nodes(p.rhs));
31+
if constexpr (Identity == subgraph_identity::VALUE) {
32+
auto all_nodes = stdx::to_unsorted_set(
33+
stdx::tuple_cat(get_all_mentioned_nodes(p.lhs),
34+
get_all_mentioned_nodes(p.rhs)));
35+
36+
return stdx::transform([](auto const &n) { return *n; }, all_nodes);
37+
38+
} else {
39+
return stdx::tuple_cat(get_nodes(p.lhs), get_nodes(p.rhs));
40+
}
41+
}
42+
43+
friend constexpr auto tag_invoke(get_all_mentioned_nodes_t, par const &p) {
44+
return stdx::tuple_cat(get_all_mentioned_nodes(p.lhs),
45+
get_all_mentioned_nodes(p.rhs));
2546
}
2647

2748
friend constexpr auto tag_invoke(get_edges_t, par const &p) {
2849
return stdx::tuple_cat(get_edges(p.lhs), get_edges(p.rhs));
2950
}
3051
};
3152

32-
template <node Lhs, node Rhs> par(Lhs, Rhs) -> par<Lhs, Rhs>;
53+
template <subgraph Lhs, subgraph Rhs> par(Lhs, Rhs) -> par<Lhs, Rhs>;
3354
} // namespace flow::dsl
3455

35-
template <flow::dsl::node Lhs, flow::dsl::node Rhs>
56+
template <flow::dsl::subgraph Lhs, flow::dsl::subgraph Rhs>
3657
[[nodiscard]] constexpr auto operator&&(Lhs const &lhs, Rhs const &rhs) {
3758
return flow::dsl::par{lhs, rhs};
3859
}
60+
61+
template <typename Cond, flow::dsl::subgraph Lhs, flow::dsl::subgraph Rhs,
62+
flow::subgraph_identity Identity>
63+
constexpr auto make_runtime_conditional(Cond,
64+
flow::dsl::par<Lhs, Rhs, Identity>) {
65+
auto lhs = make_runtime_conditional(Cond{}, Lhs{});
66+
auto rhs = make_runtime_conditional(Cond{}, Rhs{});
67+
68+
using lhs_t = decltype(lhs);
69+
using rhs_t = decltype(rhs);
70+
71+
return flow::dsl::par<lhs_t, rhs_t, Identity>{lhs, rhs};
72+
}

0 commit comments

Comments
 (0)