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
cppa/type_lookup_table.hpp
cppa/typed_actor.hpp
cppa/typed_actor_ptr.hpp
cppa/typed_behavior.hpp
cppa/uniform_type_info.hpp
cppa/unit.hpp
cppa/util/abstract_uniform_type_info.hpp
......@@ -209,8 +210,8 @@ examples/qtsupport/qt_group_chat.cpp
examples/remote_actors/distributed_calculator.cpp
examples/remote_actors/group_chat.cpp
examples/remote_actors/group_server.cpp
examples/remote_actors/protobuf_broker.cpp
examples/remote_actors/protobuf.scala
examples/remote_actors/protobuf_broker.cpp
examples/type_system/announce_1.cpp
examples/type_system/announce_2.cpp
examples/type_system/announce_3.cpp
......@@ -321,5 +322,6 @@ unit_testing/test_serialization.cpp
unit_testing/test_spawn.cpp
unit_testing/test_sync_send.cpp
unit_testing/test_tuple.cpp
unit_testing/test_typed_spawn.cpp
unit_testing/test_uniform_type.cpp
unit_testing/test_yield_interface.cpp
......@@ -54,6 +54,8 @@ class behavior {
public:
typedef std::function<optional<any_tuple> (any_tuple&)> continuation_fun;
/** @cond PRIVATE */
typedef intrusive_ptr<detail::behavior_impl> impl_ptr;
......@@ -108,7 +110,7 @@ class behavior {
* whenever this behavior was successfully applied to
* a message.
*/
behavior add_continuation(const partial_function& fun);
behavior add_continuation(continuation_fun fun);
//template<typename F>
//inline behavior add_continuation(F fun);
......
......@@ -31,6 +31,7 @@
#ifndef BEHAVIOR_IMPL_HPP
#define BEHAVIOR_IMPL_HPP
#include "cppa/atom.hpp"
#include "cppa/optional.hpp"
#include "cppa/any_tuple.hpp"
#include "cppa/ref_counted.hpp"
......@@ -49,13 +50,23 @@ typedef optional<any_tuple> bhvr_invoke_result;
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 {
inline bhvr_invoke_result operator()() const { return any_tuple{}; }
inline bhvr_invoke_result operator()(const none_t&) const { return none; }
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));
}
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>
inline bhvr_invoke_result operator()(cow_tuple<Ts...>& value) const {
return any_tuple{std::move(value)};
......@@ -168,7 +179,7 @@ class default_behavior_impl : public behavior_impl {
}
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(); }
......
......@@ -36,6 +36,7 @@
#include <iostream>
#include <type_traits>
#include "cppa/on.hpp"
#include "cppa/logging.hpp"
#include "cppa/behavior.hpp"
#include "cppa/to_string.hpp"
......@@ -45,6 +46,9 @@
#include "cppa/partial_function.hpp"
#include "cppa/detail/memory.hpp"
#include "cppa/detail/matches.hpp"
#include "cppa/util/scope_guard.hpp"
namespace cppa { namespace detail {
......@@ -344,6 +348,90 @@ class receive_policy {
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'
template<class Client, class Fun, class Policy>
......@@ -392,8 +480,15 @@ class receive_policy {
case sync_response: {
if (awaited_response.valid() && node->mid == awaited_response) {
auto previous_node = hm_begin(client, node, policy);
if (!fun(node->msg) && handle_sync_failure_on_mismatch) {
CPPA_LOGMF(CPPA_WARNING, client, "sync failure occured");
auto res = invoke_fun(client,
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->mark_arrived(awaited_response);
......@@ -406,25 +501,11 @@ class receive_policy {
case ordinary_message: {
if (!awaited_response.valid()) {
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) {
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);
return hm_msg_handled;
}
......
......@@ -47,6 +47,8 @@
namespace cppa {
namespace detail { struct optional_any_tuple_visitor; }
/**
* @brief Provides the @p continue_with member function as used in
* <tt>sync_send(...).then(...).continue_with(...)</tt>.
......@@ -55,20 +57,32 @@ class continue_helper {
public:
typedef int message_id_wrapper_tag;
inline continue_helper(message_id mid) : m_mid(mid) { }
template<typename F>
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);
if (ref_opt) {
auto& ref = *ref_opt;
// copy original behavior
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");
}
inline message_id get_message_id() const {
return m_mid;
}
private:
message_id m_mid;
......@@ -175,6 +189,8 @@ class typed_continue_helper {
public:
typedef int message_id_wrapper_tag;
typedef typename detail::lifted_result_type<R>::type result_types;
typed_continue_helper(continue_helper ch) : m_ch(std::move(ch)) { }
......@@ -185,6 +201,10 @@ class typed_continue_helper {
m_ch.continue_with(std::move(fun));
}
inline message_id get_message_id() const {
return m_ch.get_message_id();
}
private:
continue_helper m_ch;
......
......@@ -97,7 +97,7 @@ void send(const typed_actor_ptr<Signatures...>& whom, Ts&&... what) {
>>::template eval
>::value;
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<
>::type
>
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) {
*/
template<typename... Signatures>
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) {
* @returns An {@link actor_ptr} to the spawned {@link actor}.
* @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) {
auto ptr = make_counted<Impl>(std::forward<Ts>(args)...);
ptr->join(grp);
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>
typed_actor_ptr<typename detail::deduce_signature<Ts>::type...>
spawn_typed(const match_expr<Ts...>& me) {
static_assert(util::conjunction<
detail::match_expr_has_no_guard<Ts>::value...
>::value,
"typed actors are not allowed to use guard expressions");
typedef util::type_list<
typename detail::deduce_signature<Ts>::arg_types...
>
args;
static_assert(util::tl_is_distinct<args>::value,
"typed actors are not allowed to define multiple patterns "
"with identical signature");
auto ptr = make_counted<typed_actor<match_expr<Ts...>>>(me);
return {eval_sopts(Options, get_scheduler()->exec(Options, std::move(ptr)))};
static_assert(util::tl_is_distinct<
util::type_list<
typename detail::deduce_signature<Ts>::arg_types...
>
>::value,
"typed actors are not allowed to define "
"multiple patterns with identical signature");
using impl = detail::default_typed_actor<
typename detail::deduce_signature<Ts>::type...
>;
return spawn_typed<impl, Options>(me);
}
template<typename... Ts>
......
......@@ -32,6 +32,7 @@
#define CPPA_TYPED_ACTOR_HPP
#include "cppa/replies_to.hpp"
#include "cppa/typed_behavior.hpp"
#include "cppa/message_future.hpp"
#include "cppa/event_based_actor.hpp"
......@@ -39,75 +40,60 @@
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 {
CPPA_LOG_ERROR("a typed actor received a "
"non-matching message: "
<< to_string(self->last_dequeued()));
}
using signatures = util::type_list<Signatures...>;
inline void operator()() const { }
using behavior_type = typed_behavior<Signatures...>;
template<typename T>
inline void operator()(T& value) const {
reply_to(m_hdl, std::move(value));
}
using typed_pointer_type = typed_actor_ptr<Signatures...>;
protected:
virtual behavior_type make_behavior() = 0;
template<typename... Ts>
inline void operator()(cow_tuple<Ts...>& value) const {
reply_tuple_to(m_hdl, std::move(value));
void init() final {
auto bhvr = make_behavior();
m_bhvr_stack.push_back(std::move(bhvr.unbox()));
}
template<typename R>
inline void operator()(typed_continue_helper<R>& ch) const {
auto hdl = m_hdl;
ch.continue_with([=](R value) {
typed_actor_result_visitor tarv{hdl};
auto ov = make_optional_variant(std::move(value));
apply_visitor(tarv, ov);
});
void do_become(behavior&&, bool) final {
CPPA_LOG_ERROR("typed actors are not allowed to call become()");
quit(exit_reason::unallowed_function_call);
}
private:
};
response_handle m_hdl;
} // namespace cppa
};
namespace cppa { namespace detail {
template<typename MatchExpr>
class typed_actor : public event_based_actor {
template<typename... Signatures>
class default_typed_actor : public typed_actor<Signatures...> {
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:
void init() override {
m_bhvr_stack.push_back(partial_function{
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);
typed_behavior<Signatures...> make_behavior() override {
return m_bhvr;
}
private:
MatchExpr m_fun;
typed_behavior<Signatures...> m_bhvr;
};
} // namespace cppa
} } // namespace cppa::detail
#endif // CPPA_TYPED_ACTOR_HPP
......@@ -38,67 +38,9 @@
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>
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:
typedef util::type_list<Signatures...> signatures;
......@@ -111,22 +53,39 @@ class typed_actor_ptr {
template<typename... Others>
typed_actor_ptr(typed_actor_ptr<Others...> other) {
static_assert(util::tl_is_strict_subset<
util::type_list<Signatures...>,
util::type_list<Others...>
>::value,
"'this' must be a strict subset of 'other'");
m_ptr = std::move(other.m_ptr);
set(std::move(other));
}
private:
template<typename... Others>
typed_actor_ptr& operator=(typed_actor_ptr<Others...> other) {
set(std::move(other));
return *this;
}
/** @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 */
private:
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;
};
......
/******************************************************************************\
* ___ __ *
* /\_ \ __/\ \ *
* \//\ \ /\_\ \ \____ ___ _____ _____ __ *
* \ \ \ \/\ \ \ '__`\ /'___\/\ '__`\/\ '__`\ /'__`\ *
* \_\ \_\ \ \ \ \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 {
>::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() {
while (!shutdown_flag) { sleep(1); }
aout << color::cyan << "received CTRL+C" << color::reset_endl;
// shutdown actors
send_exit(master, exit_reason::user_defined);
send_exit(master, exit_reason::user_shutdown);
// await actors
act.sa_handler = [](int) { abort(); };
set_sighandler();
......
......@@ -55,7 +55,7 @@ void tester(const actor_ptr& testee) {
// will be invoked if we receive an unexpected response message
self->on_sync_failure([] {
aout << "AUT (actor under test) failed" << endl;
self->quit(exit_reason::user_defined);
self->quit(exit_reason::user_shutdown);
});
// first test: 2 + 1 = 3
sync_send(testee, atom("plus"), 2, 1).then(
......
......@@ -112,7 +112,7 @@ void client_repl(const string& host, uint16_t port) {
line = trim(std::move(line)); // ignore leading and trailing whitespaces
if (line == "quit") {
// force client to quit
send_exit(client, exit_reason::user_defined);
send_exit(client, exit_reason::user_shutdown);
return;
}
// the STL way of line.starts_with("connect")
......
......@@ -142,7 +142,7 @@ int main(int argc, char** argv) {
}
);
// force actor to quit
send_exit(client_actor, exit_reason::user_defined);
send_exit(client_actor, exit_reason::user_shutdown);
await_all_others_done();
shutdown();
return 0;
......
......@@ -122,7 +122,7 @@ void protobuf_io(broker* thisptr, connection_handle hdl, const actor_ptr& buddy)
send(buddy, atom("pong"), p.pong().id());
}
else {
self->quit(exit_reason::user_defined);
self->quit(exit_reason::user_shutdown);
cerr << "neither Ping nor Pong!" << endl;
}
// receive next length prefix
......@@ -138,7 +138,7 @@ void protobuf_io(broker* thisptr, connection_handle hdl, const actor_ptr& buddy)
num_bytes = htonl(num_bytes);
if (num_bytes < 0 || num_bytes > (1024 * 1024)) {
aout << "someone is trying something nasty" << endl;
self->quit(exit_reason::user_defined);
self->quit(exit_reason::user_shutdown);
return;
}
// receive protobuf data
......
......@@ -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.
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}
\label{sec:strong:spawn}
......@@ -40,5 +43,35 @@ using subtype2 = typed_actor_ptr<
subtype1 p3 = p0;
\end{lstlisting}
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).
\ No newline at end of file
\clearpage
\subsection{Class-based Typed Actors}
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}
% code listings setup
\lstset{%
language=C++,%
morekeywords={constexpr,nullptr,size_t,uint32_t,assert},%
morekeywords={constexpr,nullptr,size_t,uint32_t,assert,override,final},%
basicstyle=\ttfamily\small,%
sensitive=true,%
keywordstyle=\color{blue},%
......
......@@ -39,9 +39,11 @@ class continuation_decorator : public detail::behavior_impl {
public:
typedef behavior::continuation_fun continuation_fun;
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)) {
CPPA_REQUIRE(m_decorated != nullptr);
}
......@@ -73,15 +75,15 @@ class continuation_decorator : public detail::behavior_impl {
private:
partial_function m_fun;
continuation_fun m_fun;
pointer m_decorated;
};
behavior::behavior(const partial_function& fun) : m_impl(fun.m_impl) { }
behavior behavior::add_continuation(const partial_function& fun) {
return {new continuation_decorator(fun, m_impl)};
behavior behavior::add_continuation(continuation_fun fun) {
return {new continuation_decorator(std::move(fun), m_impl)};
}
......
......@@ -24,6 +24,7 @@ add_unit_test(primitive_variant)
add_unit_test(yield_interface)
add_unit_test(tuple)
add_unit_test(spawn ping_pong.cpp)
add_unit_test(typed_spawn)
add_unit_test(local_group)
add_unit_test(sync_send)
add_unit_test(remote_actor ping_pong.cpp)
......
......@@ -24,7 +24,7 @@ behavior ping_behavior(size_t num_pings) {
if (++s_pongs >= num_pings) {
CPPA_LOGF_INFO("reached maximum, send {'EXIT', user_defined} "
<< "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();
}
return {atom("ping"), value};
......@@ -32,7 +32,7 @@ behavior ping_behavior(size_t num_pings) {
others() >> [] {
CPPA_LOGF_ERROR("unexpected message; "
<< to_string(self->last_dequeued()));
self->quit(exit_reason::user_defined);
self->quit(exit_reason::user_shutdown);
}
);
}
......@@ -46,7 +46,7 @@ behavior pong_behavior() {
others() >> [] {
CPPA_LOGF_ERROR("unexpected message; "
<< 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) {
},
after(std::chrono::seconds(2)) >> [] {
CPPA_UNEXPECTED_TOUT();
self->quit(exit_reason::user_defined);
self->quit(exit_reason::user_shutdown);
}
);
}
......
......@@ -29,8 +29,6 @@ int main() {
CPPA_TEST(test_metaprogramming);
CPPA_PRINT("test type_list");
typedef type_list<int, float, std::string> l1;
typedef typename tl_reverse<l1>::type r1;
......@@ -52,8 +50,6 @@ int main() {
CPPA_CHECK_EQUAL((util::tl_count<l1, is_int>::value), 1);
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<4, 5> il1;
typedef typename il_right<il0, 2>::type il2;
......@@ -67,6 +63,7 @@ int main() {
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_a, list_a>::value));
CPPA_CHECK((tl_is_strict_subset<list_b, list_b>::value));
}
return CPPA_TEST_RESULT();
......
This diff is collapsed.
......@@ -104,12 +104,13 @@ struct D : popular_actor {
void init() {
become (
others() >> [=] {
/*
response_handle handle = make_response_handle();
sync_send_tuple(buddy(), last_dequeued()).then([=] {
reply_to(handle, last_dequeued());
quit();
});
/*/
//*/
return sync_send_tuple(buddy(), last_dequeued()).then([=]() -> any_tuple {
quit();
return last_dequeued();
......@@ -138,7 +139,7 @@ struct D : popular_actor {
struct server : event_based_actor {
void init() {
auto die = [=] { quit(exit_reason::user_defined); };
auto die = [=] { quit(exit_reason::user_shutdown); };
become (
on(atom("idle")) >> [=] {
auto worker = last_sender();
......@@ -201,10 +202,10 @@ int main() {
);
CPPA_CHECK_EQUAL(sync_failure_called, true);
CPPA_CHECK_EQUAL(int_handler_called, false);
self->quit(exit_reason::user_defined);
self->quit(exit_reason::user_shutdown);
});
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()
);
auto mirror = spawn<sync_mirror>();
......@@ -214,7 +215,7 @@ int main() {
.continue_with([&] { continuation_called = true; });
self->exec_behavior_stack();
CPPA_CHECK_EQUAL(continuation_called, true);
send_exit(mirror, exit_reason::user_defined);
send_exit(mirror, exit_reason::user_shutdown);
await_all_others_done();
CPPA_CHECKPOINT();
auto await_success_message = [&] {
......@@ -245,7 +246,11 @@ int main() {
int i = 0;
receive_for(i, 3) (
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(),
after(std::chrono::seconds(0)) >> CPPA_UNEXPECTED_TOUT_CB()
);
......@@ -270,7 +275,7 @@ int main() {
sync_send(c, atom("gogo")).then(CPPA_CHECKPOINT_CB())
.continue_with(CPPA_CHECKPOINT_CB());
self->exec_behavior_stack();
send_exit(c, exit_reason::user_defined);
send_exit(c, exit_reason::user_shutdown);
await_all_others_done();
CPPA_CHECKPOINT();
......@@ -300,11 +305,11 @@ int main() {
others() >> CPPA_UNEXPECTED_MSG_CB()
);
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 (
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()
);
await_all_others_done();
......
......@@ -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() {
check_move_optional();
CPPA_PRINT(__func__);
CPPA_CHECK_EQUAL(s_expensive_copies.load(), 0);
send(spawn<dummy_receiver>(), expensive_copy_struct());
receive (
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