Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
A
Actor Framework
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
cpp-libs
Actor Framework
Commits
f9561cb6
Commit
f9561cb6
authored
Feb 05, 2022
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement prefix_and_tail flow operator
parent
5bbe822f
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
532 additions
and
27 deletions
+532
-27
libcaf_core/CMakeLists.txt
libcaf_core/CMakeLists.txt
+1
-0
libcaf_core/caf/flow/observable.hpp
libcaf_core/caf/flow/observable.hpp
+270
-24
libcaf_core/caf/flow/observer.hpp
libcaf_core/caf/flow/observer.hpp
+22
-0
libcaf_core/caf/flow/step.hpp
libcaf_core/caf/flow/step.hpp
+27
-3
libcaf_core/test/flow/prefix_and_tail.cpp
libcaf_core/test/flow/prefix_and_tail.cpp
+212
-0
No files found.
libcaf_core/CMakeLists.txt
View file @
f9561cb6
...
@@ -288,6 +288,7 @@ caf_add_component(
...
@@ -288,6 +288,7 @@ caf_add_component(
flow.for_each
flow.for_each
flow.merge
flow.merge
flow.observe_on
flow.observe_on
flow.prefix_and_tail
flow.single
flow.single
function_view
function_view
fused_downstream_manager
fused_downstream_manager
...
...
libcaf_core/caf/flow/observable.hpp
View file @
f9561cb6
...
@@ -12,6 +12,7 @@
...
@@ -12,6 +12,7 @@
#include "caf/async/consumer.hpp"
#include "caf/async/consumer.hpp"
#include "caf/async/producer.hpp"
#include "caf/async/producer.hpp"
#include "caf/async/spsc_buffer.hpp"
#include "caf/async/spsc_buffer.hpp"
#include "caf/cow_tuple.hpp"
#include "caf/defaults.hpp"
#include "caf/defaults.hpp"
#include "caf/detail/core_export.hpp"
#include "caf/detail/core_export.hpp"
#include "caf/disposable.hpp"
#include "caf/disposable.hpp"
...
@@ -157,19 +158,24 @@ public:
...
@@ -157,19 +158,24 @@ public:
/// Registers a callback for `on_complete` events.
/// Registers a callback for `on_complete` events.
template
<
class
F
>
template
<
class
F
>
auto
do_on_complete
(
F
f
)
{
auto
do_on_complete
(
F
f
)
{
return
transform
(
on_complete_step
<
T
,
F
>
{
std
::
move
(
f
)});
return
transform
(
do_
on_complete_step
<
T
,
F
>
{
std
::
move
(
f
)});
}
}
/// Registers a callback for `on_error` events.
/// Registers a callback for `on_error` events.
template
<
class
F
>
template
<
class
F
>
auto
do_on_error
(
F
f
)
{
auto
do_on_error
(
F
f
)
{
return
transform
(
on_error_step
<
T
,
F
>
{
std
::
move
(
f
)});
return
transform
(
do_
on_error_step
<
T
,
F
>
{
std
::
move
(
f
)});
}
}
/// Registers a callback that runs on `on_complete` or `on_error`.
/// Registers a callback that runs on `on_complete` or `on_error`.
template
<
class
F
>
template
<
class
F
>
auto
do_finally
(
F
f
)
{
auto
do_finally
(
F
f
)
{
return
transform
(
finally_step
<
T
,
F
>
{
std
::
move
(
f
)});
return
transform
(
do_finally_step
<
T
,
F
>
{
std
::
move
(
f
)});
}
/// Catches errors by converting them into errors instead.
auto
on_error_complete
()
{
return
transform
(
on_error_complete_step
<
T
>
{});
}
}
/// Returns a transformation that selects only the first `n` items.
/// Returns a transformation that selects only the first `n` items.
...
@@ -213,6 +219,18 @@ public:
...
@@ -213,6 +219,18 @@ public:
template
<
class
F
>
template
<
class
F
>
auto
concat_map
(
F
f
);
auto
concat_map
(
F
f
);
/// Takes @p prefix_size elements from this observable and emits it in a tuple
/// containing an observable for the remaining elements as the second value.
/// The returned observable either emits a single element (the tuple) or none
/// if this observable never produces sufficient elements for the prefix.
/// @pre `prefix_size > 0`
observable
<
cow_tuple
<
std
::
vector
<
T
>
,
observable
<
T
>>>
prefix_and_tail
(
size_t
prefix_size
);
/// Similar to `prefix_and_tail(1)` but passes the single element directly in
/// the tuple instead of wrapping it in a list.
observable
<
cow_tuple
<
T
,
observable
<
T
>>>
head_and_tail
();
/// Creates an asynchronous resource that makes emitted items available in a
/// Creates an asynchronous resource that makes emitted items available in a
/// spsc buffer.
/// spsc buffer.
async
::
consumer_resource
<
T
>
to_resource
(
size_t
buffer_size
,
async
::
consumer_resource
<
T
>
to_resource
(
size_t
buffer_size
,
...
@@ -323,6 +341,15 @@ public:
...
@@ -323,6 +341,15 @@ public:
return
lift
().
concat_map
(
std
::
move
(
f
));
return
lift
().
concat_map
(
std
::
move
(
f
));
}
}
observable
<
cow_tuple
<
std
::
vector
<
T
>
,
observable
<
T
>>>
prefix_and_tail
(
size_t
prefix_size
)
&&
{
return
lift
().
prefix_and_tail
(
prefix_size
);
}
observable
<
cow_tuple
<
T
,
observable
<
T
>>>
head_and_tail
()
&&
{
return
lift
().
head_and_tail
();
}
disposable
subscribe
(
observer
<
T
>
what
)
&&
{
disposable
subscribe
(
observer
<
T
>
what
)
&&
{
return
lift
().
subscribe
(
std
::
move
(
what
));
return
lift
().
subscribe
(
std
::
move
(
what
));
}
}
...
@@ -425,7 +452,7 @@ private:
...
@@ -425,7 +452,7 @@ private:
intrusive_ptr
<
impl
>
pimpl_
;
intrusive_ptr
<
impl
>
pimpl_
;
};
};
template
<
class
In
,
class
Out
>
template
<
class
In
,
class
Out
=
In
>
using
processor_impl
=
typename
processor
<
In
,
Out
>::
impl
;
using
processor_impl
=
typename
processor
<
In
,
Out
>::
impl
;
// -- representing an error as an observable -----------------------------------
// -- representing an error as an observable -----------------------------------
...
@@ -950,21 +977,8 @@ public:
...
@@ -950,21 +977,8 @@ public:
// nop
// nop
}
}
std
::
tuple
<
Step
,
Steps
...
>
steps
;
void
on_complete
()
override
{
super
::
sub_
=
nullptr
;
private:
bool
do_on_next
(
span
<
const
input_type
>
items
)
override
{
auto
f
=
[
this
,
items
](
auto
&
step
,
auto
&
...
steps
)
{
term_step
<
output_type
>
term
{
this
};
for
(
auto
&&
item
:
items
)
if
(
!
step
.
on_next
(
item
,
steps
...,
term
))
return
false
;
return
true
;
};
return
std
::
apply
(
f
,
steps
);
}
void
do_on_complete
()
override
{
auto
f
=
[
this
](
auto
&
step
,
auto
&
...
steps
)
{
auto
f
=
[
this
](
auto
&
step
,
auto
&
...
steps
)
{
term_step
<
output_type
>
term
{
this
};
term_step
<
output_type
>
term
{
this
};
step
.
on_complete
(
steps
...,
term
);
step
.
on_complete
(
steps
...,
term
);
...
@@ -972,13 +986,28 @@ public:
...
@@ -972,13 +986,28 @@ public:
std
::
apply
(
f
,
steps
);
std
::
apply
(
f
,
steps
);
}
}
void
do_on_error
(
const
error
&
what
)
override
{
void
on_error
(
const
error
&
what
)
override
{
super
::
sub_
=
nullptr
;
auto
f
=
[
this
,
&
what
](
auto
&
step
,
auto
&
...
steps
)
{
auto
f
=
[
this
,
&
what
](
auto
&
step
,
auto
&
...
steps
)
{
term_step
<
output_type
>
term
{
this
};
term_step
<
output_type
>
term
{
this
};
step
.
on_error
(
what
,
steps
...,
term
);
step
.
on_error
(
what
,
steps
...,
term
);
};
};
std
::
apply
(
f
,
steps
);
std
::
apply
(
f
,
steps
);
}
}
std
::
tuple
<
Step
,
Steps
...
>
steps
;
private:
bool
do_on_next
(
span
<
const
input_type
>
items
)
override
{
auto
f
=
[
this
,
items
](
auto
&
step
,
auto
&
...
steps
)
{
term_step
<
output_type
>
term
{
this
};
for
(
auto
&&
item
:
items
)
if
(
!
step
.
on_next
(
item
,
steps
...,
term
))
return
false
;
return
true
;
};
return
std
::
apply
(
f
,
steps
);
}
};
};
template
<
class
Tuple
>
template
<
class
Tuple
>
...
@@ -1025,22 +1054,26 @@ public:
...
@@ -1025,22 +1054,26 @@ public:
template
<
class
F
>
template
<
class
F
>
auto
do_on_complete
(
F
f
)
&&
{
auto
do_on_complete
(
F
f
)
&&
{
return
std
::
move
(
*
this
)
//
return
std
::
move
(
*
this
)
//
.
transform
(
on_complete_step
<
output_type
,
F
>
{
std
::
move
(
f
)});
.
transform
(
do_
on_complete_step
<
output_type
,
F
>
{
std
::
move
(
f
)});
}
}
template
<
class
F
>
template
<
class
F
>
auto
do_on_error
(
F
f
)
&&
{
auto
do_on_error
(
F
f
)
&&
{
return
std
::
move
(
*
this
)
//
return
std
::
move
(
*
this
)
//
.
transform
(
on_error_step
<
output_type
,
F
>
{
std
::
move
(
f
)});
.
transform
(
do_
on_error_step
<
output_type
,
F
>
{
std
::
move
(
f
)});
}
}
template
<
class
F
>
template
<
class
F
>
auto
do_finally
(
F
f
)
&&
{
auto
do_finally
(
F
f
)
&&
{
return
std
::
move
(
*
this
)
//
return
std
::
move
(
*
this
)
//
.
transform
(
finally_step
<
output_type
,
F
>
{
std
::
move
(
f
)});
.
transform
(
do_finally_step
<
output_type
,
F
>
{
std
::
move
(
f
)});
}
auto
on_error_complete
()
{
return
std
::
move
(
*
this
)
//
.
transform
(
on_error_complete_step
<
output_type
>
{});
}
}
observable
<
output_type
>
as_observable
()
&&
override
{
observable
<
output_type
>
as_observable
()
&&
{
auto
pimpl
=
make_counted
<
impl
>
(
source_
.
ptr
()
->
ctx
(),
std
::
move
(
steps_
));
auto
pimpl
=
make_counted
<
impl
>
(
source_
.
ptr
()
->
ctx
(),
std
::
move
(
steps_
));
auto
res
=
pimpl
->
as_observable
();
auto
res
=
pimpl
->
as_observable
();
source_
.
subscribe
(
observer
<
input_type
>
{
std
::
move
(
pimpl
)});
source_
.
subscribe
(
observer
<
input_type
>
{
std
::
move
(
pimpl
)});
...
@@ -1511,6 +1544,219 @@ auto observable<T>::concat_map(F f) {
...
@@ -1511,6 +1544,219 @@ auto observable<T>::concat_map(F f) {
return
obs
->
merger
();
return
obs
->
merger
();
}
}
// -- observable::prefix_and_tail ----------------------------------------------
template
<
class
T
>
class
prefix_and_tail_observable_impl
final
:
public
ref_counted
,
public
observable_impl
<
T
>
,
// For the forwarding to the 'tail'.
public
processor_impl
<
T
,
cow_tuple
<
std
::
vector
<
T
>
,
observable
<
T
>>>
{
public:
// -- member types -----------------------------------------------------------
using
in_t
=
T
;
using
out_t
=
cow_tuple
<
std
::
vector
<
T
>
,
observable
<
T
>>
;
using
in_obs_t
=
observable_impl
<
in_t
>
;
using
out_obs_t
=
observable_impl
<
out_t
>
;
// -- constructors, destructors, and assignment operators --------------------
prefix_and_tail_observable_impl
(
coordinator
*
ctx
,
size_t
prefix_size
)
:
ctx_
(
ctx
),
prefix_size_
(
prefix_size
)
{
// nop
}
// -- friends ----------------------------------------------------------------
CAF_INTRUSIVE_PTR_FRIENDS
(
prefix_and_tail_observable_impl
)
// -- implementation of disposable::impl -------------------------------------
void
dispose
()
override
{
if
(
sub_
)
{
sub_
.
cancel
();
sub_
=
nullptr
;
}
if
(
obs_
)
{
obs_
.
on_complete
();
obs_
=
nullptr
;
}
if
(
tail_
)
{
tail_
.
on_complete
();
tail_
=
nullptr
;
}
}
bool
disposed
()
const
noexcept
override
{
return
!
sub_
&&
!
obs_
&&
!
tail_
;
}
void
ref_disposable
()
const
noexcept
override
{
this
->
ref
();
}
void
deref_disposable
()
const
noexcept
override
{
this
->
deref
();
}
// -- implementation of observable<in_t>::impl -------------------------------
coordinator
*
ctx
()
const
noexcept
override
{
return
ctx_
;
}
disposable
subscribe
(
observer
<
in_t
>
sink
)
override
{
if
(
sink
.
ptr
()
==
tail_
.
ptr
())
{
return
in_obs_t
::
do_subscribe
(
sink
.
ptr
());
}
else
{
sink
.
on_error
(
make_error
(
sec
::
invalid_observable
));
return
disposable
{};
}
}
void
on_request
(
observer_impl
<
in_t
>*
,
size_t
n
)
override
{
if
(
sub_
)
sub_
.
request
(
n
);
}
void
on_cancel
(
observer_impl
<
in_t
>*
)
override
{
if
(
sub_
)
{
sub_
.
cancel
();
sub_
=
nullptr
;
}
}
// -- implementation of observable<out_t>::impl ------------------------------
disposable
subscribe
(
observer
<
out_t
>
sink
)
override
{
obs_
=
sink
;
return
out_obs_t
::
do_subscribe
(
sink
.
ptr
());
}
void
on_request
(
observer_impl
<
out_t
>*
,
size_t
)
override
{
if
(
sub_
&&
!
requested_prefix_
)
{
requested_prefix_
=
true
;
sub_
.
request
(
prefix_size_
);
}
}
void
on_cancel
(
observer_impl
<
out_t
>*
)
override
{
// Only has an effect when canceling immediately. Otherwise, we forward to
// tail_ and the original observer no longer is of any interest since it
// receives at most one item anyways.
if
(
sub_
&&
!
tail_
)
{
sub_
.
cancel
();
sub_
=
nullptr
;
}
}
// -- implementation of observer<in_t>::impl ---------------------------------
void
on_subscribe
(
subscription
sub
)
override
{
if
(
!
had_subscriber_
)
{
had_subscriber_
=
true
;
sub_
=
std
::
move
(
sub
);
}
else
{
sub
.
cancel
();
}
}
void
on_next
(
span
<
const
in_t
>
items
)
override
{
if
(
tail_
)
{
tail_
.
on_next
(
items
);
}
else
if
(
obs_
)
{
CAF_ASSERT
(
prefix_
.
size
()
+
items
.
size
()
<=
prefix_size_
);
prefix_
.
insert
(
prefix_
.
end
(),
items
.
begin
(),
items
.
end
());
if
(
prefix_
.
size
()
>=
prefix_size_
)
{
auto
tptr
=
make_counted
<
broadcaster_impl
<
in_t
>>
(
ctx_
);
tail_
=
tptr
->
as_observer
();
static_cast
<
observable_impl
<
in_t
>*>
(
this
)
->
subscribe
(
tail_
);
auto
item
=
make_cow_tuple
(
std
::
move
(
prefix_
),
tptr
->
as_observable
());
obs_
.
on_next
(
make_span
(
&
item
,
1
));
obs_
.
on_complete
();
obs_
=
nullptr
;
}
}
}
void
on_complete
()
override
{
sub_
=
nullptr
;
if
(
obs_
)
{
obs_
.
on_complete
();
obs_
=
nullptr
;
}
if
(
tail_
)
{
tail_
.
on_complete
();
tail_
=
nullptr
;
}
}
void
on_error
(
const
error
&
what
)
override
{
sub_
=
nullptr
;
if
(
obs_
)
{
obs_
.
on_error
(
what
);
obs_
=
nullptr
;
}
if
(
tail_
)
{
tail_
.
on_error
(
what
);
tail_
=
nullptr
;
}
}
private:
/// Our context.
coordinator
*
ctx_
;
/// Subscription to the input data.
subscription
sub_
;
/// Handle for the observer that gets the prefix + tail tuple.
observer
<
out_t
>
obs_
;
/// Handle to the tail for forwarding any data after the prefix.
observer
<
in_t
>
tail_
;
/// Makes sure we only respect the first subscriber.
bool
had_subscriber_
=
false
;
/// Stores whether we have requested the prefix;
bool
requested_prefix_
=
false
;
/// Buffer for storing the prefix elements.
std
::
vector
<
in_t
>
prefix_
;
/// User-defined size of the prefix.
size_t
prefix_size_
;
};
template
<
class
T
>
observable
<
cow_tuple
<
std
::
vector
<
T
>
,
observable
<
T
>>>
observable
<
T
>::
prefix_and_tail
(
size_t
prefix_size
)
{
using
impl_t
=
prefix_and_tail_observable_impl
<
T
>
;
using
out_t
=
cow_tuple
<
std
::
vector
<
T
>
,
observable
<
T
>>
;
auto
obs
=
make_counted
<
impl_t
>
(
pimpl_
->
ctx
(),
prefix_size
);
pimpl_
->
subscribe
(
obs
->
as_observer
());
return
static_cast
<
observable_impl
<
out_t
>*>
(
obs
.
get
())
->
as_observable
();
}
// -- observable::prefix_and_tail ----------------------------------------------
template
<
class
T
>
observable
<
cow_tuple
<
T
,
observable
<
T
>>>
observable
<
T
>::
head_and_tail
()
{
using
tuple_t
=
cow_tuple
<
std
::
vector
<
T
>
,
observable
<
T
>>
;
return
prefix_and_tail
(
1
)
.
map
([](
const
tuple_t
&
tup
)
{
auto
&
[
prefix
,
tail
]
=
tup
.
data
();
CAF_ASSERT
(
prefix
.
size
()
==
1
);
return
make_cow_tuple
(
prefix
.
front
(),
tail
);
})
.
as_observable
();
}
// -- observable::to_resource --------------------------------------------------
// -- observable::to_resource --------------------------------------------------
/// Reads from an observable buffer and emits the consumed items.
/// Reads from an observable buffer and emits the consumed items.
...
...
libcaf_core/caf/flow/observer.hpp
View file @
f9561cb6
...
@@ -426,6 +426,10 @@ private:
...
@@ -426,6 +426,10 @@ private:
namespace
caf
::
flow
{
namespace
caf
::
flow
{
/// Creates an observer from given callbacks.
/// @param on_next Callback for handling incoming elements.
/// @param on_error Callback for handling an error.
/// @param on_complete Callback for handling the end-of-stream event.
template
<
class
OnNext
,
class
OnError
,
class
OnComplete
>
template
<
class
OnNext
,
class
OnError
,
class
OnComplete
>
auto
make_observer
(
OnNext
on_next
,
OnError
on_error
,
OnComplete
on_complete
)
{
auto
make_observer
(
OnNext
on_next
,
OnError
on_error
,
OnComplete
on_complete
)
{
using
impl_type
=
detail
::
default_observer_impl
<
OnNext
,
OnError
,
OnComplete
>
;
using
impl_type
=
detail
::
default_observer_impl
<
OnNext
,
OnError
,
OnComplete
>
;
...
@@ -435,6 +439,9 @@ auto make_observer(OnNext on_next, OnError on_error, OnComplete on_complete) {
...
@@ -435,6 +439,9 @@ auto make_observer(OnNext on_next, OnError on_error, OnComplete on_complete) {
return
observer
<
input_type
>
{
std
::
move
(
ptr
)};
return
observer
<
input_type
>
{
std
::
move
(
ptr
)};
}
}
/// Creates an observer from given callbacks.
/// @param on_next Callback for handling incoming elements.
/// @param on_error Callback for handling an error.
template
<
class
OnNext
,
class
OnError
>
template
<
class
OnNext
,
class
OnError
>
auto
make_observer
(
OnNext
on_next
,
OnError
on_error
)
{
auto
make_observer
(
OnNext
on_next
,
OnError
on_error
)
{
using
impl_type
=
detail
::
default_observer_impl
<
OnNext
,
OnError
>
;
using
impl_type
=
detail
::
default_observer_impl
<
OnNext
,
OnError
>
;
...
@@ -443,6 +450,8 @@ auto make_observer(OnNext on_next, OnError on_error) {
...
@@ -443,6 +450,8 @@ auto make_observer(OnNext on_next, OnError on_error) {
return
observer
<
input_type
>
{
std
::
move
(
ptr
)};
return
observer
<
input_type
>
{
std
::
move
(
ptr
)};
}
}
/// Creates an observer from given callbacks.
/// @param on_next Callback for handling incoming elements.
template
<
class
OnNext
>
template
<
class
OnNext
>
auto
make_observer
(
OnNext
on_next
)
{
auto
make_observer
(
OnNext
on_next
)
{
using
impl_type
=
detail
::
default_observer_impl
<
OnNext
>
;
using
impl_type
=
detail
::
default_observer_impl
<
OnNext
>
;
...
@@ -451,4 +460,17 @@ auto make_observer(OnNext on_next) {
...
@@ -451,4 +460,17 @@ auto make_observer(OnNext on_next) {
return
observer
<
input_type
>
{
std
::
move
(
ptr
)};
return
observer
<
input_type
>
{
std
::
move
(
ptr
)};
}
}
/// Creates an observer from a smart pointer to a custom object that implements
/// `on_next`, `on_error` and `on_complete` as member functions.
/// @param ptr Smart pointer to a custom object.
template
<
class
SmartPointer
>
auto
make_observer_from_ptr
(
SmartPointer
ptr
)
{
using
obj_t
=
std
::
remove_reference_t
<
decltype
(
*
ptr
)
>
;
using
on_next_fn
=
decltype
(
&
obj_t
::
on_next
);
using
value_type
=
typename
detail
::
on_next_trait_t
<
on_next_fn
>::
value_type
;
return
make_observer
([
ptr
](
const
value_type
&
x
)
{
ptr
->
on_next
(
x
);
},
[
ptr
](
const
error
&
what
)
{
ptr
->
on_error
(
what
);
},
[
ptr
]
{
ptr
->
on_complete
();
});
}
}
// namespace caf::flow
}
// namespace caf::flow
libcaf_core/caf/flow/step.hpp
View file @
f9561cb6
...
@@ -150,7 +150,7 @@ struct flat_map_optional_step {
...
@@ -150,7 +150,7 @@ struct flat_map_optional_step {
};
};
template
<
class
T
,
class
Fn
>
template
<
class
T
,
class
Fn
>
struct
on_complete_step
{
struct
do_
on_complete_step
{
using
input_type
=
T
;
using
input_type
=
T
;
using
output_type
=
T
;
using
output_type
=
T
;
...
@@ -175,7 +175,7 @@ struct on_complete_step {
...
@@ -175,7 +175,7 @@ struct on_complete_step {
};
};
template
<
class
T
,
class
Fn
>
template
<
class
T
,
class
Fn
>
struct
on_error_step
{
struct
do_
on_error_step
{
using
input_type
=
T
;
using
input_type
=
T
;
using
output_type
=
T
;
using
output_type
=
T
;
...
@@ -200,7 +200,7 @@ struct on_error_step {
...
@@ -200,7 +200,7 @@ struct on_error_step {
};
};
template
<
class
T
,
class
Fn
>
template
<
class
T
,
class
Fn
>
struct
finally_step
{
struct
do_
finally_step
{
using
input_type
=
T
;
using
input_type
=
T
;
using
output_type
=
T
;
using
output_type
=
T
;
...
@@ -225,4 +225,28 @@ struct finally_step {
...
@@ -225,4 +225,28 @@ struct finally_step {
}
}
};
};
/// Catches errors by converting them into complete events instead.
template
<
class
T
>
struct
on_error_complete_step
{
using
input_type
=
T
;
using
output_type
=
T
;
template
<
class
Next
,
class
...
Steps
>
bool
on_next
(
const
input_type
&
item
,
Next
&
next
,
Steps
&
...
steps
)
{
return
next
.
on_next
(
item
,
steps
...);
}
template
<
class
Next
,
class
...
Steps
>
void
on_complete
(
Next
&
next
,
Steps
&
...
steps
)
{
next
.
on_complete
(
steps
...);
}
template
<
class
Next
,
class
...
Steps
>
void
on_error
(
const
error
&
,
Next
&
next
,
Steps
&
...
steps
)
{
next
.
on_complete
(
steps
...);
}
};
}
// namespace caf::flow
}
// namespace caf::flow
libcaf_core/test/flow/prefix_and_tail.cpp
0 → 100644
View file @
f9561cb6
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#define CAF_SUITE flow.prefix_and_tail
#include "caf/flow/observable.hpp"
#include "core-test.hpp"
#include <memory>
#include "caf/flow/coordinator.hpp"
#include "caf/flow/merge.hpp"
#include "caf/flow/observable_builder.hpp"
#include "caf/flow/observer.hpp"
#include "caf/flow/scoped_coordinator.hpp"
using
namespace
caf
;
namespace
{
template
<
class
T
>
struct
test_observer
{
void
on_next
(
T
x
)
{
values
.
emplace_back
(
std
::
move
(
x
));
}
void
on_error
(
const
error
&
what
)
{
had_error
=
true
;
err
=
what
;
}
void
on_complete
()
{
had_complete
=
true
;
}
std
::
vector
<
T
>
values
;
bool
had_error
=
false
;
bool
had_complete
=
false
;
error
err
;
};
struct
fixture
:
test_coordinator_fixture
<>
{
flow
::
scoped_coordinator_ptr
ctx
=
flow
::
make_scoped_coordinator
();
};
}
// namespace
BEGIN_FIXTURE_SCOPE
(
fixture
)
SCENARIO
(
"prefix_and_tail splits off initial elements"
)
{
using
tuple_t
=
cow_tuple
<
std
::
vector
<
int
>
,
flow
::
observable
<
int
>>
;
GIVEN
(
"a generation with 0 values"
)
{
WHEN
(
"calling prefix_and_tail(2)"
)
{
THEN
(
"the observer of prefix_and_tail only receives on_complete"
)
{
auto
inputs
=
std
::
vector
<
int
>
{};
auto
obs
=
std
::
make_shared
<
test_observer
<
tuple_t
>>
();
ctx
->
make_observable
()
.
from_container
(
inputs
)
.
prefix_and_tail
(
2
)
.
subscribe
(
flow
::
make_observer_from_ptr
(
obs
));
ctx
->
run
();
CHECK
(
obs
->
had_complete
);
CHECK
(
!
obs
->
had_error
);
CHECK
(
obs
->
values
.
empty
());
}
}
}
GIVEN
(
"a generation with 1 values"
)
{
WHEN
(
"calling prefix_and_tail(2)"
)
{
THEN
(
"the observer of prefix_and_tail only receives on_complete"
)
{
auto
inputs
=
std
::
vector
<
int
>
{
1
};
auto
obs
=
std
::
make_shared
<
test_observer
<
tuple_t
>>
();
ctx
->
make_observable
()
.
from_container
(
inputs
)
.
prefix_and_tail
(
2
)
.
subscribe
(
flow
::
make_observer_from_ptr
(
obs
));
ctx
->
run
();
CHECK
(
obs
->
had_complete
);
CHECK
(
!
obs
->
had_error
);
CHECK
(
obs
->
values
.
empty
());
}
}
}
GIVEN
(
"a generation with 2 values"
)
{
WHEN
(
"calling prefix_and_tail(2)"
)
{
THEN
(
"the observer receives the first 2 elements plus empty remainder"
)
{
auto
inputs
=
std
::
vector
<
int
>
{
1
,
2
};
auto
prefix_vals
=
std
::
vector
<
int
>
{
1
,
2
};
auto
tail_vals
=
std
::
vector
<
int
>
{};
auto
obs
=
std
::
make_shared
<
test_observer
<
int
>>
();
auto
flat_map_calls
=
0
;
ctx
->
make_observable
()
.
from_container
(
inputs
)
.
prefix_and_tail
(
2
)
.
flat_map
([
&
](
const
tuple_t
&
x
)
{
++
flat_map_calls
;
auto
&
[
prefix
,
tail
]
=
x
.
data
();
CHECK_EQ
(
prefix
,
prefix_vals
);
return
tail
;
})
.
subscribe
(
flow
::
make_observer_from_ptr
(
obs
));
ctx
->
run
();
CHECK_EQ
(
flat_map_calls
,
1
);
CHECK_EQ
(
obs
->
values
,
tail_vals
);
CHECK
(
obs
->
had_complete
);
CHECK
(
!
obs
->
had_error
);
}
}
}
GIVEN
(
"a generation with 8 values"
)
{
WHEN
(
"calling prefix_and_tail(2)"
)
{
THEN
(
"the observer receives the first 2 elements plus remainder"
)
{
auto
inputs
=
std
::
vector
<
int
>
{
1
,
2
,
4
,
8
,
16
,
32
,
64
,
128
};
auto
prefix_vals
=
std
::
vector
<
int
>
{
1
,
2
};
auto
tail_vals
=
std
::
vector
<
int
>
{
4
,
8
,
16
,
32
,
64
,
128
};
auto
obs
=
std
::
make_shared
<
test_observer
<
int
>>
();
auto
flat_map_calls
=
0
;
ctx
->
make_observable
()
.
from_container
(
inputs
)
.
prefix_and_tail
(
2
)
.
flat_map
([
&
](
const
tuple_t
&
x
)
{
++
flat_map_calls
;
auto
&
[
prefix
,
tail
]
=
x
.
data
();
CHECK_EQ
(
prefix
,
prefix_vals
);
return
tail
;
})
.
subscribe
(
flow
::
make_observer_from_ptr
(
obs
));
ctx
->
run
();
CHECK_EQ
(
flat_map_calls
,
1
);
CHECK_EQ
(
obs
->
values
,
tail_vals
);
CHECK
(
obs
->
had_complete
);
CHECK
(
!
obs
->
had_error
);
}
}
}
}
SCENARIO
(
"head_and_tail splits off the first element"
)
{
using
tuple_t
=
cow_tuple
<
int
,
flow
::
observable
<
int
>>
;
GIVEN
(
"a generation with 0 values"
)
{
WHEN
(
"calling head_and_tail"
)
{
THEN
(
"the observer of head_and_tail only receives on_complete"
)
{
auto
inputs
=
std
::
vector
<
int
>
{};
auto
obs
=
std
::
make_shared
<
test_observer
<
tuple_t
>>
();
ctx
->
make_observable
()
.
from_container
(
inputs
)
.
head_and_tail
()
.
subscribe
(
flow
::
make_observer_from_ptr
(
obs
));
ctx
->
run
();
CHECK
(
obs
->
had_complete
);
CHECK
(
!
obs
->
had_error
);
CHECK
(
obs
->
values
.
empty
());
}
}
}
GIVEN
(
"a generation with 1 values"
)
{
WHEN
(
"calling head_and_tail()"
)
{
THEN
(
"the observer receives the first element plus empty remainder"
)
{
auto
inputs
=
std
::
vector
<
int
>
{
1
};
auto
prefix_val
=
1
;
auto
tail_vals
=
std
::
vector
<
int
>
{};
auto
obs
=
std
::
make_shared
<
test_observer
<
int
>>
();
auto
flat_map_calls
=
0
;
ctx
->
make_observable
()
.
from_container
(
inputs
)
.
head_and_tail
()
.
flat_map
([
&
](
const
tuple_t
&
x
)
{
++
flat_map_calls
;
auto
&
[
prefix
,
tail
]
=
x
.
data
();
CHECK_EQ
(
prefix
,
prefix_val
);
return
tail
;
})
.
subscribe
(
flow
::
make_observer_from_ptr
(
obs
));
ctx
->
run
();
CHECK_EQ
(
flat_map_calls
,
1
);
CHECK_EQ
(
obs
->
values
,
tail_vals
);
CHECK
(
obs
->
had_complete
);
CHECK
(
!
obs
->
had_error
);
}
}
}
GIVEN
(
"a generation with 2 values"
)
{
WHEN
(
"calling head_and_tail()"
)
{
THEN
(
"the observer receives the first element plus remainder"
)
{
auto
inputs
=
std
::
vector
<
int
>
{
1
,
2
};
auto
prefix_val
=
1
;
auto
tail_vals
=
std
::
vector
<
int
>
{
2
};
auto
obs
=
std
::
make_shared
<
test_observer
<
int
>>
();
auto
flat_map_calls
=
0
;
ctx
->
make_observable
()
.
from_container
(
inputs
)
.
head_and_tail
()
.
flat_map
([
&
](
const
tuple_t
&
x
)
{
++
flat_map_calls
;
auto
&
[
prefix
,
tail
]
=
x
.
data
();
CHECK_EQ
(
prefix
,
prefix_val
);
return
tail
;
})
.
subscribe
(
flow
::
make_observer_from_ptr
(
obs
));
ctx
->
run
();
CHECK_EQ
(
flat_map_calls
,
1
);
CHECK_EQ
(
obs
->
values
,
tail_vals
);
CHECK
(
obs
->
had_complete
);
CHECK
(
!
obs
->
had_error
);
}
}
}
}
END_FIXTURE_SCOPE
()
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment