Commit e6df82dc authored by Dominik Charousset's avatar Dominik Charousset

updated strongly typed actors

this patch:
- unifies handling of response messages for typed and `untyped` actors
- adds class-based typed actors
- updates the manual to cover the latest implemenetation of typed actors
- adds the exit reason `user_shutdown`
parent ec87eff1
...@@ -160,6 +160,7 @@ cppa/tuple_cast.hpp ...@@ -160,6 +160,7 @@ cppa/tuple_cast.hpp
cppa/type_lookup_table.hpp cppa/type_lookup_table.hpp
cppa/typed_actor.hpp cppa/typed_actor.hpp
cppa/typed_actor_ptr.hpp cppa/typed_actor_ptr.hpp
cppa/typed_behavior.hpp
cppa/uniform_type_info.hpp cppa/uniform_type_info.hpp
cppa/unit.hpp cppa/unit.hpp
cppa/util/abstract_uniform_type_info.hpp cppa/util/abstract_uniform_type_info.hpp
...@@ -209,8 +210,8 @@ examples/qtsupport/qt_group_chat.cpp ...@@ -209,8 +210,8 @@ examples/qtsupport/qt_group_chat.cpp
examples/remote_actors/distributed_calculator.cpp examples/remote_actors/distributed_calculator.cpp
examples/remote_actors/group_chat.cpp examples/remote_actors/group_chat.cpp
examples/remote_actors/group_server.cpp examples/remote_actors/group_server.cpp
examples/remote_actors/protobuf_broker.cpp
examples/remote_actors/protobuf.scala examples/remote_actors/protobuf.scala
examples/remote_actors/protobuf_broker.cpp
examples/type_system/announce_1.cpp examples/type_system/announce_1.cpp
examples/type_system/announce_2.cpp examples/type_system/announce_2.cpp
examples/type_system/announce_3.cpp examples/type_system/announce_3.cpp
...@@ -321,5 +322,6 @@ unit_testing/test_serialization.cpp ...@@ -321,5 +322,6 @@ unit_testing/test_serialization.cpp
unit_testing/test_spawn.cpp unit_testing/test_spawn.cpp
unit_testing/test_sync_send.cpp unit_testing/test_sync_send.cpp
unit_testing/test_tuple.cpp unit_testing/test_tuple.cpp
unit_testing/test_typed_spawn.cpp
unit_testing/test_uniform_type.cpp unit_testing/test_uniform_type.cpp
unit_testing/test_yield_interface.cpp unit_testing/test_yield_interface.cpp
...@@ -54,6 +54,8 @@ class behavior { ...@@ -54,6 +54,8 @@ class behavior {
public: public:
typedef std::function<optional<any_tuple> (any_tuple&)> continuation_fun;
/** @cond PRIVATE */ /** @cond PRIVATE */
typedef intrusive_ptr<detail::behavior_impl> impl_ptr; typedef intrusive_ptr<detail::behavior_impl> impl_ptr;
...@@ -108,7 +110,7 @@ class behavior { ...@@ -108,7 +110,7 @@ class behavior {
* whenever this behavior was successfully applied to * whenever this behavior was successfully applied to
* a message. * a message.
*/ */
behavior add_continuation(const partial_function& fun); behavior add_continuation(continuation_fun fun);
//template<typename F> //template<typename F>
//inline behavior add_continuation(F fun); //inline behavior add_continuation(F fun);
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#ifndef BEHAVIOR_IMPL_HPP #ifndef BEHAVIOR_IMPL_HPP
#define BEHAVIOR_IMPL_HPP #define BEHAVIOR_IMPL_HPP
#include "cppa/atom.hpp"
#include "cppa/optional.hpp" #include "cppa/optional.hpp"
#include "cppa/any_tuple.hpp" #include "cppa/any_tuple.hpp"
#include "cppa/ref_counted.hpp" #include "cppa/ref_counted.hpp"
...@@ -49,13 +50,23 @@ typedef optional<any_tuple> bhvr_invoke_result; ...@@ -49,13 +50,23 @@ typedef optional<any_tuple> bhvr_invoke_result;
namespace cppa { namespace detail { namespace cppa { namespace detail {
template<class T> struct is_message_id_wrapper {
template<class U> static char (&test(typename U::message_id_wrapper_tag))[1];
template<class U> static char (&test(...))[2];
static constexpr bool value = sizeof(test<T>(0)) == 1;
};
struct optional_any_tuple_visitor { struct optional_any_tuple_visitor {
inline bhvr_invoke_result operator()() const { return any_tuple{}; } inline bhvr_invoke_result operator()() const { return any_tuple{}; }
inline bhvr_invoke_result operator()(const none_t&) const { return none; } inline bhvr_invoke_result operator()(const none_t&) const { return none; }
template<typename T> template<typename T>
inline bhvr_invoke_result operator()(T& value) const { inline bhvr_invoke_result operator()(T& value, typename std::enable_if<not is_message_id_wrapper<T>::value>::type* = 0) const {
return make_any_tuple(std::move(value)); return make_any_tuple(std::move(value));
} }
template<typename T>
inline bhvr_invoke_result operator()(T& value, typename std::enable_if<is_message_id_wrapper<T>::value>::type* = 0) const {
return make_any_tuple(atom("MESSAGE_ID"), value.get_message_id().integer_value());
}
template<typename... Ts> template<typename... Ts>
inline bhvr_invoke_result operator()(cow_tuple<Ts...>& value) const { inline bhvr_invoke_result operator()(cow_tuple<Ts...>& value) const {
return any_tuple{std::move(value)}; return any_tuple{std::move(value)};
...@@ -168,7 +179,7 @@ class default_behavior_impl : public behavior_impl { ...@@ -168,7 +179,7 @@ class default_behavior_impl : public behavior_impl {
} }
typename behavior_impl::pointer copy(const generic_timeout_definition& tdef) const { typename behavior_impl::pointer copy(const generic_timeout_definition& tdef) const {
return new default_behavior_impl<MatchExpr, std::function<void()> >(m_expr, tdef); return new default_behavior_impl<MatchExpr, std::function<void()>>(m_expr, tdef);
} }
void handle_timeout() { m_fun(); } void handle_timeout() { m_fun(); }
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <iostream> #include <iostream>
#include <type_traits> #include <type_traits>
#include "cppa/on.hpp"
#include "cppa/logging.hpp" #include "cppa/logging.hpp"
#include "cppa/behavior.hpp" #include "cppa/behavior.hpp"
#include "cppa/to_string.hpp" #include "cppa/to_string.hpp"
...@@ -45,6 +46,9 @@ ...@@ -45,6 +46,9 @@
#include "cppa/partial_function.hpp" #include "cppa/partial_function.hpp"
#include "cppa/detail/memory.hpp" #include "cppa/detail/memory.hpp"
#include "cppa/detail/matches.hpp"
#include "cppa/util/scope_guard.hpp"
namespace cppa { namespace detail { namespace cppa { namespace detail {
...@@ -344,6 +348,90 @@ class receive_policy { ...@@ -344,6 +348,90 @@ class receive_policy {
public: public:
template<class Client>
inline response_handle fetch_response_handle(Client* cl, int) {
return cl->make_response_handle();
}
template<class Client>
inline response_handle fetch_response_handle(Client*, response_handle& hdl) {
return std::move(hdl);
}
// - extracts response message from handler
// - returns true if fun was successfully invoked
template<class Client, class Fun, class MaybeResponseHandle = int>
optional<any_tuple> invoke_fun(Client* client,
any_tuple& msg,
message_id& mid,
Fun& fun,
MaybeResponseHandle hdl = MaybeResponseHandle{}) {
auto res = fun(msg); // might change mid
if (res) {
//message_header hdr{client, sender, mid.is_request() ? mid.response_id()
// : message_id{}};
if (res->empty()) {
// make sure synchronous requests
// always receive a response
if (mid.is_request() && !mid.is_answered()) {
CPPA_LOGMF(CPPA_WARNING, client,
"actor with ID " << client->id()
<< " did not reply to a "
"synchronous request message");
auto fhdl = fetch_response_handle(client, hdl);
if (fhdl.valid()) fhdl.apply(atom("VOID"));
}
} else {
if ( matches<atom_value, std::uint64_t>(*res)
&& res->template get_as<atom_value>(0) == atom("MESSAGE_ID")) {
auto id = res->template get_as<std::uint64_t>(1);
auto msg_id = message_id::from_integer_value(id);
auto ref_opt = client->bhvr_stack().sync_handler(msg_id);
// calls client->response_handle() if hdl is a dummy
// argument, forwards hdl otherwise to reply to the
// original request message
auto fhdl = fetch_response_handle(client, hdl);
if (ref_opt) {
auto& ref = *ref_opt;
// copy original behavior
behavior cpy = ref;
ref = cpy.add_continuation(
[=](any_tuple& intermediate) -> optional<any_tuple> {
if (!intermediate.empty()) {
// do no use lamba expresion type to
// avoid recursive template intantiaton
behavior::continuation_fun f2 = [=](any_tuple& m) -> optional<any_tuple> {
return std::move(m);
};
auto mutable_mid = mid;
// recursively call invoke_fun on the
// result to correctly handle stuff like
// sync_send(...).then(...).then(...)...
return invoke_fun(client,
intermediate,
mutable_mid,
f2,
fhdl);
}
return none;
}
);
}
// reset res to prevent "outer" invoke_fun
// from handling the result again
res->reset();
} else {
// respond by using the result of 'fun'
auto fhdl = fetch_response_handle(client, hdl);
if (fhdl.valid()) fhdl.apply(std::move(*res));
}
}
return res;
}
// fun did not return a value => no match
return none;
}
// workflow 'template' // workflow 'template'
template<class Client, class Fun, class Policy> template<class Client, class Fun, class Policy>
...@@ -392,8 +480,15 @@ class receive_policy { ...@@ -392,8 +480,15 @@ class receive_policy {
case sync_response: { case sync_response: {
if (awaited_response.valid() && node->mid == awaited_response) { if (awaited_response.valid() && node->mid == awaited_response) {
auto previous_node = hm_begin(client, node, policy); auto previous_node = hm_begin(client, node, policy);
if (!fun(node->msg) && handle_sync_failure_on_mismatch) { auto res = invoke_fun(client,
CPPA_LOGMF(CPPA_WARNING, client, "sync failure occured"); node->msg,
node->mid,
fun);
if (!res && handle_sync_failure_on_mismatch) {
CPPA_LOGMF(CPPA_WARNING,
client,
"sync failure occured in actor with ID "
<< client->id());
client->handle_sync_failure(); client->handle_sync_failure();
} }
client->mark_arrived(awaited_response); client->mark_arrived(awaited_response);
...@@ -406,25 +501,11 @@ class receive_policy { ...@@ -406,25 +501,11 @@ class receive_policy {
case ordinary_message: { case ordinary_message: {
if (!awaited_response.valid()) { if (!awaited_response.valid()) {
auto previous_node = hm_begin(client, node, policy); auto previous_node = hm_begin(client, node, policy);
auto res = fun(node->msg); auto res = invoke_fun(client,
node->msg,
node->mid,
fun);
if (res) { if (res) {
auto mid = node->mid;
auto sender = node->sender;
message_header hdr{client, sender, mid.response_id()};
if (res->empty()) {
// make sure synchronous requests
// always receive a response
if (mid.valid() && !mid.is_answered() && sender) {
CPPA_LOGMF(CPPA_WARNING, client,
"actor did not reply to a "
"synchronous request message");
sender->enqueue(std::move(hdr),
make_any_tuple(atom("VOID")));
}
} else {
// respond by using the result of 'fun'
sender->enqueue(std::move(hdr), std::move(*res));
}
hm_cleanup(client, previous_node, policy); hm_cleanup(client, previous_node, policy);
return hm_msg_handled; return hm_msg_handled;
} }
......
...@@ -47,6 +47,8 @@ ...@@ -47,6 +47,8 @@
namespace cppa { namespace cppa {
namespace detail { struct optional_any_tuple_visitor; }
/** /**
* @brief Provides the @p continue_with member function as used in * @brief Provides the @p continue_with member function as used in
* <tt>sync_send(...).then(...).continue_with(...)</tt>. * <tt>sync_send(...).then(...).continue_with(...)</tt>.
...@@ -55,20 +57,32 @@ class continue_helper { ...@@ -55,20 +57,32 @@ class continue_helper {
public: public:
typedef int message_id_wrapper_tag;
inline continue_helper(message_id mid) : m_mid(mid) { } inline continue_helper(message_id mid) : m_mid(mid) { }
template<typename F> template<typename F>
void continue_with(F fun) { void continue_with(F fun) {
continue_with(behavior::continuation_fun{partial_function{
on(any_vals, arg_match) >> fun
}});
}
inline void continue_with(behavior::continuation_fun fun) {
auto ref_opt = self->bhvr_stack().sync_handler(m_mid); auto ref_opt = self->bhvr_stack().sync_handler(m_mid);
if (ref_opt) { if (ref_opt) {
auto& ref = *ref_opt; auto& ref = *ref_opt;
// copy original behavior // copy original behavior
behavior cpy = ref; behavior cpy = ref;
ref = cpy.add_continuation(on(any_vals, arg_match) >> fun); ref = cpy.add_continuation(std::move(fun));
} }
else CPPA_LOG_ERROR(".continue_with: failed to add continuation"); else CPPA_LOG_ERROR(".continue_with: failed to add continuation");
} }
inline message_id get_message_id() const {
return m_mid;
}
private: private:
message_id m_mid; message_id m_mid;
...@@ -175,6 +189,8 @@ class typed_continue_helper { ...@@ -175,6 +189,8 @@ class typed_continue_helper {
public: public:
typedef int message_id_wrapper_tag;
typedef typename detail::lifted_result_type<R>::type result_types; typedef typename detail::lifted_result_type<R>::type result_types;
typed_continue_helper(continue_helper ch) : m_ch(std::move(ch)) { } typed_continue_helper(continue_helper ch) : m_ch(std::move(ch)) { }
...@@ -185,6 +201,10 @@ class typed_continue_helper { ...@@ -185,6 +201,10 @@ class typed_continue_helper {
m_ch.continue_with(std::move(fun)); m_ch.continue_with(std::move(fun));
} }
inline message_id get_message_id() const {
return m_ch.get_message_id();
}
private: private:
continue_helper m_ch; continue_helper m_ch;
......
...@@ -97,7 +97,7 @@ void send(const typed_actor_ptr<Signatures...>& whom, Ts&&... what) { ...@@ -97,7 +97,7 @@ void send(const typed_actor_ptr<Signatures...>& whom, Ts&&... what) {
>>::template eval >>::template eval
>::value; >::value;
static_assert(input_pos >= 0, "typed actor does not support given input"); static_assert(input_pos >= 0, "typed actor does not support given input");
send(whom.unbox(), std::forward<Ts>(what)...); send(whom.type_erased(), std::forward<Ts>(what)...);
} }
/** /**
...@@ -184,7 +184,7 @@ typed_message_future< ...@@ -184,7 +184,7 @@ typed_message_future<
>::type >::type
> >
sync_send(const typed_actor_ptr<Signatures...>& whom, Ts&&... what) { sync_send(const typed_actor_ptr<Signatures...>& whom, Ts&&... what) {
return sync_send(whom.unbox(), std::forward<Ts>(what)...); return sync_send(whom.type_erased(), std::forward<Ts>(what)...);
} }
/** /**
...@@ -395,7 +395,7 @@ inline void send_exit(actor_ptr whom, std::uint32_t rsn) { ...@@ -395,7 +395,7 @@ inline void send_exit(actor_ptr whom, std::uint32_t rsn) {
*/ */
template<typename... Signatures> template<typename... Signatures>
void send_exit(const typed_actor_ptr<Signatures...>& whom, std::uint32_t rsn) { void send_exit(const typed_actor_ptr<Signatures...>& whom, std::uint32_t rsn) {
send_exit(whom.unbox(), rsn); send_exit(whom.type_erased(), rsn);
} }
/** /**
......
...@@ -126,35 +126,43 @@ actor_ptr spawn_in_group(const group_ptr& grp, Ts&&... args) { ...@@ -126,35 +126,43 @@ actor_ptr spawn_in_group(const group_ptr& grp, Ts&&... args) {
* @returns An {@link actor_ptr} to the spawned {@link actor}. * @returns An {@link actor_ptr} to the spawned {@link actor}.
* @note The spawned has joined the group before this function returns. * @note The spawned has joined the group before this function returns.
*/ */
template<class Impl, spawn_options Options = no_spawn_options, typename... Ts> template<class Impl, spawn_options Options, typename... Ts>
actor_ptr spawn_in_group(const group_ptr& grp, Ts&&... args) { actor_ptr spawn_in_group(const group_ptr& grp, Ts&&... args) {
auto ptr = make_counted<Impl>(std::forward<Ts>(args)...); auto ptr = make_counted<Impl>(std::forward<Ts>(args)...);
ptr->join(grp); ptr->join(grp);
return eval_sopts(Options, get_scheduler()->exec(Options, ptr)); return eval_sopts(Options, get_scheduler()->exec(Options, ptr));
} }
template<class Impl, spawn_options Options = no_spawn_options, typename... Ts>
typename Impl::typed_pointer_type spawn_typed(Ts&&... args) {
static_assert(util::tl_is_distinct<typename Impl::signatures>::value,
"typed actors are not allowed to define "
"multiple patterns with identical signature");
auto p = make_counted<Impl>(std::forward<Ts>(args)...);
using result_type = typename Impl::typed_pointer_type;
return result_type::cast_from(
eval_sopts(Options, get_scheduler()->exec(Options, std::move(p)))
);
}
template<spawn_options Options, typename... Ts> template<spawn_options Options, typename... Ts>
typed_actor_ptr<typename detail::deduce_signature<Ts>::type...> typed_actor_ptr<typename detail::deduce_signature<Ts>::type...>
spawn_typed(const match_expr<Ts...>& me) { spawn_typed(const match_expr<Ts...>& me) {
static_assert(util::conjunction< static_assert(util::conjunction<
detail::match_expr_has_no_guard<Ts>::value... detail::match_expr_has_no_guard<Ts>::value...
>::value, >::value,
"typed actors are not allowed to use guard expressions"); "typed actors are not allowed to use guard expressions");
static_assert(util::tl_is_distinct<
typedef util::type_list< util::type_list<
typename detail::deduce_signature<Ts>::arg_types... typename detail::deduce_signature<Ts>::arg_types...
> >
args; >::value,
"typed actors are not allowed to define "
static_assert(util::tl_is_distinct<args>::value, "multiple patterns with identical signature");
"typed actors are not allowed to define multiple patterns " using impl = detail::default_typed_actor<
"with identical signature"); typename detail::deduce_signature<Ts>::type...
>;
auto ptr = make_counted<typed_actor<match_expr<Ts...>>>(me); return spawn_typed<impl, Options>(me);
return {eval_sopts(Options, get_scheduler()->exec(Options, std::move(ptr)))};
} }
template<typename... Ts> template<typename... Ts>
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#define CPPA_TYPED_ACTOR_HPP #define CPPA_TYPED_ACTOR_HPP
#include "cppa/replies_to.hpp" #include "cppa/replies_to.hpp"
#include "cppa/typed_behavior.hpp"
#include "cppa/message_future.hpp" #include "cppa/message_future.hpp"
#include "cppa/event_based_actor.hpp" #include "cppa/event_based_actor.hpp"
...@@ -39,75 +40,60 @@ ...@@ -39,75 +40,60 @@
namespace cppa { namespace cppa {
struct typed_actor_result_visitor { template<typename... Signatures>
class typed_actor_ptr;
typed_actor_result_visitor() : m_hdl(self->make_response_handle()) { } template<typename... Signatures>
class typed_actor : public event_based_actor {
typed_actor_result_visitor(response_handle hdl) : m_hdl(hdl) { } public:
inline void operator()(const none_t&) const { using signatures = util::type_list<Signatures...>;
CPPA_LOG_ERROR("a typed actor received a "
"non-matching message: "
<< to_string(self->last_dequeued()));
}
inline void operator()() const { } using behavior_type = typed_behavior<Signatures...>;
template<typename T> using typed_pointer_type = typed_actor_ptr<Signatures...>;
inline void operator()(T& value) const {
reply_to(m_hdl, std::move(value)); protected:
}
virtual behavior_type make_behavior() = 0;
template<typename... Ts> void init() final {
inline void operator()(cow_tuple<Ts...>& value) const { auto bhvr = make_behavior();
reply_tuple_to(m_hdl, std::move(value)); m_bhvr_stack.push_back(std::move(bhvr.unbox()));
} }
template<typename R> void do_become(behavior&&, bool) final {
inline void operator()(typed_continue_helper<R>& ch) const { CPPA_LOG_ERROR("typed actors are not allowed to call become()");
auto hdl = m_hdl; quit(exit_reason::unallowed_function_call);
ch.continue_with([=](R value) {
typed_actor_result_visitor tarv{hdl};
auto ov = make_optional_variant(std::move(value));
apply_visitor(tarv, ov);
});
} }
private: };
response_handle m_hdl; } // namespace cppa
}; namespace cppa { namespace detail {
template<typename MatchExpr> template<typename... Signatures>
class typed_actor : public event_based_actor { class default_typed_actor : public typed_actor<Signatures...> {
public: public:
typed_actor(MatchExpr expr) : m_fun(std::move(expr)) { } template<typename... Cases>
default_typed_actor(match_expr<Cases...> expr) : m_bhvr(std::move(expr)) { }
protected: protected:
void init() override { typed_behavior<Signatures...> make_behavior() override {
m_bhvr_stack.push_back(partial_function{ return m_bhvr;
on<anything>() >> [=] {
auto result = m_fun(last_dequeued());
apply_visitor(typed_actor_result_visitor{}, result);
}
});
}
void do_become(behavior&&, bool) override {
CPPA_LOG_ERROR("typed actors are not allowed to call become()");
quit(exit_reason::unallowed_function_call);
} }
private: private:
MatchExpr m_fun; typed_behavior<Signatures...> m_bhvr;
}; };
} // namespace cppa } } // namespace cppa::detail
#endif // CPPA_TYPED_ACTOR_HPP #endif // CPPA_TYPED_ACTOR_HPP
...@@ -38,67 +38,9 @@ ...@@ -38,67 +38,9 @@
namespace cppa { namespace cppa {
template<typename... Signatures>
class typed_actor_ptr;
// functions that need access to typed_actor_ptr::unbox()
template<spawn_options Options, typename... Ts>
typed_actor_ptr<typename detail::deduce_signature<Ts>::type...>
spawn_typed(const match_expr<Ts...>&);
template<typename... Ts>
void send_exit(const typed_actor_ptr<Ts...>&, std::uint32_t);
template<typename... Signatures, typename... Ts>
void send(const typed_actor_ptr<Signatures...>&, Ts&&...);
template<typename... Ts, typename... Us>
typed_message_future<
typename detail::deduce_output_type<
util::type_list<Ts...>,
util::type_list<
typename detail::implicit_conversions<
typename util::rm_const_and_ref<
Us
>::type
>::type...
>
>::type
>
sync_send(const typed_actor_ptr<Ts...>&, Us&&...);
template<typename... Signatures> template<typename... Signatures>
class typed_actor_ptr { class typed_actor_ptr {
template<typename... OtherSignatures>
friend class typed_actor_ptr;
template<spawn_options Options, typename... Ts>
friend typed_actor_ptr<typename detail::deduce_signature<Ts>::type...>
spawn_typed(const match_expr<Ts...>&);
template<typename... Ts>
friend void send_exit(const typed_actor_ptr<Ts...>&, std::uint32_t);
template<typename... Sigs, typename... Ts>
friend void send(const typed_actor_ptr<Sigs...>&, Ts&&...);
template<typename... Ts, typename... Us>
friend typed_message_future<
typename detail::deduce_output_type<
util::type_list<Ts...>,
util::type_list<
typename detail::implicit_conversions<
typename util::rm_const_and_ref<
Us
>::type
>::type...
>
>::type
>
sync_send(const typed_actor_ptr<Ts...>&, Us&&...);
public: public:
typedef util::type_list<Signatures...> signatures; typedef util::type_list<Signatures...> signatures;
...@@ -111,22 +53,39 @@ class typed_actor_ptr { ...@@ -111,22 +53,39 @@ class typed_actor_ptr {
template<typename... Others> template<typename... Others>
typed_actor_ptr(typed_actor_ptr<Others...> other) { typed_actor_ptr(typed_actor_ptr<Others...> other) {
static_assert(util::tl_is_strict_subset< set(std::move(other));
util::type_list<Signatures...>,
util::type_list<Others...>
>::value,
"'this' must be a strict subset of 'other'");
m_ptr = std::move(other.m_ptr);
} }
private: template<typename... Others>
typed_actor_ptr& operator=(typed_actor_ptr<Others...> other) {
set(std::move(other));
return *this;
}
/** @cond PRIVATE */ /** @cond PRIVATE */
const actor_ptr& unbox() const { return m_ptr; } static typed_actor_ptr cast_from(actor_ptr from) {
return {std::move(from)};
}
const actor_ptr& type_erased() const { return m_ptr; }
actor_ptr& type_erased() { return m_ptr; }
/** @endcond */ /** @endcond */
private:
typed_actor_ptr(actor_ptr ptr) : m_ptr(std::move(ptr)) { } typed_actor_ptr(actor_ptr ptr) : m_ptr(std::move(ptr)) { }
template<class ListA, class ListB>
inline void check_signatures() {
static_assert(util::tl_is_strict_subset<ListA, ListB>::value,
"'this' must be a strict subset of 'other'");
}
template<typename... Others>
inline void set(typed_actor_ptr<Others...> other) {
check_signatures<signatures, util::type_list<Others...>>();
m_ptr = std::move(other.type_erased());
}
actor_ptr m_ptr; actor_ptr m_ptr;
}; };
......
/******************************************************************************\
* ___ __ *
* /\_ \ __/\ \ *
* \//\ \ /\_\ \ \____ ___ _____ _____ __ *
* \ \ \ \/\ \ \ '__`\ /'___\/\ '__`\/\ '__`\ /'__`\ *
* \_\ \_\ \ \ \ \L\ \/\ \__/\ \ \L\ \ \ \L\ \/\ \L\.\_ *
* /\____\\ \_\ \_,__/\ \____\\ \ ,__/\ \ ,__/\ \__/.\_\ *
* \/____/ \/_/\/___/ \/____/ \ \ \/ \ \ \/ \/__/\/_/ *
* \ \_\ \ \_\ *
* \/_/ \/_/ *
* *
* Copyright (C) 2011-2013 *
* Dominik Charousset <dominik.charousset@haw-hamburg.de> *
* *
* This file is part of libcppa. *
* libcppa is free software: you can redistribute it and/or modify it under *
* the terms of the GNU Lesser General Public License as published by the *
* Free Software Foundation; either version 2.1 of the License, *
* or (at your option) any later version. *
* *
* libcppa is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
#ifndef TYPED_BEHAVIOR_HPP
#define TYPED_BEHAVIOR_HPP
#include "cppa/behavior.hpp"
#include "cppa/match_expr.hpp"
#include "cppa/detail/typed_actor_util.hpp"
namespace cppa {
template<typename... Signatures>
class typed_actor;
template<typename... Signatures>
class typed_behavior {
template<typename... OtherSignatures>
friend class typed_actor;
typed_behavior() = delete;
public:
typed_behavior(typed_behavior&&) = default;
typed_behavior(const typed_behavior&) = default;
typed_behavior& operator=(typed_behavior&&) = default;
typed_behavior& operator=(const typed_behavior&) = default;
typedef util::type_list<Signatures...> signatures;
template<typename... Cs>
typed_behavior(match_expr<Cs...> expr) {
set(std::move(expr));
}
template<typename... Cs>
typed_behavior& operator=(match_expr<Cs...> expr) {
set(std::move(expr));
return *this;
}
private:
behavior& unbox() { return m_bhvr; }
template<typename... Cs>
void set(match_expr<Cs...>&& expr) {
m_bhvr = std::move(expr);
using input = util::type_list<
typename detail::deduce_signature<Cs>::type...
>;
// check whether the signature is an exact match
static_assert(util::tl_equal<signatures, input>::value,
"'expr' does not match given signature");
}
behavior m_bhvr;
};
} // namespace cppa
#endif // TYPED_BEHAVIOR_HPP
...@@ -1052,6 +1052,17 @@ struct tl_is_strict_subset { ...@@ -1052,6 +1052,17 @@ struct tl_is_strict_subset {
>::value; >::value;
}; };
/**
* @brief Tests whether ListA equal to ListB.
*/
template<class ListA, class ListB>
struct tl_equal {
static constexpr bool value =
tl_is_strict_subset<ListA, ListB>::value
&& tl_is_strict_subset<ListB, ListA>::value;
};
/** /**
* @} * @}
*/ */
......
...@@ -394,7 +394,7 @@ int main() { ...@@ -394,7 +394,7 @@ int main() {
while (!shutdown_flag) { sleep(1); } while (!shutdown_flag) { sleep(1); }
aout << color::cyan << "received CTRL+C" << color::reset_endl; aout << color::cyan << "received CTRL+C" << color::reset_endl;
// shutdown actors // shutdown actors
send_exit(master, exit_reason::user_defined); send_exit(master, exit_reason::user_shutdown);
// await actors // await actors
act.sa_handler = [](int) { abort(); }; act.sa_handler = [](int) { abort(); };
set_sighandler(); set_sighandler();
......
...@@ -55,7 +55,7 @@ void tester(const actor_ptr& testee) { ...@@ -55,7 +55,7 @@ void tester(const actor_ptr& testee) {
// will be invoked if we receive an unexpected response message // will be invoked if we receive an unexpected response message
self->on_sync_failure([] { self->on_sync_failure([] {
aout << "AUT (actor under test) failed" << endl; aout << "AUT (actor under test) failed" << endl;
self->quit(exit_reason::user_defined); self->quit(exit_reason::user_shutdown);
}); });
// first test: 2 + 1 = 3 // first test: 2 + 1 = 3
sync_send(testee, atom("plus"), 2, 1).then( sync_send(testee, atom("plus"), 2, 1).then(
......
...@@ -112,7 +112,7 @@ void client_repl(const string& host, uint16_t port) { ...@@ -112,7 +112,7 @@ void client_repl(const string& host, uint16_t port) {
line = trim(std::move(line)); // ignore leading and trailing whitespaces line = trim(std::move(line)); // ignore leading and trailing whitespaces
if (line == "quit") { if (line == "quit") {
// force client to quit // force client to quit
send_exit(client, exit_reason::user_defined); send_exit(client, exit_reason::user_shutdown);
return; return;
} }
// the STL way of line.starts_with("connect") // the STL way of line.starts_with("connect")
......
...@@ -142,7 +142,7 @@ int main(int argc, char** argv) { ...@@ -142,7 +142,7 @@ int main(int argc, char** argv) {
} }
); );
// force actor to quit // force actor to quit
send_exit(client_actor, exit_reason::user_defined); send_exit(client_actor, exit_reason::user_shutdown);
await_all_others_done(); await_all_others_done();
shutdown(); shutdown();
return 0; return 0;
......
...@@ -122,7 +122,7 @@ void protobuf_io(broker* thisptr, connection_handle hdl, const actor_ptr& buddy) ...@@ -122,7 +122,7 @@ void protobuf_io(broker* thisptr, connection_handle hdl, const actor_ptr& buddy)
send(buddy, atom("pong"), p.pong().id()); send(buddy, atom("pong"), p.pong().id());
} }
else { else {
self->quit(exit_reason::user_defined); self->quit(exit_reason::user_shutdown);
cerr << "neither Ping nor Pong!" << endl; cerr << "neither Ping nor Pong!" << endl;
} }
// receive next length prefix // receive next length prefix
...@@ -138,7 +138,7 @@ void protobuf_io(broker* thisptr, connection_handle hdl, const actor_ptr& buddy) ...@@ -138,7 +138,7 @@ void protobuf_io(broker* thisptr, connection_handle hdl, const actor_ptr& buddy)
num_bytes = htonl(num_bytes); num_bytes = htonl(num_bytes);
if (num_bytes < 0 || num_bytes > (1024 * 1024)) { if (num_bytes < 0 || num_bytes > (1024 * 1024)) {
aout << "someone is trying something nasty" << endl; aout << "someone is trying something nasty" << endl;
self->quit(exit_reason::user_defined); self->quit(exit_reason::user_shutdown);
return; return;
} }
// receive protobuf data // receive protobuf data
......
...@@ -7,6 +7,9 @@ When calling \lstinline^become^ in a strongly typed actor, the actor will be kil ...@@ -7,6 +7,9 @@ When calling \lstinline^become^ in a strongly typed actor, the actor will be kil
Typed actors use \lstinline^typed_actor_ptr<...>^ instead of \lstinline^actor_ptr^, whereas the template parameters hold the messaging interface. Typed actors use \lstinline^typed_actor_ptr<...>^ instead of \lstinline^actor_ptr^, whereas the template parameters hold the messaging interface.
For example, an actor responding to two integers with a dobule would use the type \lstinline^typed_actor_ptr<replies_to<int, int>::with<double>>^. For example, an actor responding to two integers with a dobule would use the type \lstinline^typed_actor_ptr<replies_to<int, int>::with<double>>^.
All functions for message passing, linking and monitoring are overloaded to accept both types of actors.
As of version 0.8, strongly typed actors cannot be published (this is a planned feature for future releases).
\subsection{Spawning Typed Actors} \subsection{Spawning Typed Actors}
\label{sec:strong:spawn} \label{sec:strong:spawn}
...@@ -40,5 +43,35 @@ using subtype2 = typed_actor_ptr< ...@@ -40,5 +43,35 @@ using subtype2 = typed_actor_ptr<
subtype1 p3 = p0; subtype1 p3 = p0;
\end{lstlisting} \end{lstlisting}
All functions for message passing, linking and monitoring are overloaded to accept both types of actors. \clearpage
As of version 0.8, strongly typed actors cannot be published (this is a planned feature for future releases). \subsection{Class-based Typed Actors}
\ No newline at end of file
Typed actors can be implemented using a class by inheriting from \lstinline^typed_actor<...>^, whereas the template parameter pack denotes the messaging interface.
Derived classes have to implemented the pure virtual member function \lstinline^make_behavior^, as shown in the example below.
\begin{lstlisting}
// implementation
class typed_testee : public typed_actor<replies_to<int>::with<bool>> {
protected:
behavior_type make_behavior() override {
// returning a non-matching expression
// results in a compile-time error
return (
on_arg_match >> [](int value) {
return value == 42;
}
);
}
};
// instantiation
auto testee = spawn_typed<typed_testee>();
\end{lstlisting}
It is worth mentioning that \lstinline^typed_actor^ implements the member function \lstinline^init()^ using the \lstinline^final^ qualifier.
Hence, derived classes are not allowed to override \lstinline^init()^.
However, typed actors are allowed to override other member functions such as \lstinline^on_exit()^.
The return type of \lstinline^make_behavior^ is \lstinline^typed_behavior<...>^, which is aliased as \lstinline^behavior_type^ for convenience.
...@@ -100,7 +100,7 @@ User Manual\\\normalsize{\texttt{libcppa} version 0.8}\vfill} ...@@ -100,7 +100,7 @@ User Manual\\\normalsize{\texttt{libcppa} version 0.8}\vfill}
% code listings setup % code listings setup
\lstset{% \lstset{%
language=C++,% language=C++,%
morekeywords={constexpr,nullptr,size_t,uint32_t,assert},% morekeywords={constexpr,nullptr,size_t,uint32_t,assert,override,final},%
basicstyle=\ttfamily\small,% basicstyle=\ttfamily\small,%
sensitive=true,% sensitive=true,%
keywordstyle=\color{blue},% keywordstyle=\color{blue},%
......
...@@ -39,9 +39,11 @@ class continuation_decorator : public detail::behavior_impl { ...@@ -39,9 +39,11 @@ class continuation_decorator : public detail::behavior_impl {
public: public:
typedef behavior::continuation_fun continuation_fun;
typedef typename behavior_impl::pointer pointer; typedef typename behavior_impl::pointer pointer;
continuation_decorator(const partial_function& fun, pointer ptr) continuation_decorator(continuation_fun fun, pointer ptr)
: super(ptr->timeout()), m_fun(fun), m_decorated(std::move(ptr)) { : super(ptr->timeout()), m_fun(fun), m_decorated(std::move(ptr)) {
CPPA_REQUIRE(m_decorated != nullptr); CPPA_REQUIRE(m_decorated != nullptr);
} }
...@@ -73,15 +75,15 @@ class continuation_decorator : public detail::behavior_impl { ...@@ -73,15 +75,15 @@ class continuation_decorator : public detail::behavior_impl {
private: private:
partial_function m_fun; continuation_fun m_fun;
pointer m_decorated; pointer m_decorated;
}; };
behavior::behavior(const partial_function& fun) : m_impl(fun.m_impl) { } behavior::behavior(const partial_function& fun) : m_impl(fun.m_impl) { }
behavior behavior::add_continuation(const partial_function& fun) { behavior behavior::add_continuation(continuation_fun fun) {
return {new continuation_decorator(fun, m_impl)}; return {new continuation_decorator(std::move(fun), m_impl)};
} }
......
...@@ -24,6 +24,7 @@ add_unit_test(primitive_variant) ...@@ -24,6 +24,7 @@ add_unit_test(primitive_variant)
add_unit_test(yield_interface) add_unit_test(yield_interface)
add_unit_test(tuple) add_unit_test(tuple)
add_unit_test(spawn ping_pong.cpp) add_unit_test(spawn ping_pong.cpp)
add_unit_test(typed_spawn)
add_unit_test(local_group) add_unit_test(local_group)
add_unit_test(sync_send) add_unit_test(sync_send)
add_unit_test(remote_actor ping_pong.cpp) add_unit_test(remote_actor ping_pong.cpp)
......
...@@ -24,7 +24,7 @@ behavior ping_behavior(size_t num_pings) { ...@@ -24,7 +24,7 @@ behavior ping_behavior(size_t num_pings) {
if (++s_pongs >= num_pings) { if (++s_pongs >= num_pings) {
CPPA_LOGF_INFO("reached maximum, send {'EXIT', user_defined} " CPPA_LOGF_INFO("reached maximum, send {'EXIT', user_defined} "
<< "to last sender and quit with normal reason"); << "to last sender and quit with normal reason");
send_exit(self->last_sender(), exit_reason::user_defined); send_exit(self->last_sender(), exit_reason::user_shutdown);
self->quit(); self->quit();
} }
return {atom("ping"), value}; return {atom("ping"), value};
...@@ -32,7 +32,7 @@ behavior ping_behavior(size_t num_pings) { ...@@ -32,7 +32,7 @@ behavior ping_behavior(size_t num_pings) {
others() >> [] { others() >> [] {
CPPA_LOGF_ERROR("unexpected message; " CPPA_LOGF_ERROR("unexpected message; "
<< to_string(self->last_dequeued())); << to_string(self->last_dequeued()));
self->quit(exit_reason::user_defined); self->quit(exit_reason::user_shutdown);
} }
); );
} }
...@@ -46,7 +46,7 @@ behavior pong_behavior() { ...@@ -46,7 +46,7 @@ behavior pong_behavior() {
others() >> [] { others() >> [] {
CPPA_LOGF_ERROR("unexpected message; " CPPA_LOGF_ERROR("unexpected message; "
<< to_string(self->last_dequeued())); << to_string(self->last_dequeued()));
self->quit(exit_reason::user_defined); self->quit(exit_reason::user_shutdown);
} }
); );
} }
......
...@@ -31,7 +31,7 @@ void testee(int current_value, int final_result) { ...@@ -31,7 +31,7 @@ void testee(int current_value, int final_result) {
}, },
after(std::chrono::seconds(2)) >> [] { after(std::chrono::seconds(2)) >> [] {
CPPA_UNEXPECTED_TOUT(); CPPA_UNEXPECTED_TOUT();
self->quit(exit_reason::user_defined); self->quit(exit_reason::user_shutdown);
} }
); );
} }
......
...@@ -29,8 +29,6 @@ int main() { ...@@ -29,8 +29,6 @@ int main() {
CPPA_TEST(test_metaprogramming); CPPA_TEST(test_metaprogramming);
CPPA_PRINT("test type_list");
typedef type_list<int, float, std::string> l1; typedef type_list<int, float, std::string> l1;
typedef typename tl_reverse<l1>::type r1; typedef typename tl_reverse<l1>::type r1;
...@@ -52,8 +50,6 @@ int main() { ...@@ -52,8 +50,6 @@ int main() {
CPPA_CHECK_EQUAL((util::tl_count<l1, is_int>::value), 1); CPPA_CHECK_EQUAL((util::tl_count<l1, is_int>::value), 1);
CPPA_CHECK_EQUAL((util::tl_count<l2, is_int>::value), 2); CPPA_CHECK_EQUAL((util::tl_count<l2, is_int>::value), 2);
CPPA_PRINT("test int_list");
typedef int_list<0, 1, 2, 3, 4, 5> il0; typedef int_list<0, 1, 2, 3, 4, 5> il0;
typedef int_list<4, 5> il1; typedef int_list<4, 5> il1;
typedef typename il_right<il0, 2>::type il2; typedef typename il_right<il0, 2>::type il2;
...@@ -67,6 +63,7 @@ int main() { ...@@ -67,6 +63,7 @@ int main() {
CPPA_CHECK((tl_is_strict_subset<list_a, list_b>::value)); CPPA_CHECK((tl_is_strict_subset<list_a, list_b>::value));
CPPA_CHECK(!(tl_is_strict_subset<list_b, list_a>::value)); CPPA_CHECK(!(tl_is_strict_subset<list_b, list_a>::value));
CPPA_CHECK((tl_is_strict_subset<list_a, list_a>::value)); CPPA_CHECK((tl_is_strict_subset<list_a, list_a>::value));
CPPA_CHECK((tl_is_strict_subset<list_b, list_b>::value));
} }
return CPPA_TEST_RESULT(); return CPPA_TEST_RESULT();
......
This diff is collapsed.
...@@ -104,12 +104,13 @@ struct D : popular_actor { ...@@ -104,12 +104,13 @@ struct D : popular_actor {
void init() { void init() {
become ( become (
others() >> [=] { others() >> [=] {
/*
response_handle handle = make_response_handle(); response_handle handle = make_response_handle();
sync_send_tuple(buddy(), last_dequeued()).then([=] { sync_send_tuple(buddy(), last_dequeued()).then([=] {
reply_to(handle, last_dequeued()); reply_to(handle, last_dequeued());
quit(); quit();
}); });
/*/ //*/
return sync_send_tuple(buddy(), last_dequeued()).then([=]() -> any_tuple { return sync_send_tuple(buddy(), last_dequeued()).then([=]() -> any_tuple {
quit(); quit();
return last_dequeued(); return last_dequeued();
...@@ -138,7 +139,7 @@ struct D : popular_actor { ...@@ -138,7 +139,7 @@ struct D : popular_actor {
struct server : event_based_actor { struct server : event_based_actor {
void init() { void init() {
auto die = [=] { quit(exit_reason::user_defined); }; auto die = [=] { quit(exit_reason::user_shutdown); };
become ( become (
on(atom("idle")) >> [=] { on(atom("idle")) >> [=] {
auto worker = last_sender(); auto worker = last_sender();
...@@ -201,10 +202,10 @@ int main() { ...@@ -201,10 +202,10 @@ int main() {
); );
CPPA_CHECK_EQUAL(sync_failure_called, true); CPPA_CHECK_EQUAL(sync_failure_called, true);
CPPA_CHECK_EQUAL(int_handler_called, false); CPPA_CHECK_EQUAL(int_handler_called, false);
self->quit(exit_reason::user_defined); self->quit(exit_reason::user_shutdown);
}); });
receive ( receive (
on(atom("DOWN"), exit_reason::user_defined) >> CPPA_CHECKPOINT_CB(), on(atom("DOWN"), exit_reason::user_shutdown) >> CPPA_CHECKPOINT_CB(),
others() >> CPPA_UNEXPECTED_MSG_CB() others() >> CPPA_UNEXPECTED_MSG_CB()
); );
auto mirror = spawn<sync_mirror>(); auto mirror = spawn<sync_mirror>();
...@@ -214,7 +215,7 @@ int main() { ...@@ -214,7 +215,7 @@ int main() {
.continue_with([&] { continuation_called = true; }); .continue_with([&] { continuation_called = true; });
self->exec_behavior_stack(); self->exec_behavior_stack();
CPPA_CHECK_EQUAL(continuation_called, true); CPPA_CHECK_EQUAL(continuation_called, true);
send_exit(mirror, exit_reason::user_defined); send_exit(mirror, exit_reason::user_shutdown);
await_all_others_done(); await_all_others_done();
CPPA_CHECKPOINT(); CPPA_CHECKPOINT();
auto await_success_message = [&] { auto await_success_message = [&] {
...@@ -245,7 +246,11 @@ int main() { ...@@ -245,7 +246,11 @@ int main() {
int i = 0; int i = 0;
receive_for(i, 3) ( receive_for(i, 3) (
on(atom("DOWN"), exit_reason::normal) >> CPPA_CHECKPOINT_CB(), on(atom("DOWN"), exit_reason::normal) >> CPPA_CHECKPOINT_CB(),
on(atom("NoWay")) >> CPPA_CHECKPOINT_CB(), on(atom("NoWay")) >> [] {
CPPA_CHECKPOINT();
CPPA_PRINT("trigger \"actor did not reply to a "
"synchronous request message\"");
},
others() >> CPPA_UNEXPECTED_MSG_CB(), others() >> CPPA_UNEXPECTED_MSG_CB(),
after(std::chrono::seconds(0)) >> CPPA_UNEXPECTED_TOUT_CB() after(std::chrono::seconds(0)) >> CPPA_UNEXPECTED_TOUT_CB()
); );
...@@ -270,7 +275,7 @@ int main() { ...@@ -270,7 +275,7 @@ int main() {
sync_send(c, atom("gogo")).then(CPPA_CHECKPOINT_CB()) sync_send(c, atom("gogo")).then(CPPA_CHECKPOINT_CB())
.continue_with(CPPA_CHECKPOINT_CB()); .continue_with(CPPA_CHECKPOINT_CB());
self->exec_behavior_stack(); self->exec_behavior_stack();
send_exit(c, exit_reason::user_defined); send_exit(c, exit_reason::user_shutdown);
await_all_others_done(); await_all_others_done();
CPPA_CHECKPOINT(); CPPA_CHECKPOINT();
...@@ -300,11 +305,11 @@ int main() { ...@@ -300,11 +305,11 @@ int main() {
others() >> CPPA_UNEXPECTED_MSG_CB() others() >> CPPA_UNEXPECTED_MSG_CB()
); );
send(s, "Ever danced with the devil in the pale moonlight?"); send(s, "Ever danced with the devil in the pale moonlight?");
// response: {'EXIT', exit_reason::user_defined} // response: {'EXIT', exit_reason::user_shutdown}
receive_loop(others() >> CPPA_UNEXPECTED_MSG_CB()); receive_loop(others() >> CPPA_UNEXPECTED_MSG_CB());
}); });
receive ( receive (
on(atom("DOWN"), exit_reason::user_defined) >> CPPA_CHECKPOINT_CB(), on(atom("DOWN"), exit_reason::user_shutdown) >> CPPA_CHECKPOINT_CB(),
others() >> CPPA_UNEXPECTED_MSG_CB() others() >> CPPA_UNEXPECTED_MSG_CB()
); );
await_all_others_done(); await_all_others_done();
......
...@@ -407,8 +407,24 @@ void check_wildcards() { ...@@ -407,8 +407,24 @@ void check_wildcards() {
} }
} }
void check_move_optional() {
CPPA_PRINT(__func__);
optional<expensive_copy_struct> opt{expensive_copy_struct{}};
opt->value = 23;
auto opt2 = std::move(opt);
auto move_fun = [](expensive_copy_struct& value) -> optional<expensive_copy_struct> {
return std::move(value);
};
auto opt3 = move_fun(*opt2);
CPPA_CHECK(opt3.valid());
CPPA_CHECK_EQUAL(opt->value, 23);
CPPA_CHECK_EQUAL(s_expensive_copies.load(), 0);
}
void check_move_ops() { void check_move_ops() {
check_move_optional();
CPPA_PRINT(__func__); CPPA_PRINT(__func__);
CPPA_CHECK_EQUAL(s_expensive_copies.load(), 0);
send(spawn<dummy_receiver>(), expensive_copy_struct()); send(spawn<dummy_receiver>(), expensive_copy_struct());
receive ( receive (
on_arg_match >> [&](expensive_copy_struct& ecs) { on_arg_match >> [&](expensive_copy_struct& ecs) {
......
/******************************************************************************\
* ___ __ *
* /\_ \ __/\ \ *
* \//\ \ /\_\ \ \____ ___ _____ _____ __ *
* \ \ \ \/\ \ \ '__`\ /'___\/\ '__`\/\ '__`\ /'__`\ *
* \_\ \_\ \ \ \ \L\ \/\ \__/\ \ \L\ \ \ \L\ \/\ \L\.\_ *
* /\____\\ \_\ \_,__/\ \____\\ \ ,__/\ \ ,__/\ \__/.\_\ *
* \/____/ \/_/\/___/ \/____/ \ \ \/ \ \ \/ \/__/\/_/ *
* \ \_\ \ \_\ *
* \/_/ \/_/ *
* *
* Copyright (C) 2011-2013 *
* Dominik Charousset <dominik.charousset@haw-hamburg.de> *
* *
* This file is part of libcppa. *
* libcppa is free software: you can redistribute it and/or modify it under *
* the terms of the GNU Lesser General Public License as published by the *
* Free Software Foundation; either version 2.1 of the License, *
* or (at your option) any later version. *
* *
* libcppa is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with libcppa. If not, see <http://www.gnu.org/licenses/>. *
\******************************************************************************/
#include "cppa/cppa.hpp"
#include "test.hpp"
using namespace std;
using namespace cppa;
struct my_request { int a; int b; };
bool operator==(const my_request& lhs, const my_request& rhs) {
return lhs.a == rhs.a && lhs.b == rhs.b;
}
typed_actor_ptr<replies_to<my_request>::with<bool>>
spawn_typed_server() {
return spawn_typed(
on_arg_match >> [](const my_request& req) {
return req.a == req.b;
}
);
}
class typed_testee : public typed_actor<replies_to<my_request>::with<bool>> {
protected:
behavior_type make_behavior() override {
return (
on_arg_match >> [](const my_request& req) {
return req.a == req.b;
}
);
}
};
int main() {
CPPA_TEST(test_typed_spawn);
announce<my_request>(&my_request::a, &my_request::b);
auto sptr = spawn_typed_server();
sync_send(sptr, my_request{2, 2}).await(
[](bool value) {
CPPA_CHECK_EQUAL(value, true);
}
);
send_exit(sptr, exit_reason::user_shutdown);
sptr = spawn_typed<typed_testee>();
sync_send(sptr, my_request{2, 2}).await(
[](bool value) {
CPPA_CHECK_EQUAL(value, true);
}
);
send_exit(sptr, exit_reason::user_shutdown);
auto ptr0 = spawn_typed(
on_arg_match >> [](double d) {
return d * d;
},
on_arg_match >> [](float f) {
return f / 2.0f;
}
);
CPPA_CHECK((std::is_same<
decltype(ptr0),
typed_actor_ptr<
replies_to<double>::with<double>,
replies_to<float>::with<float>
>
>::value));
typed_actor_ptr<replies_to<double>::with<double>> ptr0_double = ptr0;
typed_actor_ptr<replies_to<float>::with<float>> ptr0_float = ptr0;
auto ptr = spawn_typed(
on<int>() >> [] { return "wtf"; },
on<string>() >> [] { return 42; },
on<float>() >> [] { return make_cow_tuple(1, 2, 3); },
on<double>() >> [=](double d) {
return sync_send(ptr0_double, d).then(
[](double res) { return res + res; }
);
}
);
// check async messages
send(ptr0_float, 4.0f);
receive(
on_arg_match >> [](float f) {
CPPA_CHECK_EQUAL(f, 4.0f / 2.0f);
}
);
// check sync messages
sync_send(ptr0_float, 4.0f).await(
[](float f) {
CPPA_CHECK_EQUAL(f, 4.0f / 2.0f);
}
);
sync_send(ptr, 10.0).await(
[](double d) {
CPPA_CHECK_EQUAL(d, 200.0);
}
);
sync_send(ptr, 42).await(
[](const std::string& str) {
CPPA_CHECK_EQUAL(str, "wtf");
}
);
sync_send(ptr, 1.2f).await(
[](int a, int b, int c) {
CPPA_CHECK_EQUAL(a, 1);
CPPA_CHECK_EQUAL(b, 2);
CPPA_CHECK_EQUAL(c, 3);
}
);
sync_send(ptr, 1.2f).await(
[](int b, int c) {
CPPA_CHECK_EQUAL(b, 2);
CPPA_CHECK_EQUAL(c, 3);
}
);
sync_send(ptr, 1.2f).await(
[](int c) {
CPPA_CHECK_EQUAL(c, 3);
}
);
sync_send(ptr, 1.2f).await(
[] { CPPA_CHECKPOINT(); }
);
send_exit(ptr0, exit_reason::user_shutdown);
send_exit(ptr, exit_reason::user_shutdown);
await_all_others_done();
CPPA_CHECKPOINT();
shutdown();
return CPPA_TEST_RESULT();
}
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