Commit 779d9171 authored by Dominik Charousset's avatar Dominik Charousset

Allow spawning composable states directly

parent 63e96260
......@@ -37,6 +37,7 @@
#include "caf/string_algorithms.hpp"
#include "caf/scoped_execution_unit.hpp"
#include "caf/uniform_type_info_map.hpp"
#include "caf/composable_state_based_actor.hpp"
#include "caf/prohibit_top_level_spawn_marker.hpp"
#include "caf/detail/spawn_fwd.hpp"
......@@ -242,7 +243,7 @@ public:
/// @param cfg To-be-filled config for the actor.
/// @param xs Constructor arguments for `C`.
template <class C, spawn_options Os, class... Ts>
typename infer_handle_from_class<C>::type
infer_handle_from_class_t<C>
spawn_class(actor_config& cfg, Ts&&... xs) {
return spawn_impl<C, Os>(cfg, detail::spawn_fwd<Ts>(xs)...);
}
......@@ -252,26 +253,30 @@ public:
/// to opt-out of the cooperative scheduling.
/// @param xs Constructor arguments for `C`.
template <class C, spawn_options Os = no_spawn_options, class... Ts>
typename infer_handle_from_class<C>::type spawn(Ts&&... xs) {
infer_handle_from_class_t<C> spawn(Ts&&... xs) {
check_invariants<C>();
actor_config cfg;
return spawn_impl<C, Os>(cfg, detail::spawn_fwd<Ts>(xs)...);
}
template <class S, spawn_options Os = no_spawn_options>
infer_handle_from_state_t<S> spawn() {
return spawn<composable_state_based_actor<S>, Os>();
}
/// Called by `spawn_functor` to apply static assertions and
/// store an initialization function in `cfg` before calling `spawn_class`.
/// @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, class C, class F, class... Ts>
typename infer_handle_from_class<C>::type
infer_handle_from_class_t<C>
spawn_functor_impl(actor_config& cfg, F& fun, Ts&&... xs) {
constexpr bool has_blocking_base =
std::is_base_of<blocking_actor, C>::value;
static_assert(has_blocking_base || ! has_blocking_api_flag(Os),
constexpr bool is_blocking = std::is_base_of<blocking_actor, C>::value;
static_assert(is_blocking || ! has_blocking_api_flag(Os),
"blocking functor-based actors "
"need to be spawned using the blocking_api flag");
static_assert(! has_blocking_base || has_blocking_api_flag(Os),
static_assert(! is_blocking || has_blocking_api_flag(Os),
"non-blocking functor-based actors "
"cannot be spawned using the blocking_api flag");
detail::init_fun_factory<C, F> fac;
......@@ -286,9 +291,9 @@ public:
/// @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>
typename infer_handle_from_fun<F>::type
infer_handle_from_fun_t<F>
spawn_functor(actor_config& cfg, F& fun, Ts&&... xs) {
using impl = typename infer_handle_from_fun<F>::impl;
using impl = infer_impl_from_fun_t<F>;
return spawn_functor_impl<Os, impl>(cfg, fun, std::forward<Ts>(xs)...);
}
......@@ -297,15 +302,16 @@ public:
/// The behavior of `spawn` can be modified by setting `Os`, e.g.,
/// to opt-out of the cooperative scheduling.
template <spawn_options Os = no_spawn_options, class F, class... Ts>
typename infer_handle_from_fun<F>::type
infer_handle_from_fun_t<F>
spawn(F fun, Ts&&... xs) {
check_invariants<typename infer_handle_from_fun<F>::impl>();
check_invariants<infer_impl_from_fun_t<F>>();
actor_config cfg;
return spawn_functor<Os>(cfg, fun, std::forward<Ts>(xs)...);
}
template <class T, spawn_options Os = no_spawn_options, class Iter, class F, class... Ts>
typename infer_handle_from_class<T>::type
template <class T, spawn_options Os = no_spawn_options,
class Iter, class F, class... Ts>
infer_handle_from_class_t<T>
spawn_in_groups_impl(actor_config& cfg, Iter first, Iter second, Ts&&... xs) {
check_invariants<T>();
auto irange = make_input_range(first, second);
......@@ -313,11 +319,12 @@ public:
return spawn_class<T, Os>(cfg, std::forward<Ts>(xs)...);
}
template <spawn_options Os = no_spawn_options, class Iter, class F, class... Ts>
typename infer_handle_from_fun<F>::type
template <spawn_options Os = no_spawn_options,
class Iter, class F, class... Ts>
infer_handle_from_fun_t<F>
spawn_in_groups_impl(actor_config& cfg, Iter first, Iter second,
F& fun, Ts&&... xs) {
check_invariants<typename infer_handle_from_fun<F>::impl>();
check_invariants<infer_impl_from_fun_t<F>>();
auto irange = make_input_range(first, second);
cfg.groups = &irange;
return spawn_functor<Os>(cfg, fun, std::forward<Ts>(xs)...);
......@@ -325,7 +332,7 @@ public:
/// Returns a new functor-based actor subscribed to all groups in `gs`.
template <spawn_options Os = no_spawn_options, class F, class... Ts>
typename infer_handle_from_fun<F>::type
infer_handle_from_fun_t<F>
spawn_in_groups(std::initializer_list<group> gs, F fun, Ts&&... xs) {
actor_config cfg;
return spawn_in_groups_impl(cfg, gs.begin(), gs.end(), fun,
......@@ -334,7 +341,7 @@ public:
/// Returns a new functor-based actor subscribed to all groups in `gs`.
template <spawn_options Os = no_spawn_options, class Gs, class F, class... Ts>
typename infer_handle_from_fun<F>::type
infer_handle_from_fun_t<F>
spawn_in_groups(const Gs& gs, F fun, Ts&&... xs) {
actor_config cfg;
return spawn_in_groups_impl(cfg, gs.begin(), gs.end(), fun,
......@@ -343,14 +350,14 @@ public:
/// Returns a new functor-based actor subscribed to all groups in `gs`.
template <spawn_options Os = no_spawn_options, class F, class... Ts>
typename infer_handle_from_fun<F>::type
infer_handle_from_fun_t<F>
spawn_in_group(const group& grp, F fun, Ts&&... xs) {
return spawn_in_groups({grp}, std::move(fun), std::forward<Ts>(xs)...);
}
/// Returns a new class-based actor subscribed to all groups in `gs`.
template <class T, spawn_options Os = no_spawn_options, class... Ts>
typename infer_handle_from_class<T>::type
infer_handle_from_class_t<T>
spawn_in_groups(std::initializer_list<group> gs, Ts&&... xs) {
actor_config cfg;
return spawn_in_groups_impl<T>(cfg, gs.begin(), gs.end(),
......@@ -359,7 +366,7 @@ public:
/// Returns a new class-based actor subscribed to all groups in `gs`.
template <class T, spawn_options Os = no_spawn_options, class Gs, class... Ts>
typename infer_handle_from_class<T>::type
infer_handle_from_class_t<T>
spawn_in_groups(const Gs& gs, Ts&&... xs) {
actor_config cfg;
return spawn_in_groups_impl<T>(cfg, gs.begin(), gs.end(),
......@@ -368,7 +375,7 @@ public:
/// Returns a new class-based actor subscribed to all groups in `gs`.
template <class T, spawn_options Os = no_spawn_options, class... Ts>
typename infer_handle_from_class<T>::type
infer_handle_from_class_t<T>
spawn_in_group(const group& grp, Ts&&... xs) {
return spawn_in_groups<T>({grp}, std::forward<Ts>(xs)...);
}
......@@ -400,7 +407,7 @@ private:
}
template <class C, spawn_options Os, class... Ts>
typename infer_handle_from_class<C>::type
infer_handle_from_class_t<C>
spawn_impl(actor_config& cfg, Ts&&... xs) {
static_assert(! std::is_base_of<blocking_actor, C>::value
|| has_blocking_api_flag(Os),
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* 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. *
******************************************************************************/
#ifndef CAF_COMPOSABLE_STATE_BASED_ACTOR_HPP
#define CAF_COMPOSABLE_STATE_BASED_ACTOR_HPP
#include "caf/stateful_actor.hpp"
namespace caf {
/// Implementation class for spawning composable states directly as actors.
template <class State>
class composable_state_based_actor : public stateful_actor
<State, typename State::actor_base> {
public:
static_assert(! std::is_abstract<State>::value,
"State is abstract, please make sure to override all "
"virtual operator() member functions");
using super = stateful_actor<State, typename State::actor_base>;
composable_state_based_actor(actor_config& cfg) : super(cfg) {
// nop
}
using behavior_type = typename State::behavior_type;
behavior_type make_behavior() override {
this->state.init_selfptr(this);
behavior tmp;
this->state.init_behavior(tmp);
return behavior_type{typename behavior_type::unsafe_init{}, std::move(tmp)};
}
};
} // namespace caf
#endif // CAF_COMPOSABLE_STATE_BASED_ACTOR_HPP
......@@ -24,7 +24,7 @@
#include "caf/actor_addr.hpp"
#include "caf/stateful_actor.hpp"
#include "caf/typed_behavior.hpp"
#include "caf/abstract_composable_state.hpp"
namespace caf {
......@@ -143,6 +143,7 @@ struct infer_handle_from_fun_impl<Result,
static constexpr spawn_mode mode = spawn_mode::function_with_selfptr;
};
/// Deduces an actor handle type from a function or function object.
template <class F, class Trait = typename detail::get_callable_trait<F>::type>
struct infer_handle_from_fun {
using result_type = typename Trait::result_type;
......@@ -156,6 +157,14 @@ struct infer_handle_from_fun {
static constexpr spawn_mode mode = delegate::mode;
};
/// @relates infer_handle_from_fun
template <class T>
using infer_handle_from_fun_t = typename infer_handle_from_fun<T>::type;
/// @relates infer_handle_from_fun
template <class T>
using infer_impl_from_fun_t = typename infer_handle_from_fun<T>::impl;
template <class T>
struct infer_handle_from_behavior {
using type = actor;
......@@ -166,7 +175,9 @@ struct infer_handle_from_behavior<typed_behavior<Sigs...>> {
using type = typed_actor<Sigs...>;
};
template <class T>
/// Deduces `actor` for dynamically typed actors, otherwise `typed_actor<...>`
/// is deduced.
template <class T, bool = std::is_base_of<abstract_actor, T>::value>
struct infer_handle_from_class {
using type =
typename infer_handle_from_behavior<
......@@ -175,6 +186,31 @@ struct infer_handle_from_class {
static constexpr spawn_mode mode = spawn_mode::clazz;
};
template <class T>
struct infer_handle_from_class<T, false> {
// nop; this enables SFINAE for spawn to differentiate between
// spawns using actor classes or composable states
};
/// @relates infer_handle_from_class
template <class T>
using infer_handle_from_class_t = typename infer_handle_from_class<T>::type;
template <class T, bool = std::is_base_of<abstract_composable_state, T>::value>
struct infer_handle_from_state {
using type = typename T::handle_type;
};
template <class T>
struct infer_handle_from_state<T, false> {
// nop; this enables SFINAE for spawn to differentiate between
// spawns using actor classes or composable states
};
/// @relates infer_handle_from_state
template <class T>
using infer_handle_from_state_t = typename infer_handle_from_state<T>::type;
} // namespace caf
#endif // CAF_INFER_HANDLE_HPP
......
......@@ -37,7 +37,7 @@ public:
"cannot create a pointer view to an unrelated actor type");
}
typed_actor_pointer(const nullptr_t&) : view_(nullptr) {
typed_actor_pointer(const std::nullptr_t&) : view_(nullptr) {
// nop
}
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* 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. *
******************************************************************************/
#include "caf/config.hpp"
#define CAF_SUITE composable_states
#include "caf/test/unit_test.hpp"
#include "caf/all.hpp"
using namespace std;
using namespace caf;
namespace {
using i3_actor = typed_actor<replies_to<int, int, int>::with<int>>;
using d_actor = typed_actor<replies_to<double>::with<double, double>>;
using foo_actor = i3_actor::extend_with<d_actor>;
class foo_actor_state : public composable_state<foo_actor> {
public:
result<int> operator()(int x, int y, int z) override {
return x + y + z;
}
result<double, double> operator()(double x) override {
return {x, x};
}
};
class i3_actor_state : public composable_state<i3_actor> {
public:
result<int> operator()(int x, int y, int z) override {
return x + y + z;
}
};
class d_actor_state : public composable_state<d_actor> {
public:
result<double, double> operator()(double x) override {
return {x, x};
}
};
class i3_actor_state2 : public composable_state<i3_actor> {
public:
result<int> operator()(int x, int y, int z) override {
return x * (y * z);
}
};
struct foo_actor_state2 : composed_state<i3_actor_state2, i3_actor_state, d_actor_state> {
result<int> operator()(int x, int y, int z) override {
return x - y - z;
}
};
using add_atom = atom_constant<atom("Add")>;
using get_name_atom = atom_constant<atom("GetName")>;
// "base" interface
using named_actor = typed_actor<replies_to<get_name_atom>::with<std::string>>;
// a simple dictionary
using dict = named_actor::extend<replies_to<get_atom, std::string>::with<std::string>,
replies_to<put_atom, std::string, std::string>::with<void>>;
// a simple calculator
using calc = named_actor::extend<replies_to<add_atom, int, int>::with<int>>;
class dict_state : public composable_state<dict> {
public:
result<std::string> operator()(get_name_atom) override {
return "dictionary";
}
result<std::string> operator()(get_atom, const std::string& key) override {
auto i = values_.find(key);
if (i == values_.end())
return "";
return i->second;
}
result<void> operator()(put_atom, const std::string& key, const std::string& value) override {
values_[key] = value;
return unit;
}
protected:
std::unordered_map<std::string, std::string> values_;
};
class calc_state : public composable_state<calc> {
public:
result<std::string> operator()(get_name_atom) override {
return "calculator";
}
result<int> operator()(add_atom, int x, int y) override {
return x + y;
}
};
class dict_calc_state : public composed_state<dict_state, calc_state> {
public:
// composed_state<...> will mark this operator pure virtual, because
// of conflicting declarations in dict_state and calc_state
result<std::string> operator()(get_name_atom) override {
return "calculating dictionary";
}
};
} // namespace <anonymous>
CAF_TEST(composable_states) {
actor_system sys;
//auto x1 = sys.spawn<stateful_impl<foo_actor_state>>();
auto x1 = sys.spawn<foo_actor_state>();
scoped_actor self{sys};
self->request(x1, 1, 2, 4).receive(
[](int y) {
CAF_CHECK(y == 7);
}
);
self->send_exit(x1, exit_reason::kill);
//auto x2 = sys.spawn<stateful_impl<composed_state<i3_actor_state, d_actor_state>>>();
auto x2 = sys.spawn<composed_state<i3_actor_state, d_actor_state>>();
self->request(x2, 1, 2, 4).receive(
[](int y) {
CAF_CHECK(y == 7);
}
);
self->request(x2, 1.0).receive(
[](double y1, double y2) {
CAF_CHECK(y1 == 1.0);
CAF_CHECK(y1 == y2);
}
);
self->send_exit(x2, exit_reason::kill);
//auto x3 = sys.spawn<stateful_impl<foo_actor_state2>>();
auto x3 = sys.spawn<foo_actor_state2>();
self->request(x3, 1, 2, 4).receive(
[](int y) {
CAF_CHECK(y == -5);
}
);
self->send_exit(x3, exit_reason::kill);
//auto x4 = sys.spawn<stateful_impl<dict_calc_state>>();
auto x4 = sys.spawn<dict_calc_state>();
self->request(x4, add_atom::value, 10, 20).receive(
[](int y) {
CAF_CHECK(y == 30);
}
);
self->send_exit(x4, exit_reason::kill);
}
......@@ -72,224 +72,6 @@ static_assert(! std::is_convertible<dummy3, dummy5>::value,
static_assert(! std::is_convertible<dummy4, dummy5>::value,
"handle is assignable to broader definition");
// mockup
using caf::detail::type_list;
using caf::detail::tl_apply;
using caf::detail::tl_union;
using caf::detail::tl_intersect;
using i3_actor = typed_actor<replies_to<int, int, int>::with<int>>;
using d_actor = typed_actor<replies_to<double>::with<double, double>>;
using foo_actor = i3_actor::extend_with<d_actor>;
class foo_actor_state : public composable_state<foo_actor> {
public:
result<int> operator()(int x, int y, int z) override {
return x + y + z;
}
result<double, double> operator()(double x) override {
return {x, x};
}
};
class i3_actor_state : public composable_state<i3_actor> {
public:
result<int> operator()(int x, int y, int z) override {
return x + y + z;
}
};
class d_actor_state : public composable_state<d_actor> {
public:
result<double, double> operator()(double x) override {
return {x, x};
}
};
struct match_case_filter {
template <class... Xs, class... Ts>
void operator()(std::tuple<Xs...>& tup, Ts&... xs) const {
std::integral_constant<size_t, 0> next_pos;
detail::type_list<> next_token;
(*this)(next_pos, tup, next_token, xs...);
}
template <size_t P, class Tuple, class... Consumed>
void operator()(std::integral_constant<size_t, P>, Tuple&,
detail::type_list<Consumed...>) const {
// end of recursion
}
template <size_t P, class Tuple, class... Consumed, class T, class... Ts>
typename std::enable_if<
detail::tl_index_of<detail::type_list<Consumed...>, T>::value == -1
>::type
operator()(std::integral_constant<size_t, P>, Tuple& tup,
detail::type_list<Consumed...>, T& x, Ts&... xs) const {
get<P>(tup) = std::move(x);
std::integral_constant<size_t, P + 1> next_pos;
detail::type_list<Consumed..., T> next_token;
(*this)(next_pos, tup, next_token, xs...);
}
template <size_t P, class Tuple, class... Consumed, class T, class... Ts>
typename std::enable_if<
detail::tl_index_of<detail::type_list<Consumed...>, T>::value != -1
>::type
operator()(std::integral_constant<size_t, P> pos, Tuple& tup,
detail::type_list<Consumed...> token, T&, Ts&... xs) const {
(*this)(pos, tup, token, xs...);
}
};
using detail::type_list;
class i3_actor_state2 : public composable_state<i3_actor> {
public:
result<int> operator()(int x, int y, int z) override {
return x * (y * z);
}
};
struct foo_actor_state2 : composed_state<i3_actor_state2, i3_actor_state, d_actor_state> {
result<int> operator()(int x, int y, int z) override {
return x - y - z;
}
};
using add_atom = atom_constant<atom("Add")>;
using get_name_atom = atom_constant<atom("GetName")>;
// "base" interface
using named_actor = typed_actor<replies_to<get_name_atom>::with<std::string>>;
// a simple dictionary
using dict = named_actor::extend<replies_to<get_atom, std::string>::with<std::string>,
replies_to<put_atom, std::string, std::string>::with<void>>;
// a simple calculator
using calc = named_actor::extend<replies_to<add_atom, int, int>::with<int>>;
class dict_state : public composable_state<dict> {
public:
result<std::string> operator()(get_name_atom) override {
return "dictionary";
}
result<std::string> operator()(get_atom, const std::string& key) override {
auto i = values_.find(key);
if (i == values_.end())
return "";
return i->second;
}
result<void> operator()(put_atom, const std::string& key, const std::string& value) override {
values_[key] = value;
return unit;
}
protected:
std::unordered_map<std::string, std::string> values_;
};
class calc_state : public composable_state<calc> {
public:
result<std::string> operator()(get_name_atom) override {
return "calculator";
}
result<int> operator()(add_atom, int x, int y) override {
return x + y;
}
};
class dict_calc_state : public composed_state<dict_state, calc_state> {
public:
// composed_state<...> will mark this operator pure virtual, because
// of conflicting declarations in dict_state and calc_state
result<std::string> operator()(get_name_atom) override {
return "calculating dictionary";
}
};
template <class State>
class stateful_impl : public stateful_actor<State, typename State::actor_base> {
public:
static_assert(! std::is_abstract<State>::value,
"State is abstract, please make sure to override all "
"virtual operator() member functions");
using super = stateful_actor<State, typename State::actor_base>;
stateful_impl(actor_config& cfg) : super(cfg) {
// nop
}
using behavior_type = typename State::behavior_type;
behavior_type make_behavior() override {
this->state.init_selfptr(this);
behavior tmp;
this->state.init_behavior(tmp);
return behavior_type{typename behavior_type::unsafe_init{}, std::move(tmp)};
}
};
} // namespace <anonymous>
CAF_TEST(foobarz) {
actor_system sys;
auto x1 = sys.spawn<stateful_impl<foo_actor_state>>();
scoped_actor self{sys};
self->request(x1, 1, 2, 4).receive(
[](int y) {
CAF_CHECK(y == 7);
}
);
self->send_exit(x1, exit_reason::kill);
auto x2 = sys.spawn<stateful_impl<composed_state<i3_actor_state, d_actor_state>>>();
self->request(x2, 1, 2, 4).receive(
[](int y) {
CAF_CHECK(y == 7);
}
);
self->request(x2, 1.0).receive(
[](double y1, double y2) {
CAF_CHECK(y1 == 1.0);
CAF_CHECK(y1 == y2);
}
);
self->send_exit(x2, exit_reason::kill);
auto x3 = sys.spawn<stateful_impl<foo_actor_state2>>();
self->request(x3, 1, 2, 4).receive(
[](int y) {
CAF_CHECK(y == -5);
}
);
self->send_exit(x3, exit_reason::kill);
auto x4 = sys.spawn<stateful_impl<dict_calc_state>>();
self->request(x4, add_atom::value, 10, 20).receive(
[](int y) {
CAF_CHECK(y == 30);
}
);
self->send_exit(x4, exit_reason::kill);
}
namespace {
/******************************************************************************
* simple request/response test *
******************************************************************************/
......
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