@@ -14,7 +14,38 @@ namespace pipeable
14
14
15
15
namespace impl
16
16
{
17
- template <typename output_t >
17
+ template <typename T>
18
+ struct non_threadsafe_receivers : std::vector<T>
19
+ {
20
+ template <typename callback_t >
21
+ void for_each (callback_t && callback) const
22
+ {
23
+ for (auto && downstream : *this )
24
+ {
25
+ std::forward<callback_t >(callback)(std::forward<decltype (downstream)>(downstream));
26
+ }
27
+ }
28
+
29
+ template <typename callback_t >
30
+ void modify_list (callback_t && callback)
31
+ {
32
+ std::forward<callback_t >(callback)(*this );
33
+ }
34
+ };
35
+
36
+ template <typename receiver_t , typename >
37
+ struct receivers_t
38
+ {
39
+ };
40
+
41
+ struct non_thread_safe {};
42
+ template <typename receiver_t >
43
+ struct receivers_t <receiver_t , non_thread_safe>
44
+ {
45
+ using type_t = non_threadsafe_receivers<receiver_t >;
46
+ };
47
+
48
+ template <typename output_t , typename collection_type_tag_t >
18
49
struct data_generator_impl : impl::custom_pipeable_tag
19
50
{
20
51
using downstream_t = std::function<void (output_t )>;
@@ -23,19 +54,18 @@ namespace pipeable
23
54
concepts::IsConvertible<arg_t , output_t > = nullptr >
24
55
void operator ()(arg_t && arg) const
25
56
{
26
- for (auto & downstream : receivers_)
27
- {
57
+ receivers_.for_each ([&](auto && downstream) {
28
58
if constexpr (std::is_rvalue_reference_v<output_t >)
29
59
{
30
- downstream.second (FWD (arg));
60
+ std::forward< decltype ( downstream)>(downstream) .second (std::move (arg));
31
61
}
32
62
else
33
63
{
34
64
// Intentionally don't forward here, since if we have multiple receivers and the value is "moved from"
35
65
// into the first, remaining receivers will (if it can be moved from) not get the value.
36
- downstream.second (arg);
66
+ std::forward< decltype ( downstream)>(downstream) .second (arg);
37
67
}
38
- }
68
+ });
39
69
}
40
70
41
71
template <typename callable_t ,
@@ -47,20 +77,25 @@ namespace pipeable
47
77
{
48
78
invocation::invoke (FWD (downstream), FWD (arg));
49
79
};
50
- receivers_.emplace_back (id, receiverCall);
80
+ receivers_.modify_list ([&](auto & receivers) {
81
+ receivers.emplace_back (id, receiverCall);
82
+ });
51
83
}
52
84
53
85
template <typename callable_t ,
54
86
concepts::IsInvocable<callable_t , output_t > = nullptr >
55
87
void operator -=(callable_t && downstream)
56
88
{
57
- receivers_.erase (std::remove_if (receivers_.begin (), receivers_.end (), [addr = identifier (downstream)](auto && receiver) {
58
- return receiver.first == addr;
59
- }), receivers_.end ());
89
+ receivers_.modify_list ([&](auto & receivers){
90
+ receivers.erase (std::remove_if (receivers.begin (), receivers.end (), [addr = identifier (downstream)](auto && receiver) {
91
+ return receiver.first == addr;
92
+ }), receivers.end ());
93
+ });
60
94
}
61
95
62
96
private:
63
- std::vector<std::pair<const void *, downstream_t >> receivers_;
97
+
98
+ typename receivers_t <std::pair<const void *, downstream_t >, collection_type_tag_t >::type_t receivers_;
64
99
65
100
template <typename callable_t >
66
101
static constexpr const void * identifier (const callable_t & callable)
@@ -83,53 +118,57 @@ namespace pipeable
83
118
using bases_t ::operator +=...;
84
119
using bases_t ::operator -=...;
85
120
};
86
- }
87
121
88
- template <typename ... outputs_t >
89
- struct data_generator : impl::multi_generator_impl<impl::data_generator_impl<outputs_t >...>
90
- {
91
- template <typename callable_t ,
92
- concepts::IsInvocableWithAny<callable_t , outputs_t ...> = nullptr >
93
- void operator +=(callable_t && downstream)
122
+ template <typename threading_t , typename ... outputs_t >
123
+ struct multi_generator : impl::multi_generator_impl<impl::data_generator_impl<outputs_t , threading_t >...>
94
124
{
95
- // Recursively call += for each output type to find exact base/data_generator
96
- auto callback = [](auto && base, auto && downstream){
97
- base->operator +=(FWD (downstream));
98
- };
99
- do_for_each_matching_generator<callable_t , outputs_t ...>(FWD (downstream), callback);
100
- }
101
-
102
- template <typename callable_t ,
103
- concepts::IsInvocableWithAny<callable_t , outputs_t ...> = nullptr >
104
- void operator -=(callable_t && downstream)
105
- {
106
- // Recursively call -= for each output type to find exact base/data_generator
107
- auto callback = [](auto && base, auto && downstream)
125
+ template <typename callable_t ,
126
+ concepts::IsInvocableWithAny<callable_t , outputs_t ...> = nullptr >
127
+ void operator +=(callable_t && downstream)
108
128
{
109
- base->operator -=(FWD (downstream));
110
- };
111
- do_for_each_matching_generator<callable_t , outputs_t ...>(FWD (downstream), callback);
112
- }
113
-
114
- private:
115
- template <typename callable_t , typename output_t , typename callback_t >
116
- void do_for_each_matching_generator (callable_t && downstream, callback_t && callback)
117
- {
118
- // Register callable for matching output only
119
- if constexpr (meta::is_invocable_v<callable_t , output_t >)
129
+ // Recursively call += for each output type to find exact base/data_generator
130
+ auto callback = [](auto && base, auto && downstream) {
131
+ base->operator +=(FWD (downstream));
132
+ };
133
+ do_for_each_matching_generator<callable_t , outputs_t ...>(FWD (downstream), callback);
134
+ }
135
+
136
+ template <typename callable_t ,
137
+ concepts::IsInvocableWithAny<callable_t , outputs_t ...> = nullptr >
138
+ void operator -=(callable_t && downstream)
120
139
{
121
- // Explicitly call correct base to avoid ambiguity
122
- using exact_base_t = impl::data_generator_impl<output_t >;
123
- callback (static_cast <exact_base_t *>(this ), FWD (downstream));
140
+ // Recursively call -= for each output type to find exact base/data_generator
141
+ auto callback = [](auto && base, auto && downstream)
142
+ {
143
+ base->operator -=(FWD (downstream));
144
+ };
145
+ do_for_each_matching_generator<callable_t , outputs_t ...>(FWD (downstream), callback);
124
146
}
125
- }
126
147
127
- template <typename callable_t , typename output_t , typename tail_t , typename ... tails_t , typename callback_t >
128
- void do_for_each_matching_generator (callable_t && downstream, callback_t && callback)
129
- {
130
- do_for_each_matching_generator<callable_t , output_t >(FWD (downstream), FWD (callback));
131
- // Recursively register callable for each (matching) output type
132
- do_for_each_matching_generator<callable_t , tail_t , tails_t ...>(FWD (downstream), FWD (callback));
133
- }
134
- };
148
+ private:
149
+ template <typename callable_t , typename output_t , typename callback_t >
150
+ void do_for_each_matching_generator (callable_t && downstream, callback_t && callback)
151
+ {
152
+ // Register callable for matching output only
153
+ if constexpr (meta::is_invocable_v<callable_t , output_t >)
154
+ {
155
+ // Explicitly call correct base to avoid ambiguity
156
+ using exact_base_t = impl::data_generator_impl<output_t , threading_t >;
157
+ callback (static_cast <exact_base_t *>(this ), FWD (downstream));
158
+ }
159
+ }
160
+
161
+ template <typename callable_t , typename output_t , typename tail_t , typename ... tails_t , typename callback_t >
162
+ void do_for_each_matching_generator (callable_t && downstream, callback_t && callback)
163
+ {
164
+ do_for_each_matching_generator<callable_t , output_t >(FWD (downstream), FWD (callback));
165
+ // Recursively register callable for each (matching) output type
166
+ do_for_each_matching_generator<callable_t , tail_t , tails_t ...>(FWD (downstream), FWD (callback));
167
+ }
168
+ };
169
+ }
170
+
171
+ template <typename ... outputs_t >
172
+ struct data_generator : impl::multi_generator<impl::non_thread_safe, outputs_t ...>
173
+ {};
135
174
}
0 commit comments