Unverified Commit ce63edff authored by Noir's avatar Noir Committed by GitHub

Merge pull request #1272

Modernize functor_attachable and allow non-const default handlers
parents a920cf3d 3731fc84
......@@ -20,6 +20,8 @@ is based on [Keep a Changelog](https://keepachangelog.com).
properly back off, causing high CPU load due to spinning and in some scenarios
never recovering. This issue has been resolved by properly handling
`SSL_ERROR_WANT_READ` on the transport (#1060).
- Scheduled actors now accept default handlers for down messages etc. with
non-const apply operator such as lambda expressions declared as `mutable`.
### Removed
......
......@@ -324,6 +324,7 @@ caf_add_component(
response_promise
result
save_inspector
scheduled_actor
selective_streaming
serial_reply
serialization
......
......@@ -11,41 +11,31 @@
namespace caf::detail {
template <class F,
int Args = tl_size<typename get_callable_trait<F>::arg_types>::value>
struct functor_attachable : attachable {
static_assert(Args == 1 || Args == 2,
"Only 0, 1 or 2 arguments for F are supported");
F functor_;
functor_attachable(F arg) : functor_(std::move(arg)) {
// nop
}
void actor_exited(const error& fail_state, execution_unit*) override {
functor_(fail_state);
}
static constexpr size_t token_type = attachable::token::anonymous;
};
template <class F>
struct functor_attachable<F, 2> : attachable {
F functor_;
functor_attachable(F arg) : functor_(std::move(arg)) {
// nop
}
void actor_exited(const error& x, execution_unit* y) override {
functor_(x, y);
}
};
class functor_attachable : public attachable {
public:
static constexpr size_t num_args
= tl_size<typename get_callable_trait<F>::arg_types>::value;
template <class F>
struct functor_attachable<F, 0> : attachable {
F functor_;
functor_attachable(F arg) : functor_(std::move(arg)) {
static_assert(num_args < 3, "Only 0, 1 or 2 arguments for F are supported");
explicit functor_attachable(F fn) : fn_(std::move(fn)) {
// nop
}
void actor_exited(const error&, execution_unit*) override {
functor_();
void actor_exited(const error& fail_state, execution_unit* host) override {
if constexpr (num_args == 0)
fn_();
else if constexpr (num_args == 1)
fn_(fail_state);
else
fn_(fail_state, host);
}
static constexpr size_t token_type = attachable::token::anonymous;
private:
F fn_;
};
} // namespace caf::detail
......@@ -294,10 +294,12 @@ public:
/// Sets a custom handler for unexpected messages.
template <class F>
typename std::enable_if<std::is_convertible<
F, std::function<skippable_result(message&)>>::value>::type
std::enable_if_t<std::is_invocable_r_v<skippable_result, F, message&>>
set_default_handler(F fun) {
default_handler_ = [=](scheduled_actor*, message& xs) { return fun(xs); };
using std::move;
default_handler_ = [fn{move(fun)}](scheduled_actor*, message& xs) mutable {
return fn(xs);
};
}
/// Sets a custom handler for error messages.
......@@ -309,9 +311,11 @@ public:
}
/// Sets a custom handler for error messages.
template <class T>
auto set_error_handler(T fun) -> decltype(fun(std::declval<error&>())) {
set_error_handler([fun](scheduled_actor*, error& x) { fun(x); });
template <class F>
std::enable_if_t<std::is_invocable_v<F, error&>> set_error_handler(F fun) {
error_handler_ = [fn{std::move(fun)}](scheduled_actor*, error& x) mutable {
fn(x);
};
}
/// Sets a custom handler for down messages.
......@@ -323,9 +327,12 @@ public:
}
/// Sets a custom handler for down messages.
template <class T>
auto set_down_handler(T fun) -> decltype(fun(std::declval<down_msg&>())) {
set_down_handler([fun](scheduled_actor*, down_msg& x) { fun(x); });
template <class F>
std::enable_if_t<std::is_invocable_v<F, down_msg&>> set_down_handler(F fun) {
using std::move;
down_handler_ = [fn{move(fun)}](scheduled_actor*, down_msg& x) mutable {
fn(x);
};
}
/// Sets a custom handler for node down messages.
......@@ -337,11 +344,13 @@ public:
}
/// Sets a custom handler for down messages.
template <class T>
auto set_node_down_handler(T fun)
-> decltype(fun(std::declval<node_down_msg&>())) {
set_node_down_handler(
[fun](scheduled_actor*, node_down_msg& x) { fun(x); });
template <class F>
std::enable_if_t<std::is_invocable_v<F, node_down_msg&>>
set_node_down_handler(F fun) {
node_down_handler_ = [fn{std::move(fun)}](scheduled_actor*,
node_down_msg& x) mutable {
fn(x);
};
}
/// Sets a custom handler for error messages.
......@@ -353,9 +362,12 @@ public:
}
/// Sets a custom handler for exit messages.
template <class T>
auto set_exit_handler(T fun) -> decltype(fun(std::declval<exit_msg&>())) {
set_exit_handler([fun](scheduled_actor*, exit_msg& x) { fun(x); });
template <class F>
std::enable_if_t<std::is_invocable_v<F, exit_msg&>> set_exit_handler(F fun) {
using std::move;
exit_handler_ = [fn{move(fun)}](scheduled_actor*, exit_msg& x) mutable {
fn(x);
};
}
#ifdef CAF_ENABLE_EXCEPTIONS
......@@ -371,11 +383,12 @@ public:
/// Sets a custom exception handler for this actor. If multiple handlers are
/// defined, only the functor that was added *last* is being executed.
template <class F>
typename std::enable_if<std::is_convertible<
F, std::function<error(std::exception_ptr&)>>::value>::type
set_exception_handler(F f) {
set_exception_handler(
[f](scheduled_actor*, std::exception_ptr& x) { return f(x); });
std::enable_if_t<std::is_invocable_r_v<error, F, std::exception_ptr&>>
set_exception_handler(F fun) {
exception_handler_ = [fn{std::move(fun)}](scheduled_actor*,
std::exception_ptr& x) mutable {
return fn(x);
};
}
#endif // CAF_ENABLE_EXCEPTIONS
......
// 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 scheduled_actor
#include "caf/scheduled_actor.hpp"
#include "caf/test/dsl.hpp"
using namespace caf;
#define ASSERT_COMPILES(expr, msg) \
static_assert(std::is_void_v<decltype(expr)>, msg);
namespace {
constexpr scheduled_actor* nil_actor = nullptr;
// -- compile-time checks for set_default_handler ------------------------------
struct mutable_default_fn {
skippable_result operator()(message&) {
return {};
}
};
ASSERT_COMPILES(nil_actor->set_default_handler(mutable_default_fn{}),
"set_default_handler must accept mutable function objects");
struct const_default_fn {
skippable_result operator()(message&) const {
return {};
}
};
ASSERT_COMPILES(nil_actor->set_default_handler(const_default_fn{}),
"set_default_handler must accept const function objects");
// -- compile-time checks for set_error_handler --------------------------------
struct mutable_error_fn {
void operator()(error&) {
// nop
}
};
ASSERT_COMPILES(nil_actor->set_error_handler(mutable_error_fn{}),
"set_error_handler must accept mutable function objects");
struct const_error_fn {
void operator()(error&) const {
// nop
}
};
ASSERT_COMPILES(nil_actor->set_error_handler(const_error_fn{}),
"set_error_handler must accept const function objects");
// -- compile-time checks for set_down_handler ---------------------------------
struct mutable_down_fn {
void operator()(down_msg&) {
// nop
}
};
ASSERT_COMPILES(nil_actor->set_down_handler(mutable_down_fn{}),
"set_down_handler must accept mutable function objects");
struct const_down_fn {
void operator()(down_msg&) const {
// nop
}
};
ASSERT_COMPILES(nil_actor->set_down_handler(const_down_fn{}),
"set_down_handler must accept const function objects");
// -- compile-time checks for set_node_down_handler ----------------------------
struct mutable_node_down_fn {
void operator()(node_down_msg&) {
// nop
}
};
ASSERT_COMPILES(nil_actor->set_node_down_handler(mutable_node_down_fn{}),
"set_node_down_handler must accept mutable function objects");
struct const_node_down_fn {
void operator()(node_down_msg&) const {
// nop
}
};
ASSERT_COMPILES(nil_actor->set_node_down_handler(const_node_down_fn{}),
"set_node_down_handler must accept const function objects");
// -- compile-time checks for set_exit_handler ---------------------------------
struct mutable_exit_fn {
void operator()(exit_msg&) {
// nop
}
};
ASSERT_COMPILES(nil_actor->set_exit_handler(mutable_exit_fn{}),
"set_exit_handler must accept mutable function objects");
struct const_exit_fn {
void operator()(exit_msg&) const {
// nop
}
};
ASSERT_COMPILES(nil_actor->set_exit_handler(const_exit_fn{}),
"set_exit_handler must accept const function objects");
// -- compile-time checks for set_exception_handler ----------------------------
#ifdef CAF_ENABLE_EXCEPTIONS
struct mutable_exception_fn {
error operator()(std::exception_ptr&) {
return {};
}
};
ASSERT_COMPILES(nil_actor->set_exception_handler(mutable_exception_fn{}),
"set_exception_handler must accept mutable function objects");
struct const_exception_fn {
error operator()(std::exception_ptr&) const {
return {};
}
};
ASSERT_COMPILES(nil_actor->set_exception_handler(const_exception_fn{}),
"set_exception_handler must accept const function objects");
#endif // CAF_ENABLE_EXCEPTIONS
} // namespace
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