Commit ef25ccde authored by Dominik Charousset's avatar Dominik Charousset

Merge branch 'issue/1006'

Closes #1006.
parents e44c8110 d6aefd04
...@@ -84,6 +84,20 @@ is based on [Keep a Changelog](https://keepachangelog.com). ...@@ -84,6 +84,20 @@ is based on [Keep a Changelog](https://keepachangelog.com).
+ `illegal_escape_sequence` => `invalid_escape_sequence` + `illegal_escape_sequence` => `invalid_escape_sequence`
+ `illegal_argument` => `invalid_argument` + `illegal_argument` => `invalid_argument`
+ `illegal_category` => `invalid_category` + `illegal_category` => `invalid_category`
- CAF no longer automagically flattens `tuple`, `optional`, or `expected` when
returning these types from message handlers. Users can simply replace
`std::tuple<A, B, C>` with `caf::result<A, B, C>` for returning more than one
value from a message handler.
- A `caf::result` can no longer represent `skip`. Whether a message gets skipped
or not is now only for the default handler to decide. Consequently, default
handlers now return `skippable_result` instead of `result<message>`. A
skippable result is a variant over `delegated<message>`, `message`, `error`,
or `skip_t`. The only good use case for message handlers that skip a message
in their body was in typed actors for getting around the limitation that a
typed behavior always must provide all message handlers (typed behavior assume
a complete implementation of the interface). This use case received direct
support: constructing a typed behavior with `partial_behavior_init` as first
argument suppresses the check for completeness .
### Removed ### Removed
...@@ -93,6 +107,9 @@ is based on [Keep a Changelog](https://keepachangelog.com). ...@@ -93,6 +107,9 @@ is based on [Keep a Changelog](https://keepachangelog.com).
versions of the standard were never implemented in the first place. versions of the standard were never implemented in the first place.
Consequently, we've dropped the `opencl` module. Consequently, we've dropped the `opencl` module.
- The old `duration` type is now superseded by `timespan` (#994). - The old `duration` type is now superseded by `timespan` (#994).
- The enum `match_result` became obsolete. Individual message handlers can no
longer skip messages. Hence, message handlers can only succeed (match) or not.
Consequently, invoking a message handler or behavior now returns a boolean.
### Fixed ### Fixed
......
...@@ -43,9 +43,9 @@ chopstick::behavior_type taken_chopstick(chopstick::pointer, ...@@ -43,9 +43,9 @@ chopstick::behavior_type taken_chopstick(chopstick::pointer,
// either taken by a philosopher or available // either taken by a philosopher or available
chopstick::behavior_type available_chopstick(chopstick::pointer self) { chopstick::behavior_type available_chopstick(chopstick::pointer self) {
return { return {
[=](take_atom) -> std::tuple<taken_atom, bool> { [=](take_atom) -> result<taken_atom, bool> {
self->become(taken_chopstick(self, self->current_sender())); self->become(taken_chopstick(self, self->current_sender()));
return std::make_tuple(taken_atom_v, true); return {taken_atom_v, true};
}, },
[](put_atom) { cerr << "chopstick received unexpected 'put'" << endl; }, [](put_atom) { cerr << "chopstick received unexpected 'put'" << endl; },
}; };
...@@ -54,8 +54,8 @@ chopstick::behavior_type available_chopstick(chopstick::pointer self) { ...@@ -54,8 +54,8 @@ chopstick::behavior_type available_chopstick(chopstick::pointer self) {
chopstick::behavior_type taken_chopstick(chopstick::pointer self, chopstick::behavior_type taken_chopstick(chopstick::pointer self,
const strong_actor_ptr& user) { const strong_actor_ptr& user) {
return { return {
[](take_atom) -> std::tuple<taken_atom, bool> { [](take_atom) -> result<taken_atom, bool> {
return std::make_tuple(taken_atom_v, false); return {taken_atom_v, false};
}, },
[=](put_atom) { [=](put_atom) {
if (self->current_sender() == user) if (self->current_sender() == user)
......
...@@ -3,17 +3,14 @@ ...@@ -3,17 +3,14 @@
using namespace caf; using namespace caf;
behavior server(event_based_actor* self) { behavior server(event_based_actor* self) {
self->set_default_handler(skip);
return { return {
[=](idle_atom, const actor& worker) { [=](idle_atom, const actor& worker) {
self->become( self->become(keep_behavior, [=](ping_atom atm) {
keep_behavior,
[=](ping_atom atm) {
self->delegate(worker, atm); self->delegate(worker, atm);
self->unbecome(); self->unbecome();
});
}, },
[=](idle_atom, const actor&) { return skip(); });
},
[=](ping_atom) { return skip(); },
}; };
} }
......
...@@ -23,7 +23,7 @@ behavior ping(event_based_actor* self, actor pong_actor, int n) { ...@@ -23,7 +23,7 @@ behavior ping(event_based_actor* self, actor pong_actor, int n) {
behavior pong() { behavior pong() {
return { return {
[=](ping_atom, int x) { return std::make_tuple(pong_atom_v, x); }, [=](ping_atom, int x) { return make_result(pong_atom_v, x); },
}; };
} }
......
...@@ -6,8 +6,6 @@ file(GLOB_RECURSE CAF_CORE_HEADERS "caf/*.hpp") ...@@ -6,8 +6,6 @@ file(GLOB_RECURSE CAF_CORE_HEADERS "caf/*.hpp")
add_enum_consistency_check("caf/sec.hpp" "src/sec_strings.cpp") add_enum_consistency_check("caf/sec.hpp" "src/sec_strings.cpp")
add_enum_consistency_check("caf/pec.hpp" "src/pec_strings.cpp") add_enum_consistency_check("caf/pec.hpp" "src/pec_strings.cpp")
add_enum_consistency_check("caf/match_result.hpp"
"src/match_result_strings.cpp")
add_enum_consistency_check("caf/stream_priority.hpp" add_enum_consistency_check("caf/stream_priority.hpp"
"src/stream_priority_strings.cpp") "src/stream_priority_strings.cpp")
add_enum_consistency_check("caf/exit_reason.hpp" add_enum_consistency_check("caf/exit_reason.hpp"
...@@ -111,7 +109,6 @@ set(CAF_CORE_SOURCES ...@@ -111,7 +109,6 @@ set(CAF_CORE_SOURCES
src/logger.cpp src/logger.cpp
src/mailbox_element.cpp src/mailbox_element.cpp
src/make_config_option.cpp src/make_config_option.cpp
src/match_result_strings.cpp
src/memory_managed.cpp src/memory_managed.cpp
src/message.cpp src/message.cpp
src/message_builder.cpp src/message_builder.cpp
......
...@@ -99,8 +99,8 @@ public: ...@@ -99,8 +99,8 @@ public:
} }
/// Runs this handler with callback. /// Runs this handler with callback.
match_result operator()(detail::invoke_result_visitor& f, message& xs) { bool operator()(detail::invoke_result_visitor& f, message& xs) {
return impl_ ? impl_->invoke(f, xs) : match_result::no_match; return impl_ ? impl_->invoke(f, xs) : false;
} }
/// Checks whether this behavior is not empty. /// Checks whether this behavior is not empty.
......
...@@ -28,7 +28,11 @@ namespace caf { ...@@ -28,7 +28,11 @@ namespace caf {
template <class F> template <class F>
struct catch_all { struct catch_all {
using fun_type = std::function<result<message>(message&)>; using fun_type = std::function<skippable_result(message&)>;
static_assert(std::is_convertible<F, fun_type>::value,
"catch-all handler must have signature "
"skippable_result (message&)");
F handler; F handler;
...@@ -41,10 +45,6 @@ struct catch_all { ...@@ -41,10 +45,6 @@ struct catch_all {
// nop // nop
} }
static_assert(std::is_convertible<F, fun_type>::value,
"catch-all handler must have signature "
"result<message> (message&)");
fun_type lift() const { fun_type lift() const {
return handler; return handler;
} }
......
...@@ -31,7 +31,6 @@ ...@@ -31,7 +31,6 @@
#include "caf/detail/type_traits.hpp" #include "caf/detail/type_traits.hpp"
#include "caf/intrusive_ptr.hpp" #include "caf/intrusive_ptr.hpp"
#include "caf/make_counted.hpp" #include "caf/make_counted.hpp"
#include "caf/match_result.hpp"
#include "caf/message.hpp" #include "caf/message.hpp"
#include "caf/none.hpp" #include "caf/none.hpp"
#include "caf/optional.hpp" #include "caf/optional.hpp"
...@@ -62,10 +61,9 @@ public: ...@@ -62,10 +61,9 @@ public:
explicit behavior_impl(timespan tout); explicit behavior_impl(timespan tout);
match_result invoke_empty(detail::invoke_result_visitor& f); bool invoke_empty(detail::invoke_result_visitor& f);
virtual match_result invoke(detail::invoke_result_visitor& f, message& xs) virtual bool invoke(detail::invoke_result_visitor& f, message& xs) = 0;
= 0;
optional<message> invoke(message&); optional<message> invoke(message&);
...@@ -123,15 +121,13 @@ public: ...@@ -123,15 +121,13 @@ public:
// nop // nop
} }
virtual match_result invoke(detail::invoke_result_visitor& f, virtual bool invoke(detail::invoke_result_visitor& f, message& xs) override {
message& xs) override {
return invoke_impl(f, xs, std::make_index_sequence<sizeof...(Ts)>{}); return invoke_impl(f, xs, std::make_index_sequence<sizeof...(Ts)>{});
} }
template <size_t... Is> template <size_t... Is>
match_result invoke_impl(detail::invoke_result_visitor& f, message& msg, bool invoke_impl(detail::invoke_result_visitor& f, message& msg,
std::index_sequence<Is...>) { std::index_sequence<Is...>) {
auto result = match_result::no_match;
auto dispatch = [&](auto& fun) { auto dispatch = [&](auto& fun) {
using fun_type = std::decay_t<decltype(fun)>; using fun_type = std::decay_t<decltype(fun)>;
using trait = get_callable_trait_t<fun_type>; using trait = get_callable_trait_t<fun_type>;
...@@ -141,18 +137,16 @@ public: ...@@ -141,18 +137,16 @@ public:
using fun_result = decltype(detail::apply_args(fun, xs)); using fun_result = decltype(detail::apply_args(fun, xs));
if constexpr (std::is_same<void, fun_result>::value) { if constexpr (std::is_same<void, fun_result>::value) {
detail::apply_args(fun, xs); detail::apply_args(fun, xs);
result = f.visit(unit) ? match_result::match : match_result::skip; f(unit);
} else { } else {
auto invoke_res = detail::apply_args(fun, xs); auto invoke_res = detail::apply_args(fun, xs);
result = f.visit(invoke_res) ? match_result::match f(invoke_res);
: match_result::skip;
} }
return true; return true;
} }
return false; return false;
}; };
static_cast<void>((dispatch(std::get<Is>(cases_)) || ...)); return (dispatch(std::get<Is>(cases_)) || ...);
return result;
} }
void handle_timeout() override { void handle_timeout() override {
......
...@@ -35,7 +35,7 @@ public: ...@@ -35,7 +35,7 @@ public:
virtual ~blocking_behavior(); virtual ~blocking_behavior();
virtual result<message> fallback(message&); virtual skippable_result fallback(message&);
virtual timespan timeout(); virtual timespan timeout();
...@@ -54,7 +54,7 @@ public: ...@@ -54,7 +54,7 @@ public:
blocking_behavior_v2(blocking_behavior_v2&&) = default; blocking_behavior_v2(blocking_behavior_v2&&) = default;
result<message> fallback(message& x) override { skippable_result fallback(message& x) override {
return f.handler(x); return f.handler(x);
} }
}; };
...@@ -93,7 +93,7 @@ public: ...@@ -93,7 +93,7 @@ public:
blocking_behavior_v4(blocking_behavior_v4&&) = default; blocking_behavior_v4(blocking_behavior_v4&&) = default;
result<message> fallback(message& x) override { skippable_result fallback(message& x) override {
return f1.handler(x); return f1.handler(x);
} }
......
...@@ -27,7 +27,9 @@ namespace caf::detail { ...@@ -27,7 +27,9 @@ namespace caf::detail {
template <class Self> template <class Self>
class default_invoke_result_visitor : public invoke_result_visitor { class default_invoke_result_visitor : public invoke_result_visitor {
public: public:
inline default_invoke_result_visitor(Self* ptr) : self_(ptr) { using super = invoke_result_visitor;
default_invoke_result_visitor(Self* ptr) : self_(ptr) {
// nop // nop
} }
...@@ -35,9 +37,7 @@ public: ...@@ -35,9 +37,7 @@ public:
// nop // nop
} }
void operator()() override { using super::operator();
// nop
}
void operator()(error& x) override { void operator()(error& x) override {
CAF_LOG_TRACE(CAF_ARG(x)); CAF_LOG_TRACE(CAF_ARG(x));
...@@ -49,11 +49,6 @@ public: ...@@ -49,11 +49,6 @@ public:
delegate(x); delegate(x);
} }
void operator()(const none_t& x) override {
CAF_LOG_TRACE(CAF_ARG(x));
delegate(x);
}
private: private:
void deliver(response_promise& rp, error& x) { void deliver(response_promise& rp, error& x) {
CAF_LOG_DEBUG("report error back to requesting actor"); CAF_LOG_DEBUG("report error back to requesting actor");
...@@ -68,11 +63,6 @@ private: ...@@ -68,11 +63,6 @@ private:
rp.deliver(std::move(x)); rp.deliver(std::move(x));
} }
void deliver(response_promise& rp, const none_t&) {
error err = sec::unexpected_response;
deliver(rp, err);
}
template <class T> template <class T>
void delegate(T& x) { void delegate(T& x) {
auto rp = self_->make_response_promise(); auto rp = self_->make_response_promise();
......
...@@ -25,7 +25,6 @@ ...@@ -25,7 +25,6 @@
#include "caf/detail/int_list.hpp" #include "caf/detail/int_list.hpp"
#include "caf/detail/overload.hpp" #include "caf/detail/overload.hpp"
#include "caf/detail/type_traits.hpp" #include "caf/detail/type_traits.hpp"
#include "caf/expected.hpp"
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/make_sink_result.hpp" #include "caf/make_sink_result.hpp"
#include "caf/make_source_result.hpp" #include "caf/make_source_result.hpp"
...@@ -51,47 +50,13 @@ public: ...@@ -51,47 +50,13 @@ public:
// -- virtual handlers ------------------------------------------------------- // -- virtual handlers -------------------------------------------------------
/// Called whenever no result messages gets produced, e.g., when returning a
/// `response_promise`.
virtual void operator()() = 0;
/// Called if the message handler returned an error. /// Called if the message handler returned an error.
virtual void operator()(error&) = 0; virtual void operator()(error&) = 0;
/// Called if the message handler returned any "ordinary" value. /// Called if the message handler returned any "ordinary" value.
virtual void operator()(message&) = 0; virtual void operator()(message&) = 0;
/// Called if the message handler returns "nothing", for example a // -- extraction and conversions ---------------------------------------------
/// default-constructed `optional<T>`.
virtual void operator()(const none_t&) = 0;
// -- on-the-fly type conversions --------------------------------------------
/// Called if the message handler returns `void` or `unit_t`.
inline void operator()(const unit_t&) {
message empty_msg;
(*this)(empty_msg);
}
/// Unwraps an `optional<T>` by recursively calling the visitor with either
/// `none_t` or `T`.
template <class T>
void operator()(optional<T>& x) {
if (x)
(*this)(*x);
else
(*this)(none);
}
/// Unwraps an `expected<T>` by recursively calling the visitor with either
/// `error` or `T`.
template <class T>
void operator()(expected<T>& x) {
if (x)
(*this)(*x);
else
(*this)(x.error());
}
/// Wraps arbitrary values into a `message` and calls the visitor recursively. /// Wraps arbitrary values into a `message` and calls the visitor recursively.
template <class... Ts> template <class... Ts>
...@@ -103,114 +68,62 @@ public: ...@@ -103,114 +68,62 @@ public:
(*this)(tmp); (*this)(tmp);
} }
/// Wraps the tuple into a `message` and calls the visitor recursively with /// Called if the message handler returns `void` or `unit_t`.
/// its contents. void operator()(const unit_t&) {
template <class... Ts> message empty_msg;
void operator()(std::tuple<Ts...>& xs) { (*this)(empty_msg);
apply_args(*this, get_indices(xs), xs);
} }
/// Disambiguates the variadic `operator<Ts...>()`. /// Disambiguates the variadic `operator<Ts...>()`.
inline void operator()(none_t& x) { void operator()(unit_t& x) {
(*this)(const_cast<const none_t&>(x)); (*this)(const_cast<const unit_t&>(x));
} }
/// Disambiguates the variadic `operator<Ts...>()`. /// Dispatches on the runtime-type of `x`.
inline void operator()(unit_t& x) { template <class... Ts>
(*this)(const_cast<const unit_t&>(x)); void operator()(result<Ts...>& res) {
caf::visit([this](auto& x) { (*this)(x); }, res);
} }
// -- special-purpose handlers that don't produce results -------------------- // -- special-purpose handlers that don't produce results --------------------
/// Calls `(*this)()`. void operator()(response_promise&) {
inline void operator()(response_promise&) { // nop
(*this)();
} }
/// Calls `(*this)()`.
template <class... Ts> template <class... Ts>
void operator()(typed_response_promise<Ts...>&) { void operator()(typed_response_promise<Ts...>&) {
(*this)(); // nop
} }
/// Calls `(*this)()`.
template <class... Ts> template <class... Ts>
void operator()(delegated<Ts...>&) { void operator()(delegated<Ts...>&) {
(*this)(); // nop
} }
/// Calls `(*this)()`.
template <class Out, class... Ts> template <class Out, class... Ts>
void operator()(outbound_stream_slot<Out, Ts...>&) { void operator()(outbound_stream_slot<Out, Ts...>&) {
(*this)(); // nop
} }
/// Calls `(*this)()`.
template <class In> template <class In>
void operator()(inbound_stream_slot<In>&) { void operator()(inbound_stream_slot<In>&) {
(*this)(); // nop
} }
/// Calls `(*this)()`.
template <class In> template <class In>
void operator()(make_sink_result<In>&) { void operator()(make_sink_result<In>&) {
(*this)(); // nop
} }
/// Calls `(*this)()`.
template <class DownstreamManager, class... Ts> template <class DownstreamManager, class... Ts>
void operator()(make_source_result<DownstreamManager, Ts...>&) { void operator()(make_source_result<DownstreamManager, Ts...>&) {
(*this)(); // nop
} }
/// Calls `(*this)()`.
template <class In, class DownstreamManager, class... Ts> template <class In, class DownstreamManager, class... Ts>
void operator()(make_stage_result<In, DownstreamManager, Ts...>&) { void operator()(make_stage_result<In, DownstreamManager, Ts...>&) {
(*this)(); // nop
}
// -- visit API: return true if T was visited, false if T was skipped --------
/// Delegates `x` to the appropriate handler and returns `true`.
template <class T>
bool visit(T& x) {
(*this)(x);
return true;
}
/// Returns `false`.
inline bool visit(skip_t&) {
return false;
}
/// Returns `false`.
inline bool visit(const skip_t&) {
return false;
}
/// Returns `false` if `x != none`, otherwise calls the void handler and
/// returns `true`..
inline bool visit(optional<skip_t>& x) {
if (x)
return false;
(*this)();
return true;
}
/// Dispatches on the runtime-type of `x`.
template <class... Ts>
bool visit(result<Ts...>& res) {
auto f = detail::make_overload(
[this](auto& x) {
(*this)(x);
return true;
},
[this](delegated<Ts...>&) {
(*this)();
return true;
},
[this](skip_t&) { return false; });
return caf::visit(f, res);
} }
}; };
......
...@@ -87,7 +87,7 @@ struct CAF_CORE_EXPORT function_view_storage_catch_all { ...@@ -87,7 +87,7 @@ struct CAF_CORE_EXPORT function_view_storage_catch_all {
// nop // nop
} }
result<message> operator()(message& msg) { skippable_result operator()(message& msg) {
*storage_ = std::move(msg); *storage_ = std::move(msg);
return message{}; return message{};
} }
......
...@@ -135,6 +135,7 @@ class resumable; ...@@ -135,6 +135,7 @@ class resumable;
class scheduled_actor; class scheduled_actor;
class scoped_actor; class scoped_actor;
class serializer; class serializer;
class skip_t;
class stream_manager; class stream_manager;
class string_view; class string_view;
class tracing_data; class tracing_data;
...@@ -193,6 +194,7 @@ using ip_address = ipv6_address; ...@@ -193,6 +194,7 @@ using ip_address = ipv6_address;
using ip_endpoint = ipv6_endpoint; using ip_endpoint = ipv6_endpoint;
using ip_subnet = ipv6_subnet; using ip_subnet = ipv6_subnet;
using settings = dictionary<config_value>; using settings = dictionary<config_value>;
using skippable_result = variant<delegated<message>, message, error, skip_t>;
using stream_slot = uint16_t; using stream_slot = uint16_t;
using type_id_t = uint16_t; using type_id_t = uint16_t;
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2020 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <string>
#include "caf/detail/core_export.hpp"
namespace caf {
/// Denotes the invoke result of a ::behavior or ::message_handler.
enum class match_result {
no_match,
match,
skip,
};
/// @relates match_result
CAF_CORE_EXPORT std::string to_string(match_result);
} // namespace caf
...@@ -91,8 +91,8 @@ public: ...@@ -91,8 +91,8 @@ public:
} }
/// Runs this handler with callback. /// Runs this handler with callback.
match_result operator()(detail::invoke_result_visitor& f, message& xs) { bool operator()(detail::invoke_result_visitor& f, message& xs) {
return impl_ ? impl_->invoke(f, xs) : match_result::no_match; return impl_ ? impl_->invoke(f, xs) : false;
} }
/// Returns a new handler that concatenates this handler /// Returns a new handler that concatenates this handler
......
...@@ -50,7 +50,7 @@ class result_base { ...@@ -50,7 +50,7 @@ class result_base {
public: public:
static_assert(sizeof...(Ts) > 0); static_assert(sizeof...(Ts) > 0);
using types = detail::type_list<delegated<Ts...>, message, error, skip_t>; using types = detail::type_list<delegated<Ts...>, message, error>;
result_base() = default; result_base() = default;
...@@ -71,10 +71,6 @@ public: ...@@ -71,10 +71,6 @@ public:
// nop // nop
} }
result_base(skip_t) : content_(skip) {
// nop
}
result_base(delegated<Ts...> x) : content_(x) { result_base(delegated<Ts...> x) : content_(x) {
// nop // nop
} }
...@@ -109,7 +105,7 @@ protected: ...@@ -109,7 +105,7 @@ protected:
// nop // nop
} }
variant<delegated<Ts...>, message, error, skip_t> content_; variant<delegated<Ts...>, message, error> content_;
}; };
// -- result<Ts...> and its specializations ------------------------------------ // -- result<Ts...> and its specializations ------------------------------------
...@@ -236,6 +232,20 @@ public: ...@@ -236,6 +232,20 @@ public:
} }
}; };
// -- free functions -----------------------------------------------------------
/// Convenience function for wrapping the parameter pack `xs...` into a
/// `result`.
template <class... Ts>
auto make_result(Ts&&... xs) {
return result<std::decay_t<Ts>...>(std::forward<Ts>(xs)...);
}
// -- special type alias for a skippable result<message> -----------------------
/// Similar to `result<message>`, but also allows to *skip* a message.
using skippable_result = variant<delegated<message>, message, error, skip_t>;
// -- sum type access to result<Ts...> ----------------------------------------- // -- sum type access to result<Ts...> -----------------------------------------
template <class... Ts> template <class... Ts>
......
...@@ -84,21 +84,21 @@ namespace caf { ...@@ -84,21 +84,21 @@ namespace caf {
/// @relates scheduled_actor /// @relates scheduled_actor
/// Default handler function that sends the message back to the sender. /// Default handler function that sends the message back to the sender.
CAF_CORE_EXPORT result<message> reflect(scheduled_actor*, message&); CAF_CORE_EXPORT skippable_result reflect(scheduled_actor*, message&);
/// @relates scheduled_actor /// @relates scheduled_actor
/// Default handler function that sends /// Default handler function that sends
/// the message back to the sender and then quits. /// the message back to the sender and then quits.
CAF_CORE_EXPORT result<message> reflect_and_quit(scheduled_actor*, message&); CAF_CORE_EXPORT skippable_result reflect_and_quit(scheduled_actor*, message&);
/// @relates scheduled_actor /// @relates scheduled_actor
/// Default handler function that prints messages /// Default handler function that prints messages
/// message via `aout` and drops them afterwards. /// message via `aout` and drops them afterwards.
CAF_CORE_EXPORT result<message> print_and_drop(scheduled_actor*, message&); CAF_CORE_EXPORT skippable_result print_and_drop(scheduled_actor*, message&);
/// @relates scheduled_actor /// @relates scheduled_actor
/// Default handler function that simply drops messages. /// Default handler function that simply drops messages.
CAF_CORE_EXPORT result<message> drop(scheduled_actor*, message&); CAF_CORE_EXPORT skippable_result drop(scheduled_actor*, message&);
/// A cooperatively scheduled, event-based actor implementation. /// A cooperatively scheduled, event-based actor implementation.
class CAF_CORE_EXPORT scheduled_actor : public local_actor, class CAF_CORE_EXPORT scheduled_actor : public local_actor,
...@@ -189,7 +189,7 @@ public: ...@@ -189,7 +189,7 @@ public:
using pointer = scheduled_actor*; using pointer = scheduled_actor*;
/// Function object for handling unmatched messages. /// Function object for handling unmatched messages.
using default_handler = std::function<result<message>(pointer, message&)>; using default_handler = std::function<skippable_result(pointer, message&)>;
/// Function object for handling error messages. /// Function object for handling error messages.
using error_handler = std::function<void(pointer, error&)>; using error_handler = std::function<void(pointer, error&)>;
...@@ -215,8 +215,8 @@ public: ...@@ -215,8 +215,8 @@ public:
size_t max_throughput; size_t max_throughput;
/// Consumes upstream messages. /// Consumes upstream messages.
intrusive::task_result intrusive::task_result operator()(size_t, upstream_queue&,
operator()(size_t, upstream_queue&, mailbox_element&); mailbox_element&);
/// Consumes downstream messages. /// Consumes downstream messages.
intrusive::task_result intrusive::task_result
...@@ -730,8 +730,8 @@ public: ...@@ -730,8 +730,8 @@ public:
/// Closes all incoming stream traffic for a manager. Emits a drop message on /// Closes all incoming stream traffic for a manager. Emits a drop message on
/// each path if `reason == none` and a `forced_drop` message on each path /// each path if `reason == none` and a `forced_drop` message on each path
/// otherwise. /// otherwise.
virtual void virtual void erase_inbound_paths_later(const stream_manager* mgr,
erase_inbound_paths_later(const stream_manager* mgr, error reason); error reason);
// -- handling of stream messages -------------------------------------------- // -- handling of stream messages --------------------------------------------
......
...@@ -31,20 +31,13 @@ namespace caf { ...@@ -31,20 +31,13 @@ namespace caf {
/// skipping to the runtime. /// skipping to the runtime.
class CAF_CORE_EXPORT skip_t { class CAF_CORE_EXPORT skip_t {
public: public:
using fun = std::function<result<message>(scheduled_actor* self, message&)>; using fun = std::function<skippable_result(scheduled_actor* self, message&)>;
constexpr skip_t() { constexpr skip_t() {
// nop // nop
} }
constexpr skip_t operator()() const {
return *this;
}
operator fun() const; operator fun() const;
private:
static result<message> skip_fun_impl(scheduled_actor*, message&);
}; };
/// Tells the runtime system to skip a message when used as message /// Tells the runtime system to skip a message when used as message
......
...@@ -27,9 +27,7 @@ ...@@ -27,9 +27,7 @@
#include "caf/detail/typed_actor_util.hpp" #include "caf/detail/typed_actor_util.hpp"
namespace caf { namespace caf ::detail {
namespace detail {
// converts a list of replies_to<...>::with<...> elements to a list of // converts a list of replies_to<...>::with<...> elements to a list of
// lists containing the replies_to<...> half only // lists containing the replies_to<...> half only
...@@ -127,15 +125,30 @@ void static_check_typed_behavior_input() { ...@@ -127,15 +125,30 @@ void static_check_typed_behavior_input() {
"typed behavior (exact match needed)"); "typed behavior (exact match needed)");
} }
} // namespace detail } // namespace caf::detail
template <class... Sigs> namespace caf::mixin {
class typed_actor;
namespace mixin {
template <class, class, class> template <class, class, class>
class behavior_stack_based_impl; class behavior_stack_based_impl;
}
} // namespace caf::mixin
namespace caf {
/// Tag type for constructing a `typed_behavior` with an incomplete list of
/// message handlers, delegating to the default handler for all unmatched
/// inputs.
struct partial_behavior_init_t {};
constexpr partial_behavior_init_t partial_behavior_init
= partial_behavior_init_t{};
/// Empty struct tag for constructing from an untyped behavior.
struct unsafe_behavior_init_t {};
constexpr unsafe_behavior_init_t unsafe_behavior_init
= unsafe_behavior_init_t{};
template <class... Sigs> template <class... Sigs>
class typed_behavior { class typed_behavior {
...@@ -156,9 +169,6 @@ public: ...@@ -156,9 +169,6 @@ public:
/// Stores the template parameter pack in a type list. /// Stores the template parameter pack in a type list.
using signatures = detail::type_list<Sigs...>; using signatures = detail::type_list<Sigs...>;
/// Empty struct tag for constructing from an untyped behavior.
struct unsafe_init {};
// -- constructors, destructors, and assignment operators -------------------- // -- constructors, destructors, and assignment operators --------------------
typed_behavior(typed_behavior&&) = default; typed_behavior(typed_behavior&&) = default;
...@@ -182,11 +192,18 @@ public: ...@@ -182,11 +192,18 @@ public:
set(detail::make_behavior(std::move(x), std::move(xs)...)); set(detail::make_behavior(std::move(x), std::move(xs)...));
} }
typed_behavior(unsafe_init, behavior x) : bhvr_(std::move(x)) { template <class... Ts>
typed_behavior(partial_behavior_init_t, Ts... xs)
: typed_behavior(unsafe_behavior_init, behavior{std::move(xs)...}) {
// TODO: implement type checking.
}
typed_behavior(unsafe_behavior_init_t, behavior x) : bhvr_(std::move(x)) {
// nop // nop
} }
typed_behavior(unsafe_init, message_handler x) : bhvr_(std::move(x)) { typed_behavior(unsafe_behavior_init_t, message_handler x)
: bhvr_(std::move(x)) {
// nop // nop
} }
......
...@@ -158,7 +158,7 @@ behavior spawn_serv_impl(stateful_actor<spawn_serv_state>* self) { ...@@ -158,7 +158,7 @@ behavior spawn_serv_impl(stateful_actor<spawn_serv_state>* self) {
CAF_LOG_TRACE(""); CAF_LOG_TRACE("");
return { return {
[=](spawn_atom, const std::string& name, message& args, [=](spawn_atom, const std::string& name, message& args,
actor_system::mpi& xs) -> expected<strong_actor_ptr> { actor_system::mpi& xs) -> result<strong_actor_ptr> {
CAF_LOG_TRACE(CAF_ARG(name) << CAF_ARG(args)); CAF_LOG_TRACE(CAF_ARG(name) << CAF_ARG(args));
return self->system().spawn<strong_actor_ptr>(name, std::move(args), return self->system().spawn<strong_actor_ptr>(name, std::move(args),
self->context(), true, &xs); self->context(), true, &xs);
......
...@@ -190,19 +190,14 @@ blocking_actor::mailbox_visitor::operator()(mailbox_element& x) { ...@@ -190,19 +190,14 @@ blocking_actor::mailbox_visitor::operator()(mailbox_element& x) {
[&] { self->current_element_ = prev_element; }); [&] { self->current_element_ = prev_element; });
// Dispatch on x. // Dispatch on x.
detail::default_invoke_result_visitor<blocking_actor> visitor{self}; detail::default_invoke_result_visitor<blocking_actor> visitor{self};
switch (bhvr.nested(visitor, x.content())) { if (bhvr.nested(visitor, x.content()))
default:
return check_if_done(); return check_if_done();
case match_result::no_match: { // Blocking actors can have fallback // Blocking actors can have fallback handlers for catch-all rules.
// handlers for catch-all rules.
auto sres = bhvr.fallback(self->current_element_->payload); auto sres = bhvr.fallback(self->current_element_->payload);
if (!holds_alternative<skip_t>(sres)) { auto f = detail::make_overload(
visitor.visit(sres); [&](skip_t&) {
return check_if_done(); // Response handlers must get re-invoked with an error when
} // receiving an unexpected message.
}
// Response handlers must get re-invoked with an error when receiving an
// unexpected message.
if (mid.is_response()) { if (mid.is_response()) {
auto err = make_error(sec::unexpected_response, std::move(x.payload)); auto err = make_error(sec::unexpected_response, std::move(x.payload));
mailbox_element tmp{std::move(x.sender), x.mid, std::move(x.stages), mailbox_element tmp{std::move(x.sender), x.mid, std::move(x.stages),
...@@ -211,10 +206,13 @@ blocking_actor::mailbox_visitor::operator()(mailbox_element& x) { ...@@ -211,10 +206,13 @@ blocking_actor::mailbox_visitor::operator()(mailbox_element& x) {
bhvr.nested(tmp.content()); bhvr.nested(tmp.content());
return check_if_done(); return check_if_done();
} }
CAF_ANNOTATE_FALLTHROUGH;
case match_result::skip:
return intrusive::task_result::skip; return intrusive::task_result::skip;
} },
[&](auto& res) {
visitor(res);
return check_if_done();
});
return visit(f, sres);
}; };
// Post-process the returned value from the function body. // Post-process the returned value from the function body.
auto result = body(); auto result = body();
......
...@@ -28,9 +28,8 @@ namespace { ...@@ -28,9 +28,8 @@ namespace {
class combinator final : public behavior_impl { class combinator final : public behavior_impl {
public: public:
match_result invoke(detail::invoke_result_visitor& f, message& xs) override { bool invoke(detail::invoke_result_visitor& f, message& xs) override {
auto x = first->invoke(f, xs); return first->invoke(f, xs) || second->invoke(f, xs);
return x == match_result::no_match ? second->invoke(f, xs) : x;
} }
void handle_timeout() override { void handle_timeout() override {
...@@ -53,10 +52,6 @@ class maybe_message_visitor : public detail::invoke_result_visitor { ...@@ -53,10 +52,6 @@ class maybe_message_visitor : public detail::invoke_result_visitor {
public: public:
optional<message> value; optional<message> value;
void operator()() override {
value = message{};
}
void operator()(error& x) override { void operator()(error& x) override {
value = make_message(std::move(x)); value = make_message(std::move(x));
} }
...@@ -64,10 +59,6 @@ public: ...@@ -64,10 +59,6 @@ public:
void operator()(message& x) override { void operator()(message& x) override {
value = std::move(x); value = std::move(x);
} }
void operator()(const none_t&) override {
(*this)();
}
}; };
} // namespace } // namespace
...@@ -84,27 +75,20 @@ behavior_impl::behavior_impl(timespan tout) : timeout_(tout) { ...@@ -84,27 +75,20 @@ behavior_impl::behavior_impl(timespan tout) : timeout_(tout) {
// nop // nop
} }
match_result behavior_impl::invoke_empty(detail::invoke_result_visitor& f) { bool behavior_impl::invoke_empty(detail::invoke_result_visitor& f) {
message xs; message xs;
return invoke(f, xs); return invoke(f, xs);
} }
optional<message> behavior_impl::invoke(message& xs) { optional<message> behavior_impl::invoke(message& xs) {
maybe_message_visitor f; maybe_message_visitor f;
// the following const-cast is safe, because invoke() is aware of if (invoke(f, xs))
// copy-on-write and does not modify x if it's shared
if (!xs.empty())
invoke(f, xs);
else
invoke_empty(f);
return std::move(f.value); return std::move(f.value);
return none;
} }
match_result behavior_impl::invoke(detail::invoke_result_visitor& f, bool behavior_impl::invoke(detail::invoke_result_visitor& f, message& xs) {
message& xs) {
if (!xs.empty())
return invoke(f, xs); return invoke(f, xs);
return invoke_empty(f);
} }
void behavior_impl::handle_timeout() { void behavior_impl::handle_timeout() {
......
...@@ -28,7 +28,7 @@ blocking_behavior::blocking_behavior(behavior& x) : nested(x) { ...@@ -28,7 +28,7 @@ blocking_behavior::blocking_behavior(behavior& x) : nested(x) {
// nop // nop
} }
result<message> blocking_behavior::fallback(message&) { skippable_result blocking_behavior::fallback(message&) {
return skip; return skip;
} }
......
...@@ -161,9 +161,9 @@ public: ...@@ -161,9 +161,9 @@ public:
CAF_LOG_TRACE(""); CAF_LOG_TRACE("");
// instead of dropping "unexpected" messages, // instead of dropping "unexpected" messages,
// we simply forward them to our acquaintances // we simply forward them to our acquaintances
auto fwd = [=](scheduled_actor*, message& msg) -> result<message> { auto fwd = [=](scheduled_actor*, message& msg) -> skippable_result {
send_to_acquaintances(std::move(msg)); send_to_acquaintances(std::move(msg));
return message{}; return delegated<message>{};
}; };
set_default_handler(fwd); set_default_handler(fwd);
set_down_handler([=](down_msg& dm) { set_down_handler([=](down_msg& dm) {
...@@ -313,7 +313,7 @@ behavior proxy_broker::make_behavior() { ...@@ -313,7 +313,7 @@ behavior proxy_broker::make_behavior() {
CAF_LOG_TRACE(""); CAF_LOG_TRACE("");
// instead of dropping "unexpected" messages, // instead of dropping "unexpected" messages,
// we simply forward them to our acquaintances // we simply forward them to our acquaintances
auto fwd = [=](local_actor*, message& msg) -> result<message> { auto fwd = [=](local_actor*, message& msg) -> skippable_result {
group_->send_all_subscribers(current_element_->sender, std::move(msg), group_->send_all_subscribers(current_element_->sender, std::move(msg),
context()); context());
return message{}; return message{};
......
// clang-format off
// DO NOT EDIT: this file is auto-generated by caf-generate-enum-strings.
// Run the target update-enum-strings if this file is out of sync.
#include "caf/match_result.hpp"
#include <string>
namespace caf {
std::string to_string(match_result x) {
switch(x) {
default:
return "???";
case match_result::no_match:
return "no_match";
case match_result::match:
return "match";
case match_result::skip:
return "skip";
};
}
} // namespace caf
...@@ -82,38 +82,27 @@ invoke_message_result raw_event_based_actor::consume(mailbox_element& x) { ...@@ -82,38 +82,27 @@ invoke_message_result raw_event_based_actor::consume(mailbox_element& x) {
} }
// handle everything else as ordinary message // handle everything else as ordinary message
detail::default_invoke_result_visitor<event_based_actor> visitor{this}; detail::default_invoke_result_visitor<event_based_actor> visitor{this};
bool skipped = false;
auto had_timeout = getf(has_timeout_flag); auto had_timeout = getf(has_timeout_flag);
if (had_timeout) if (had_timeout)
unsetf(has_timeout_flag); unsetf(has_timeout_flag);
// restore timeout at scope exit if message was skipped if (!bhvr_stack_.empty()) {
auto timeout_guard = detail::make_scope_guard([&] {
if (skipped && had_timeout)
setf(has_timeout_flag);
});
auto call_default_handler = [&] {
auto sres = call_handler(default_handler_, this, x.payload);
auto f = detail::make_overload([&](auto& x) { visitor.visit(x); },
[&](skip_t& x) { skipped = true; });
visit(f, sres);
};
if (bhvr_stack_.empty()) {
call_default_handler();
return !skipped ? invoke_message_result::consumed
: invoke_message_result::skipped;
}
auto& bhvr = bhvr_stack_.back(); auto& bhvr = bhvr_stack_.back();
switch (bhvr(visitor, x.content())) { if (bhvr(visitor, x.content()))
default: return invoke_message_result::consumed;
break;
case match_result::skip:
skipped = true;
break;
case match_result::no_match:
call_default_handler();
} }
return !skipped ? invoke_message_result::consumed auto sres = call_handler(default_handler_, this, x.payload);
: invoke_message_result::skipped; auto f = detail::make_overload(
[&](auto& x) {
visitor(x);
return invoke_message_result::consumed;
},
[&](skip_t& x) {
// Restore timeout if message was skipped.
if (had_timeout)
setf(has_timeout_flag);
return invoke_message_result::skipped;
});
return visit(f, sres);
}; };
// Post-process the returned value from the function body. // Post-process the returned value from the function body.
auto result = body(); auto result = body();
......
...@@ -36,26 +36,26 @@ namespace caf { ...@@ -36,26 +36,26 @@ namespace caf {
// -- related free functions --------------------------------------------------- // -- related free functions ---------------------------------------------------
result<message> reflect(scheduled_actor*, message& msg) { skippable_result reflect(scheduled_actor*, message& msg) {
return std::move(msg); return std::move(msg);
} }
result<message> reflect_and_quit(scheduled_actor* ptr, message& msg) { skippable_result reflect_and_quit(scheduled_actor* ptr, message& msg) {
error err = exit_reason::normal; error err = exit_reason::normal;
scheduled_actor::default_error_handler(ptr, err); scheduled_actor::default_error_handler(ptr, err);
return reflect(ptr, msg); return reflect(ptr, msg);
} }
result<message> print_and_drop(scheduled_actor* ptr, message& msg) { skippable_result print_and_drop(scheduled_actor* ptr, message& msg) {
CAF_LOG_WARNING("unexpected message:" << msg); CAF_LOG_WARNING("unexpected message:" << msg);
aout(ptr) << "*** unexpected message [id: " << ptr->id() aout(ptr) << "*** unexpected message [id: " << ptr->id()
<< ", name: " << ptr->name() << "]: " << to_string(msg) << ", name: " << ptr->name() << "]: " << to_string(msg)
<< std::endl; << std::endl;
return sec::unexpected_message; return make_error(sec::unexpected_message);
} }
result<message> drop(scheduled_actor*, message&) { skippable_result drop(scheduled_actor*, message&) {
return sec::unexpected_message; return make_error(sec::unexpected_message);
} }
// -- implementation details --------------------------------------------------- // -- implementation details ---------------------------------------------------
...@@ -67,7 +67,7 @@ void silently_ignore(scheduled_actor*, T&) { ...@@ -67,7 +67,7 @@ void silently_ignore(scheduled_actor*, T&) {
// nop // nop
} }
result<message> drop_after_quit(scheduled_actor* self, message&) { skippable_result drop_after_quit(scheduled_actor* self, message&) {
if (self->current_message_id().is_request()) if (self->current_message_id().is_request())
return make_error(sec::request_receiver_down); return make_error(sec::request_receiver_down);
return make_message(); return make_message();
...@@ -691,38 +691,26 @@ invoke_message_result scheduled_actor::consume(mailbox_element& x) { ...@@ -691,38 +691,26 @@ invoke_message_result scheduled_actor::consume(mailbox_element& x) {
return invoke_message_result::consumed; return invoke_message_result::consumed;
case message_category::ordinary: { case message_category::ordinary: {
detail::default_invoke_result_visitor<scheduled_actor> visitor{this}; detail::default_invoke_result_visitor<scheduled_actor> visitor{this};
bool skipped = false;
auto had_timeout = getf(has_timeout_flag); auto had_timeout = getf(has_timeout_flag);
if (had_timeout) if (had_timeout)
unsetf(has_timeout_flag); unsetf(has_timeout_flag);
// restore timeout at scope exit if message was skipped if (!bhvr_stack_.empty()) {
auto timeout_guard = detail::make_scope_guard([&] {
if (skipped && had_timeout)
setf(has_timeout_flag);
});
auto call_default_handler = [&] {
auto f = detail::make_overload([&](auto& val) { visitor.visit(val); },
[&](skip_t&) { skipped = true; });
auto sres = call_handler(default_handler_, this, x.payload);
visit(f, sres);
};
if (bhvr_stack_.empty()) {
call_default_handler();
return !skipped ? invoke_message_result::consumed
: invoke_message_result::skipped;
}
auto& bhvr = bhvr_stack_.back(); auto& bhvr = bhvr_stack_.back();
switch (bhvr(visitor, x.content())) { if (bhvr(visitor, x.content()))
default: return invoke_message_result::consumed;
break;
case match_result::skip:
skipped = true;
break;
case match_result::no_match:
call_default_handler();
} }
return !skipped ? invoke_message_result::consumed auto sres = call_handler(default_handler_, this, x.payload);
: invoke_message_result::skipped; auto f = detail::make_overload(
[&](auto& x) {
visitor(x);
return invoke_message_result::consumed;
},
[&](skip_t&) {
if (had_timeout)
setf(has_timeout_flag);
return invoke_message_result::skipped;
});
return visit(f, sres);
} }
} }
// Unreachable. // Unreachable.
...@@ -1100,10 +1088,6 @@ scheduled_actor::handle_open_stream_msg(mailbox_element& x) { ...@@ -1100,10 +1088,6 @@ scheduled_actor::handle_open_stream_msg(mailbox_element& x) {
CAF_LOG_TRACE(CAF_ARG(x)); CAF_LOG_TRACE(CAF_ARG(x));
// Fetches a stream manager from a behavior. // Fetches a stream manager from a behavior.
struct visitor : detail::invoke_result_visitor { struct visitor : detail::invoke_result_visitor {
void operator()() override {
// nop
}
void operator()(error&) override { void operator()(error&) override {
// nop // nop
} }
...@@ -1111,10 +1095,6 @@ scheduled_actor::handle_open_stream_msg(mailbox_element& x) { ...@@ -1111,10 +1095,6 @@ scheduled_actor::handle_open_stream_msg(mailbox_element& x) {
void operator()(message&) override { void operator()(message&) override {
// nop // nop
} }
void operator()(const none_t&) override {
// nop
}
}; };
// Extract the handshake part of the message. // Extract the handshake part of the message.
CAF_ASSERT(x.content().match_elements<open_stream_msg>()); CAF_ASSERT(x.content().match_elements<open_stream_msg>());
...@@ -1128,35 +1108,20 @@ scheduled_actor::handle_open_stream_msg(mailbox_element& x) { ...@@ -1128,35 +1108,20 @@ scheduled_actor::handle_open_stream_msg(mailbox_element& x) {
auto rp = make_response_promise(); auto rp = make_response_promise();
rp.deliver(sec::stream_init_failed); rp.deliver(sec::stream_init_failed);
}; };
// Utility for invoking the default handler. // Invoke behavior and dispatch on the result.
auto fallback = [&] { auto& bs = bhvr_stack();
if (!bs.empty() && bs.back()(f, osm.msg))
return invoke_message_result::consumed;
CAF_LOG_DEBUG("no match in behavior, fall back to default handler");
auto sres = call_handler(default_handler_, this, x.payload); auto sres = call_handler(default_handler_, this, x.payload);
if (holds_alternative<skip_t>(sres)) { if (holds_alternative<skip_t>(sres)) {
CAF_LOG_DEBUG("default handler skipped open_stream_msg:" << osm.msg); CAF_LOG_DEBUG("default handler skipped open_stream_msg:" << osm.msg);
return invoke_message_result::skipped; return invoke_message_result::skipped;
} else { } else {
CAF_LOG_DEBUG( CAF_LOG_DEBUG("default handler was called for open_stream_msg:" << osm.msg);
"default handler was called for open_stream_msg:" << osm.msg);
fail(sec::stream_init_failed, "dropped open_stream_msg (no match)"); fail(sec::stream_init_failed, "dropped open_stream_msg (no match)");
return invoke_message_result::dropped; return invoke_message_result::dropped;
} }
};
// Invoke behavior and dispatch on the result.
auto& bs = bhvr_stack();
if (bs.empty())
return fallback();
auto res = (bs.back())(f, osm.msg);
switch (res) {
case match_result::no_match:
CAF_LOG_DEBUG("no match in behavior, fall back to default handler");
return fallback();
case match_result::match: {
return invoke_message_result::consumed;
}
default:
CAF_LOG_DEBUG("behavior skipped open_stream_msg:" << osm.msg);
return invoke_message_result::skipped; // nop
}
} }
actor_clock::time_point actor_clock::time_point
......
...@@ -23,10 +23,14 @@ ...@@ -23,10 +23,14 @@
namespace caf { namespace caf {
result<message> skip_t::skip_fun_impl(scheduled_actor*, message&) { namespace {
return skip();
skippable_result skip_fun_impl(scheduled_actor*, message&) {
return skip;
} }
} // namespace
skip_t::operator fun() const { skip_t::operator fun() const {
return skip_fun_impl; return skip_fun_impl;
} }
......
...@@ -43,9 +43,9 @@ CAF_TEST_FIXTURE_SCOPE(blocking_actor_tests, fixture) ...@@ -43,9 +43,9 @@ CAF_TEST_FIXTURE_SCOPE(blocking_actor_tests, fixture)
CAF_TEST(catch_all) { CAF_TEST(catch_all) {
self->send(self, 42); self->send(self, 42);
self->receive([](float) { CAF_FAIL("received unexpected float"); }, self->receive([](float) { CAF_FAIL("received unexpected float"); },
others >> [](message& msg) -> result<message> { others >> [](message& msg) -> skippable_result {
CAF_CHECK_EQUAL(to_string(msg), "(42)"); CAF_CHECK_EQUAL(to_string(msg), "(42)");
return sec::unexpected_message; return make_error(sec::unexpected_message);
}); });
self->receive( self->receive(
[](const error& err) { [](const error& err) {
...@@ -75,110 +75,4 @@ CAF_TEST(timeout_in_scoped_actor) { ...@@ -75,110 +75,4 @@ CAF_TEST(timeout_in_scoped_actor) {
CAF_CHECK(timeout_called); CAF_CHECK(timeout_called);
} }
// -- scoped_actors using skip -------------------------------------------------
using msg_t = int;
// send_order_t contains messages which are send to an actor in the same order
// as in vector
using send_order_t = std::vector<msg_t>;
// sequence_t contains a number of messages for processing by an actor with
// the information to skip the current message for later processing
using sequence_t = std::vector<std::pair<msg_t, bool>>;
using check_order_t = std::pair<send_order_t, sequence_t>;
behavior check_order_behavior_factory(local_actor*,
sequence_t::const_iterator* seq_it_ptr) {
return {
[=](int i) -> result<void> {
auto& seq_it = *seq_it_ptr;
CAF_CHECK_EQUAL(i, seq_it->first);
if (seq_it->second) {
CAF_MESSAGE("current: " << i << "; awaiting: " << seq_it->first
<< " SKIPPED");
++seq_it;
return skip();
} else {
CAF_MESSAGE("current: " << i << "; awaiting: " << seq_it->first
<< " OK");
++seq_it;
return unit;
}
}
};
}
void check_order_event_based_actor(const check_order_t& corder) {
actor_system_config cfg;
actor_system system{cfg};
auto& send_order = corder.first;
auto& sequence = corder.second;
auto seq_it = sequence.cbegin();
{
auto tmp = system.spawn(
[&](event_based_actor* self) {
self->set_default_handler(skip);
for(auto i : send_order) {
self->send(self, i);
}
self->become(
check_order_behavior_factory(self, &seq_it));
}
);
}
system.await_all_actors_done();
}
void check_order_scoped_actor(const check_order_t& corder) {
actor_system_config cfg;
actor_system system{cfg};
auto& send_order = corder.first;
auto& sequence = corder.second;
auto seq_it = begin(sequence);
scoped_actor self{system};
auto check_order_behavior =
check_order_behavior_factory(actor_cast<local_actor*>(self), &seq_it);
for(auto i : send_order) {
self->send(self, i);
}
while (seq_it != end(sequence)) {
self->receive(check_order_behavior);
}
}
CAF_TEST(skip_message) {
check_order_t a = {
{0, 1, 2, 3}, //recv_order = 0,1,2,3
{{0, false}, {1, false}, {2, false}, {3, false}}
};
check_order_t b = {
{3, 2, 1, 0}, //recv_order = 0,1,2,3
{{3, true}, {2, true}, {1, true}, {0, false},
{3, true}, {2, true}, {1, false},
{3, true}, {2, false},
{3, false}}
};
check_order_t c = {
{1, 0, 2}, //recv_order = 0,1,2
{{1, true}, {0, false},
{1, false},
{2, false}}
};
check_order_t d = {
{3, 1, 2, 0}, //recv_order = 0,1,2,3
{{3, true}, {1, true}, {2, true}, {0, false},
{3, true}, {1, false},
{3, true}, {2, false},
{3, false}}
};
check_order_event_based_actor(a);
check_order_event_based_actor(b);
check_order_event_based_actor(c);
check_order_event_based_actor(d);
check_order_scoped_actor(a);
check_order_scoped_actor(b);
check_order_scoped_actor(c);
check_order_scoped_actor(d);
}
CAF_TEST_FIXTURE_SCOPE_END() CAF_TEST_FIXTURE_SCOPE_END()
...@@ -42,7 +42,9 @@ using second_stage = typed_actor<replies_to<double, double>::with<double>>; ...@@ -42,7 +42,9 @@ using second_stage = typed_actor<replies_to<double, double>::with<double>>;
first_stage::behavior_type typed_first_stage() { first_stage::behavior_type typed_first_stage() {
return { return {
[](int i) { return std::make_tuple(i * 2.0, i * 4.0); }, [](int i) -> result<double, double> {
return {i * 2.0, i * 4.0};
},
}; };
} }
......
...@@ -370,7 +370,7 @@ CAF_TEST(spawn_event_testee2_test) { ...@@ -370,7 +370,7 @@ CAF_TEST(spawn_event_testee2_test) {
CAF_TEST(function_spawn) { CAF_TEST(function_spawn) {
scoped_actor self{system}; scoped_actor self{system};
auto f = [](const std::string& name) -> behavior { auto f = [](const std::string& name) -> behavior {
return ([name](get_atom) { return std::make_tuple(name_atom_v, name); }); return ([name](get_atom) { return make_result(name_atom_v, name); });
}; };
auto a1 = system.spawn(f, "alice"); auto a1 = system.spawn(f, "alice");
auto a2 = system.spawn(f, "bob"); auto a2 = system.spawn(f, "bob");
......
...@@ -45,9 +45,9 @@ calculator::behavior_type multiplier() { ...@@ -45,9 +45,9 @@ calculator::behavior_type multiplier() {
calculator::behavior_type divider() { calculator::behavior_type divider() {
return { return {
[](int x, int y) -> optional<int> { [](int x, int y) -> result<int> {
if (y == 0) if (y == 0)
return none; return make_error(sec::runtime_error, "division by zero");
return x / y; return x / y;
}, },
}; };
...@@ -57,7 +57,9 @@ using doubler = typed_actor<replies_to<int>::with<int, int>>; ...@@ -57,7 +57,9 @@ using doubler = typed_actor<replies_to<int>::with<int, int>>;
doubler::behavior_type simple_doubler() { doubler::behavior_type simple_doubler() {
return { return {
[](int x) { return std::make_tuple(x, x); }, [](int x) -> result<int, int> {
return {x, x};
},
}; };
} }
......
...@@ -165,34 +165,18 @@ CAF_TEST(typed_behavior_assignment) { ...@@ -165,34 +165,18 @@ CAF_TEST(typed_behavior_assignment) {
replies_to<double, double>::with<int, int>>; replies_to<double, double>::with<int, int>>;
// compatible handlers resulting in perfect match // compatible handlers resulting in perfect match
auto f1 = [=](int) { return 0.; }; auto f1 = [=](int) { return 0.; };
auto f2 = [=](double, double) { return std::make_tuple(0, 0); }; auto f2 = [=](double, double) -> result<int, int> { return {0, 0}; };
// compatible handlers using skip
auto g1 = [=](int) { return skip(); };
auto g2 = [=](double, double) { return skip(); };
// incompatbile handlers // incompatbile handlers
auto e1 = [=](int) { return 0.f; }; auto e1 = [=](int) { return 0.f; };
auto e2 = [=](double, double) { return std::make_tuple(0.f, 0.f); }; auto e2 = [=](double, double) { return std::make_tuple(0.f, 0.f); };
// omit one handler // omit one handler
CAF_CHECK_EQUAL(bi_pair(false, -1), tb_assign<bh1>(f1)); CAF_CHECK_EQUAL(bi_pair(false, -1), tb_assign<bh1>(f1));
CAF_CHECK_EQUAL(bi_pair(false, -1), tb_assign<bh1>(f2)); CAF_CHECK_EQUAL(bi_pair(false, -1), tb_assign<bh1>(f2));
CAF_CHECK_EQUAL(bi_pair(false, -1), tb_assign<bh1>(g1));
CAF_CHECK_EQUAL(bi_pair(false, -1), tb_assign<bh1>(g2));
CAF_CHECK_EQUAL(bi_pair(false, -1), tb_assign<bh1>(e1)); CAF_CHECK_EQUAL(bi_pair(false, -1), tb_assign<bh1>(e1));
CAF_CHECK_EQUAL(bi_pair(false, -1), tb_assign<bh1>(e2)); CAF_CHECK_EQUAL(bi_pair(false, -1), tb_assign<bh1>(e2));
// any valid alteration of (f1, f2, g1, g2) // any valid alteration of (f1, f2)
CAF_CHECK_EQUAL(bi_pair(true, 2), tb_assign<bh1>(f1, f2)); CAF_CHECK_EQUAL(bi_pair(true, 2), tb_assign<bh1>(f1, f2));
CAF_CHECK_EQUAL(bi_pair(true, 2), tb_assign<bh1>(f2, f1)); CAF_CHECK_EQUAL(bi_pair(true, 2), tb_assign<bh1>(f2, f1));
CAF_CHECK_EQUAL(bi_pair(true, 2), tb_assign<bh1>(g1, g2));
CAF_CHECK_EQUAL(bi_pair(true, 2), tb_assign<bh1>(g2, g1));
CAF_CHECK_EQUAL(bi_pair(true, 2), tb_assign<bh1>(g1, f2));
CAF_CHECK_EQUAL(bi_pair(true, 2), tb_assign<bh1>(f2, g1));
CAF_CHECK_EQUAL(bi_pair(true, 2), tb_assign<bh1>(f1, g2));
CAF_CHECK_EQUAL(bi_pair(true, 2), tb_assign<bh1>(g2, f1));
// any invalid alteration of (f1, f2, g1, g2)
CAF_CHECK_EQUAL(bi_pair(false, 1), tb_assign<bh1>(f1, g1));
CAF_CHECK_EQUAL(bi_pair(false, 1), tb_assign<bh1>(g1, f1));
CAF_CHECK_EQUAL(bi_pair(false, 1), tb_assign<bh1>(f2, g2));
CAF_CHECK_EQUAL(bi_pair(false, 1), tb_assign<bh1>(g2, g2));
// any invalid alteration of (f1, f2, e1, e2) // any invalid alteration of (f1, f2, e1, e2)
CAF_CHECK_EQUAL(bi_pair(false, 1), tb_assign<bh1>(f1, e1)); CAF_CHECK_EQUAL(bi_pair(false, 1), tb_assign<bh1>(f1, e1));
CAF_CHECK_EQUAL(bi_pair(false, 1), tb_assign<bh1>(f1, e2)); CAF_CHECK_EQUAL(bi_pair(false, 1), tb_assign<bh1>(f1, e2));
......
...@@ -32,17 +32,10 @@ template<class T> ...@@ -32,17 +32,10 @@ template<class T>
void test_unit_void() { void test_unit_void() {
auto x = result<T>{}; auto x = result<T>{};
CAF_CHECK(holds_alternative<message>(x)); CAF_CHECK(holds_alternative<message>(x));
x = skip;
CAF_CHECK(holds_alternative<skip_t>(x));
} }
} // namespace anonymous } // namespace anonymous
CAF_TEST(skip) {
auto x = result<int>{skip};
CAF_CHECK(holds_alternative<skip_t>(x));
}
CAF_TEST(value) { CAF_TEST(value) {
auto x = result<int>{42}; auto x = result<int>{42};
CAF_REQUIRE(holds_alternative<message>(x)); CAF_REQUIRE(holds_alternative<message>(x));
......
...@@ -108,36 +108,33 @@ using event_testee_type ...@@ -108,36 +108,33 @@ using event_testee_type
class event_testee : public event_testee_type::base { class event_testee : public event_testee_type::base {
public: public:
event_testee(actor_config& cfg) : event_testee_type::base(cfg) { event_testee(actor_config& cfg) : event_testee_type::base(cfg) {
// nop set_default_handler(skip);
} }
behavior_type wait4string() { behavior_type wait4string() {
return { return {
partial_behavior_init,
[=](get_state_atom) { return "wait4string"; }, [=](get_state_atom) { return "wait4string"; },
[=](const string&) { become(wait4int()); }, [=](const string&) { become(wait4int()); },
[=](float) { return skip(); },
[=](int) { return skip(); },
}; };
} }
behavior_type wait4int() { behavior_type wait4int() {
return { return {
partial_behavior_init,
[=](get_state_atom) { return "wait4int"; }, [=](get_state_atom) { return "wait4int"; },
[=](int) -> int { [=](int) -> int {
become(wait4float()); become(wait4float());
return 42; return 42;
}, },
[=](float) { return skip(); },
[=](const string&) { return skip(); },
}; };
} }
behavior_type wait4float() { behavior_type wait4float() {
return { return {
partial_behavior_init,
[=](get_state_atom) { return "wait4float"; }, [=](get_state_atom) { return "wait4float"; },
[=](float) { become(wait4string()); }, [=](float) { become(wait4string()); },
[=](const string&) { return skip(); },
[=](int) { return skip(); },
}; };
} }
...@@ -365,7 +362,7 @@ CAF_TEST(sending_typed_actors_and_down_msg) { ...@@ -365,7 +362,7 @@ CAF_TEST(sending_typed_actors_and_down_msg) {
CAF_TEST(check_signature) { CAF_TEST(check_signature) {
using foo_type = typed_actor<replies_to<put_atom>::with<ok_atom>>; using foo_type = typed_actor<replies_to<put_atom>::with<ok_atom>>;
using foo_result_type = optional<ok_atom>; using foo_result_type = result<ok_atom>;
using bar_type = typed_actor<reacts_to<ok_atom>>; using bar_type = typed_actor<reacts_to<ok_atom>>;
auto foo_action = [](foo_type::pointer ptr) -> foo_type::behavior_type { auto foo_action = [](foo_type::pointer ptr) -> foo_type::behavior_type {
return { return {
......
...@@ -366,8 +366,7 @@ behavior basp_broker::make_behavior() { ...@@ -366,8 +366,7 @@ behavior basp_broker::make_behavior() {
return unit; return unit;
return sec::cannot_close_invalid_port; return sec::cannot_close_invalid_port;
}, },
[=](get_atom, [=](get_atom, const node_id& x) -> result<node_id, std::string, uint16_t> {
const node_id& x) -> std::tuple<node_id, std::string, uint16_t> {
std::string addr; std::string addr;
uint16_t port = 0; uint16_t port = 0;
auto hdl = instance.tbl().lookup_direct(x); auto hdl = instance.tbl().lookup_direct(x);
...@@ -375,7 +374,7 @@ behavior basp_broker::make_behavior() { ...@@ -375,7 +374,7 @@ behavior basp_broker::make_behavior() {
addr = remote_addr(*hdl); addr = remote_addr(*hdl);
port = remote_port(*hdl); port = remote_port(*hdl);
} }
return std::make_tuple(x, std::move(addr), port); return {x, std::move(addr), port};
}, },
[=](tick_atom, size_t interval) { [=](tick_atom, size_t interval) {
instance.handle_heartbeat(context()); instance.handle_heartbeat(context());
......
...@@ -126,11 +126,11 @@ behavior peer_acceptor_fun(broker* self, const actor& buddy) { ...@@ -126,11 +126,11 @@ behavior peer_acceptor_fun(broker* self, const actor& buddy) {
self->fork(peer_fun, msg.handle, buddy); self->fork(peer_fun, msg.handle, buddy);
self->quit(); self->quit();
}, },
[=](publish_atom) -> expected<uint16_t> { [=](publish_atom) -> result<uint16_t> {
auto res = self->add_tcp_doorman(8080); if (auto res = self->add_tcp_doorman(8080))
if (!res)
return std::move(res.error());
return res->second; return res->second;
else
return std::move(res.error());
}, },
}; };
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment