Commit 0f1831cd authored by Dominik Charousset's avatar Dominik Charousset

Improve error reporting on function-based actors

parent 0b9b191e
...@@ -40,5 +40,11 @@ struct actor_marker<behavior> { ...@@ -40,5 +40,11 @@ struct actor_marker<behavior> {
using type = dynamically_typed_actor_base; using type = dynamically_typed_actor_base;
}; };
template <class T>
using is_statically_typed = std::is_base_of<statically_typed_actor_base, T>;
template <class T>
using is_dynamically_typed = std::is_base_of<dynamically_typed_actor_base, T>;
} // namespace caf } // namespace caf
...@@ -32,10 +32,12 @@ ...@@ -32,10 +32,12 @@
#include "caf/actor_cast.hpp" #include "caf/actor_cast.hpp"
#include "caf/actor_clock.hpp" #include "caf/actor_clock.hpp"
#include "caf/actor_config.hpp" #include "caf/actor_config.hpp"
#include "caf/actor_marker.hpp"
#include "caf/actor_registry.hpp" #include "caf/actor_registry.hpp"
#include "caf/composable_behavior_based_actor.hpp" #include "caf/composable_behavior_based_actor.hpp"
#include "caf/detail/init_fun_factory.hpp" #include "caf/detail/init_fun_factory.hpp"
#include "caf/detail/spawn_fwd.hpp" #include "caf/detail/spawn_fwd.hpp"
#include "caf/detail/spawnable.hpp"
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
#include "caf/group_manager.hpp" #include "caf/group_manager.hpp"
#include "caf/infer_handle.hpp" #include "caf/infer_handle.hpp"
...@@ -328,30 +330,27 @@ public: ...@@ -328,30 +330,27 @@ public:
return spawn<composable_behavior_based_actor<S>, Os>(); return spawn<composable_behavior_based_actor<S>, Os>();
} }
/// Called by `spawn_functor` to apply static assertions and /// Called by `spawn` when used to create a functor-based actor to select a
/// store an initialization function in `cfg` before calling `spawn_class`. /// proper implementation and then delegates to `spawn_impl`.
/// @param cfg To-be-filled config for the actor. /// @param cfg To-be-filled config for the actor.
/// @param fun Function object for the actor's behavior; will be moved. /// @param fun Function object for the actor's behavior; will be moved.
/// @param xs Arguments for `fun`. /// @param xs Arguments for `fun`.
template <spawn_options Os, class C, class F, class... Ts> /// @private
infer_handle_from_class_t<C> template <spawn_options Os = no_spawn_options, class F, class... Ts>
spawn_functor_impl(actor_config& cfg, F& fun, Ts&&... xs) { infer_handle_from_fun_t<F> spawn_functor(std::true_type, actor_config& cfg,
detail::init_fun_factory<C, F> fac; F& fun, Ts&&... xs) {
using impl = infer_impl_from_fun_t<F>;
detail::init_fun_factory<impl, F> fac;
cfg.init_fun = fac(std::move(fun), std::forward<Ts>(xs)...); cfg.init_fun = fac(std::move(fun), std::forward<Ts>(xs)...);
return spawn_impl<C, Os>(cfg); return spawn_impl<impl, Os>(cfg);
} }
/// Called by `spawn` when used to create a functor-based actor to select /// Fallback no-op overload.
/// a proper implementation and then delegates to `spawn_functor_impl`. /// @private
/// Should not be called by users of the library directly.
/// @param cfg To-be-filled config for the actor.
/// @param fun Function object for the actor's behavior; will be moved.
/// @param xs Arguments for `fun`.
template <spawn_options Os = no_spawn_options, class F, class... Ts> template <spawn_options Os = no_spawn_options, class F, class... Ts>
infer_handle_from_fun_t<F> infer_handle_from_fun_t<F> spawn_functor(std::false_type, actor_config&, F&,
spawn_functor(actor_config& cfg, F& fun, Ts&&... xs) { Ts&&...) {
using impl = infer_impl_from_fun_t<F>; return {};
return spawn_functor_impl<Os, impl>(cfg, fun, std::forward<Ts>(xs)...);
} }
/// Returns a new functor-based actor. The first argument must be the functor, /// Returns a new functor-based actor. The first argument must be the functor,
...@@ -361,9 +360,14 @@ public: ...@@ -361,9 +360,14 @@ public:
template <spawn_options Os = no_spawn_options, class F, class... Ts> template <spawn_options Os = no_spawn_options, class F, class... Ts>
infer_handle_from_fun_t<F> infer_handle_from_fun_t<F>
spawn(F fun, Ts&&... xs) { spawn(F fun, Ts&&... xs) {
check_invariants<infer_impl_from_fun_t<F>>(); using impl = infer_impl_from_fun_t<F>;
check_invariants<impl>();
static constexpr bool spawnable = detail::spawnable<F, impl, Ts...>();
static_assert(spawnable,
"cannot spawn function-based actor with given arguments");
actor_config cfg; actor_config cfg;
return spawn_functor<Os>(cfg, fun, std::forward<Ts>(xs)...); return spawn_functor<Os>(detail::bool_token<spawnable>{}, cfg, fun,
std::forward<Ts>(xs)...);
} }
/// Returns a new actor with run-time type `name`, constructed /// Returns a new actor with run-time type `name`, constructed
...@@ -393,7 +397,7 @@ public: ...@@ -393,7 +397,7 @@ public:
infer_handle_from_class_t<T> infer_handle_from_class_t<T>
spawn_class_in_groups(actor_config& cfg, Iter first, Iter last, Ts&&... xs) { spawn_class_in_groups(actor_config& cfg, Iter first, Iter last, Ts&&... xs) {
static_assert(std::is_same<infer_handle_from_class_t<T>, actor>::value, static_assert(std::is_same<infer_handle_from_class_t<T>, actor>::value,
"Only dynamically typed actors can be spawned in a group."); "only dynamically-typed actors can be spawned in a group");
check_invariants<T>(); check_invariants<T>();
auto irange = make_input_range(first, last); auto irange = make_input_range(first, last);
cfg.groups = &irange; cfg.groups = &irange;
...@@ -407,12 +411,19 @@ public: ...@@ -407,12 +411,19 @@ public:
infer_handle_from_fun_t<F> infer_handle_from_fun_t<F>
spawn_fun_in_groups(actor_config& cfg, Iter first, Iter second, spawn_fun_in_groups(actor_config& cfg, Iter first, Iter second,
F& fun, Ts&&... xs) { F& fun, Ts&&... xs) {
static_assert(std::is_same<infer_handle_from_fun_t<F>, actor>::value, using impl = infer_impl_from_fun_t<F>;
"Only dynamically actors can be spawned in a group."); check_invariants<impl>();
check_invariants<infer_impl_from_fun_t<F>>(); static constexpr bool dynamically_typed = is_dynamically_typed<impl>::value;
static_assert(dynamically_typed,
"only dynamically-typed actors can join groups");
static constexpr bool spawnable = detail::spawnable<F, impl, Ts...>();
static_assert(spawnable,
"cannot spawn function-based actor with given arguments");
static constexpr bool enabled = dynamically_typed && spawnable;
auto irange = make_input_range(first, second); auto irange = make_input_range(first, second);
cfg.groups = &irange; cfg.groups = &irange;
return spawn_functor<Os>(cfg, fun, std::forward<Ts>(xs)...); return spawn_functor<Os>(detail::bool_token<enabled>{}, cfg, fun,
std::forward<Ts>(xs)...);
} }
/// Returns a new functor-based actor subscribed to all groups in `gs`. /// Returns a new functor-based actor subscribed to all groups in `gs`.
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 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 <type_traits>
#include "caf/detail/type_traits.hpp"
#include "caf/infer_handle.hpp"
namespace caf {
namespace detail {
/// Returns whether the function object `F` is spawnable from the actor
/// implementation `Impl` with arguments of type `Ts...`.
template <class F, class Impl, class... Ts>
constexpr bool spawnable() {
return is_callable_with<F, Ts...>::value
|| is_callable_with<F, Impl*, Ts...>::value;
}
} // namespace detail
} // namespace caf
...@@ -694,6 +694,10 @@ struct is_same_ish ...@@ -694,6 +694,10 @@ struct is_same_ish
is_equal_int_type<T, U> is_equal_int_type<T, U>
>::type { }; >::type { };
/// Utility for fallbacks calling `static_assert`.
template <class>
struct always_false : std::false_type {};
} // namespace detail } // namespace detail
} // namespace caf } // namespace caf
...@@ -114,8 +114,8 @@ struct infer_handle_from_fun { ...@@ -114,8 +114,8 @@ struct infer_handle_from_fun {
}; };
/// @relates infer_handle_from_fun /// @relates infer_handle_from_fun
template <class T> template <class F>
using infer_handle_from_fun_t = typename infer_handle_from_fun<T>::type; using infer_handle_from_fun_t = typename infer_handle_from_fun<F>::type;
/// @relates infer_handle_from_fun /// @relates infer_handle_from_fun
template <class T> template <class T>
......
...@@ -34,6 +34,8 @@ ...@@ -34,6 +34,8 @@
#include "caf/behavior.hpp" #include "caf/behavior.hpp"
#include "caf/check_typed_input.hpp" #include "caf/check_typed_input.hpp"
#include "caf/delegated.hpp" #include "caf/delegated.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/detail/typed_actor_util.hpp"
#include "caf/duration.hpp" #include "caf/duration.hpp"
#include "caf/error.hpp" #include "caf/error.hpp"
#include "caf/fwd.hpp" #include "caf/fwd.hpp"
...@@ -49,8 +51,6 @@ ...@@ -49,8 +51,6 @@
#include "caf/typed_actor.hpp" #include "caf/typed_actor.hpp"
#include "caf/typed_response_promise.hpp" #include "caf/typed_response_promise.hpp"
#include "caf/detail/typed_actor_util.hpp"
namespace caf { namespace caf {
/// Base class for actors running on this node, either /// Base class for actors running on this node, either
...@@ -109,9 +109,16 @@ public: ...@@ -109,9 +109,16 @@ public:
template <spawn_options Os = no_spawn_options, class F, class... Ts> template <spawn_options Os = no_spawn_options, class F, class... Ts>
infer_handle_from_fun_t<F> spawn(F fun, Ts&&... xs) { infer_handle_from_fun_t<F> spawn(F fun, Ts&&... xs) {
using impl = infer_impl_from_fun_t<F>;
static constexpr bool spawnable = detail::spawnable<F, impl, Ts...>();
static_assert(spawnable,
"cannot spawn function-based actor with given arguments");
actor_config cfg{context()}; actor_config cfg{context()};
return eval_opts(Os, system().spawn_functor<make_unbound(Os)>( static constexpr spawn_options unbound = make_unbound(Os);
cfg, fun, std::forward<Ts>(xs)...)); detail::bool_token<spawnable> enabled;
return eval_opts(Os,
system().spawn_functor<unbound>(enabled, cfg, fun,
std::forward<Ts>(xs)...));
} }
template <class T, spawn_options Os = no_spawn_options, class Groups, template <class T, spawn_options Os = no_spawn_options, class Groups,
......
...@@ -255,8 +255,14 @@ public: ...@@ -255,8 +255,14 @@ public:
class F = std::function<void(broker*)>, class... Ts> class F = std::function<void(broker*)>, class... Ts>
typename infer_handle_from_fun<F>::type typename infer_handle_from_fun<F>::type
spawn_broker(F fun, Ts&&... xs) { spawn_broker(F fun, Ts&&... xs) {
using impl = infer_impl_from_fun_t<F>;
static constexpr bool spawnable = detail::spawnable<F, impl, Ts...>();
static_assert(spawnable,
"cannot spawn function-based broker with given arguments");
actor_config cfg{&backend()}; actor_config cfg{&backend()};
return system().spawn_functor<Os>(cfg, fun, std::forward<Ts>(xs)...); detail::bool_token<spawnable> enabled;
return system().spawn_functor<Os>(enabled, cfg, fun,
std::forward<Ts>(xs)...);
} }
/// Returns a new functor-based broker connected /// Returns a new functor-based broker connected
......
...@@ -119,7 +119,14 @@ public: ...@@ -119,7 +119,14 @@ public:
actor_config cfg{this->context()}; actor_config cfg{this->context()};
detail::init_fun_factory<impl, F> fac; detail::init_fun_factory<impl, F> fac;
cfg.init_fun = fac(std::move(fun), hdl, std::forward<Ts>(xs)...); cfg.init_fun = fac(std::move(fun), hdl, std::forward<Ts>(xs)...);
auto res = this->system().spawn_functor(cfg, fun, hdl, std::forward<Ts>(xs)...); using impl = infer_impl_from_fun_t<F>;
static constexpr bool spawnable = detail::spawnable<F, impl, decltype(hdl),
Ts...>();
static_assert(spawnable,
"cannot spawn function-based broker with given arguments");
detail::bool_token<spawnable> enabled;
auto res = this->system().spawn_functor(enabled, cfg, fun, hdl,
std::forward<Ts>(xs)...);
auto forked = static_cast<impl*>(actor_cast<abstract_actor*>(res)); auto forked = static_cast<impl*>(actor_cast<abstract_actor*>(res));
forked->move_scribe(std::move(sptr)); forked->move_scribe(std::move(sptr));
return res; return res;
......
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