Commit d36dc898 authored by Dominik Charousset's avatar Dominik Charousset

Replace `match_expr` with `match_case` tuples

The main objective of this change is to make CAF stack traces readable,
reducing the amount of "template vomit" visible at runtime to a bare minimum.
As a side effect, this patch streamlines the implementation quite a bit to have
an easier time maintaining it in the future.

Also `others()` can now be written as simply `others`.
parent 35e6b342
......@@ -146,7 +146,7 @@ behavior broker_impl(broker* self, connection_handle hdl, const actor& buddy) {
// send composed message to our buddy
self->send(buddy, atm, ival);
},
others() >> [=] {
others >> [=] {
cout << "unexpected: " << to_string(self->current_message()) << endl;
}
};
......@@ -164,7 +164,7 @@ behavior server(broker* self, const actor& buddy) {
aout(self) << "quit server (only accept 1 connection)" << endl;
self->quit();
},
others() >> [=] {
others >> [=] {
cout << "unexpected: " << to_string(self->current_message()) << endl;
}
};
......@@ -190,7 +190,7 @@ int main(int argc, char** argv) {
print_on_exit(ping_actor, "ping");
send_as(io_actor, ping_actor, kickoff_atom::value, io_actor);
},
others() >> [] {
others >> [] {
cerr << "use with eihter '-s PORT' as server or '-c HOST PORT' as client"
<< endl;
}
......
......@@ -61,7 +61,7 @@ behavior server(broker* self) {
*counter = 0;
self->delayed_send(self, std::chrono::seconds(1), tick_atom::value);
},
others() >> [=] {
others >> [=] {
aout(self) << "unexpected: " << to_string(self->current_message()) << endl;
}
};
......@@ -83,7 +83,7 @@ int main(int argc, const char **argv) {
// kill server
anon_send_exit(server_actor, exit_reason::user_shutdown);
},
others() >> [] {
others >> [] {
cerr << "use with '-p PORT' as server on port" << endl;
}
});
......
......@@ -25,7 +25,7 @@ void blocking_calculator(blocking_actor* self) {
[](minus_atom, int a, int b) {
return a - b;
},
others() >> [=] {
others >> [=] {
cout << "unexpected: " << to_string(self->current_message()) << endl;
}
);
......@@ -40,7 +40,7 @@ behavior calculator(event_based_actor* self) {
[](minus_atom, int a, int b) {
return a - b;
},
others() >> [=] {
others >> [=] {
cout << "unexpected: " << to_string(self->current_message()) << endl;
}
};
......
......@@ -143,7 +143,7 @@ void client_repl(const string& host, uint16_t port) {
<< endl;
}
},
others() >> [] {
others >> [] {
cout << "*** usage: connect <host> <port>" << endl;
}
});
......
......@@ -59,7 +59,7 @@ void client(event_based_actor* self, const string& name) {
[=](const group_down_msg& g) {
cout << "*** chatroom offline: " << to_string(g.source) << endl;
},
others() >> [=]() {
others >> [=]() {
cout << "unexpected: " << to_string(self->current_message()) << endl;
}
);
......@@ -143,7 +143,7 @@ int main(int argc, char** argv) {
" /quit quit the program\n"
" /help print this text\n" << flush;
},
others() >> [&] {
others >> [&] {
if (!s_last_line.empty()) {
anon_send(client_actor, broadcast_atom::value, s_last_line);
}
......
......@@ -50,6 +50,7 @@ set (LIBCAF_CORE_SRCS
src/get_root_uuid.cpp
src/group.cpp
src/group_manager.cpp
src/match_case.cpp
src/local_actor.cpp
src/logging.cpp
src/mailbox_based_actor.cpp
......
......@@ -46,7 +46,6 @@
#include "caf/to_string.hpp"
#include "caf/actor_addr.hpp"
#include "caf/attachable.hpp"
#include "caf/match_expr.hpp"
#include "caf/message_id.hpp"
#include "caf/replies_to.hpp"
#include "caf/serializer.hpp"
......
......@@ -26,11 +26,11 @@
#include "caf/none.hpp"
#include "caf/duration.hpp"
#include "caf/match_expr.hpp"
#include "caf/timeout_definition.hpp"
#include "caf/detail/type_list.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/detail/behavior_impl.hpp"
namespace caf {
......@@ -59,17 +59,16 @@ class behavior {
* The list of arguments can contain match expressions, message handlers,
* and up to one timeout (if set, the timeout has to be the last argument).
*/
template <class T, class... Ts>
behavior(const T& arg, Ts&&... args) {
assign(arg, std::forward<Ts>(args)...);
template <class V, class... Vs>
behavior(V v, Vs... vs) {
assign(std::move(v), std::move(vs)...);
}
/**
* Creates a behavior from `tdef` without message handler.
*/
template <class F>
behavior(const timeout_definition<F>& tdef)
: m_impl(detail::new_default_behavior(tdef.timeout, tdef.handler)) {
behavior(timeout_definition<F> tdef) : m_impl(detail::make_behavior(tdef)) {
// nop
}
......@@ -77,22 +76,29 @@ class behavior {
* Creates a behavior using `d` and `f` as timeout definition
* without message handler.
*/
template <class F>
behavior(const duration& d, F f)
: m_impl(detail::new_default_behavior(d, f)) {
//template <class F>
//behavior(duration d, F f) : m_impl(detail::make_behavior(d, f)) {
// nop
}
//}
/**
* Assigns new handlers.
*/
template <class T, class... Ts>
void assign(T&& v, Ts&&... vs) {
m_impl = detail::match_expr_concat(
detail::lift_to_match_expr(std::forward<T>(v)),
detail::lift_to_match_expr(std::forward<Ts>(vs))...);
template <class... Vs>
void assign(Vs... vs) {
m_impl = detail::make_behavior(vs...);
}
/**
* Equal to `*this = other`.
*/
void assign(message_handler other);
/**
* Equal to `*this = other`.
*/
void assign(behavior other);
/**
* Invokes the timeout callback if set.
*/
......@@ -136,22 +142,14 @@ class behavior {
// nop
}
void assign(detail::behavior_impl*);
/** @endcond */
private:
impl_ptr m_impl;
};
/**
* Creates a behavior from a match expression and a timeout definition.
* @relates behavior
*/
template <class... Cs, typename F>
behavior operator,(const match_expr<Cs...>& lhs,
const timeout_definition<F>& rhs) {
return {lhs, rhs};
}
} // namespace caf
#endif // CAF_BEHAVIOR_HPP
......@@ -31,33 +31,31 @@ namespace detail {
// this utterly useless function works around a bug in Clang that causes
// the compiler to reject the trailing return type of apply_args because
// "get" is not defined (it's found during ADL)
template<long Pos, class... Ts>
typename tl_at<type_list<Ts...>, Pos>::type get(const type_list<Ts...>&);
template<long Pos, class... Vs>
typename tl_at<type_list<Vs...>, Pos>::type get(const type_list<Vs...>&);
template <class F, long... Is, class Tuple>
inline auto apply_args(F& f, detail::int_list<Is...>, Tuple&& tup)
auto apply_args(F& f, detail::int_list<Is...>, Tuple&& tup)
-> decltype(f(get<Is>(tup)...)) {
return f(get<Is>(tup)...);
}
template <class F, class Tuple, class... Ts>
inline auto apply_args_prefixed(F& f, detail::int_list<>, Tuple&, Ts&&... args)
-> decltype(f(std::forward<Ts>(args)...)) {
return f(std::forward<Ts>(args)...);
template <class F, class Tuple, class... Vs>
auto apply_args_prefixed(F& f, detail::int_list<>, Tuple&, Vs&&... vs)
-> decltype(f(std::forward<Vs>(vs)...)) {
return f(std::forward<Vs>(vs)...);
}
template <class F, long... Is, class Tuple, class... Ts>
inline auto apply_args_prefixed(F& f, detail::int_list<Is...>, Tuple& tup,
Ts&&... args)
-> decltype(f(std::forward<Ts>(args)..., get<Is>(tup)...)) {
return f(std::forward<Ts>(args)..., get<Is>(tup)...);
template <class F, long... Is, class Tuple, class... Vs>
auto apply_args_prefixed(F& f, detail::int_list<Is...>, Tuple& tup, Vs&&... vs)
-> decltype(f(std::forward<Vs>(vs)..., get<Is>(tup)...)) {
return f(std::forward<Vs>(vs)..., get<Is>(tup)...);
}
template <class F, long... Is, class Tuple, class... Ts>
inline auto apply_args_suffxied(F& f, detail::int_list<Is...>, Tuple& tup,
Ts&&... args)
-> decltype(f(get<Is>(tup)..., std::forward<Ts>(args)...)) {
return f(get<Is>(tup)..., std::forward<Ts>(args)...);
template <class F, long... Is, class Tuple, class... Vs>
auto apply_args_suffxied(F& f, detail::int_list<Is...>, Tuple& tup, Vs&&... vs)
-> decltype(f(get<Is>(tup)..., std::forward<Vs>(vs)...)) {
return f(get<Is>(tup)..., std::forward<Vs>(vs)...);
}
} // namespace detail
......
......@@ -26,6 +26,7 @@
#include "caf/none.hpp"
#include "caf/variant.hpp"
#include "caf/optional.hpp"
#include "caf/match_case.hpp"
#include "caf/intrusive_ptr.hpp"
#include "caf/atom.hpp"
......@@ -41,6 +42,8 @@
#include "caf/detail/int_list.hpp"
#include "caf/detail/apply_args.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/detail/tail_argument_token.hpp"
#include "caf/detail/optional_message_visitor.hpp"
namespace caf {
......@@ -52,115 +55,10 @@ using bhvr_invoke_result = optional<message>;
namespace caf {
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;
};
template <class T>
struct is_response_promise : std::false_type { };
template <>
struct is_response_promise<response_promise> : std::true_type { };
template <class T>
struct is_response_promise<typed_response_promise<T>> : std::true_type { };
template <class T>
struct optional_message_visitor_enable_tpl {
static constexpr bool value =
!detail::is_one_of<
typename std::remove_const<T>::type,
none_t,
unit_t,
skip_message_t,
optional<skip_message_t>
>::value
&& !is_message_id_wrapper<T>::value
&& !std::is_convertible<T, message>::value
&& !is_response_promise<T>::value;
};
struct optional_message_visitor : static_visitor<bhvr_invoke_result> {
inline bhvr_invoke_result operator()(const none_t&) const {
return none;
}
inline bhvr_invoke_result operator()(const skip_message_t&) const {
return none;
}
inline bhvr_invoke_result operator()(const unit_t&) const {
return message{};
}
inline bhvr_invoke_result operator()(const optional<skip_message_t>& val) const {
if (val) return none;
return message{};
}
inline bhvr_invoke_result operator()(const response_promise&) const {
return message{};
}
template <class T>
inline bhvr_invoke_result operator()(const typed_response_promise<T>&) const {
return message{};
}
template <class T, class... Ts>
typename std::enable_if<
optional_message_visitor_enable_tpl<T>::value,
bhvr_invoke_result
>::type
operator()(T& v, Ts&... vs) const {
return make_message(std::move(v), std::move(vs)...);
}
template <class T>
typename std::enable_if<
is_message_id_wrapper<T>::value,
bhvr_invoke_result
>::type
operator()(T& value) const {
return make_message(atom("MESSAGE_ID"),
value.get_message_id().integer_value());
}
inline bhvr_invoke_result operator()(message& value) const {
return std::move(value);
}
template <class L, class R>
bhvr_invoke_result operator()(either_or_t<L, R>& value) const {
return std::move(value.value);
}
template <class... Ts>
bhvr_invoke_result operator()(std::tuple<Ts...>& value) const {
return detail::apply_args(*this, detail::get_indices(value), value);
}
template <class T>
typename std::enable_if<
std::is_convertible<T, message>::value,
bhvr_invoke_result
>::type
operator()(const T& value) const {
return static_cast<message>(value);
}
};
template <class... Ts>
struct has_skip_message {
static constexpr bool value =
detail::disjunction<std::is_same<Ts, skip_message_t>::value...>::value;
disjunction<std::is_same<Ts, skip_message_t>::value...>::value;
};
class behavior_impl : public ref_counted {
......@@ -168,10 +66,10 @@ class behavior_impl : public ref_counted {
using pointer = intrusive_ptr<behavior_impl>;
~behavior_impl();
behavior_impl() = default;
behavior_impl(duration tout);
virtual bhvr_invoke_result invoke(message&) = 0;
behavior_impl(duration tout = duration{});
virtual bhvr_invoke_result invoke(message&);
inline bhvr_invoke_result invoke(message&& arg) {
message tmp(std::move(arg));
......@@ -188,48 +86,53 @@ class behavior_impl : public ref_counted {
pointer or_else(const pointer& other);
private:
protected:
duration m_timeout;
match_case_info* m_begin;
match_case_info* m_end;
};
struct dummy_match_expr {
inline variant<none_t> invoke(const message&) const {
return none;
}
inline bool can_invoke(const message&) const {
return false;
template <size_t Pos, size_t Size>
struct defaut_bhvr_impl_init {
template <class Array, class Tuple>
static void init(Array& arr, Tuple& tup) {
auto& x = arr[Pos];
x.ptr = &std::get<Pos>(tup);
x.has_wildcard = x.ptr->has_wildcard();
x.type_token = x.ptr->type_token();
defaut_bhvr_impl_init<Pos + 1, Size>::init(arr, tup);
}
inline variant<none_t> operator()(const message&) const {
return none;
};
template <size_t Size>
struct defaut_bhvr_impl_init<Size, Size> {
template <class Array, class Tuple>
static void init(Array&, Tuple&) {
// nop
}
};
template <class MatchExpr, class F = std::function<void()>>
template <class Tuple>
class default_behavior_impl : public behavior_impl {
public:
template <class Expr>
default_behavior_impl(Expr&& expr, const timeout_definition<F>& d)
: behavior_impl(d.timeout)
, m_expr(std::forward<Expr>(expr))
, m_fun(d.handler) {}
template <class Expr>
default_behavior_impl(Expr&& expr, duration tout, F f)
: behavior_impl(tout),
m_expr(std::forward<Expr>(expr)),
m_fun(f) {
// nop
static constexpr size_t num_cases = std::tuple_size<Tuple>::value;
default_behavior_impl(Tuple tup) : m_cases(std::move(tup)) {
init();
}
bhvr_invoke_result invoke(message& msg) override {
auto res = m_expr(msg);
optional_message_visitor omv;
return apply_visitor(omv, res);
template <class F>
default_behavior_impl(Tuple tup, timeout_definition<F> d)
: behavior_impl(d.timeout),
m_cases(std::move(tup)),
m_fun(d.handler) {
init();
}
typename behavior_impl::pointer
copy(const generic_timeout_definition& tdef) const override {
return new default_behavior_impl<MatchExpr>(m_expr, tdef);
return new default_behavior_impl<Tuple>(m_cases, tdef);
}
void handle_timeout() override {
......@@ -237,29 +140,107 @@ class default_behavior_impl : public behavior_impl {
}
private:
MatchExpr m_expr;
F m_fun;
void init() {
defaut_bhvr_impl_init<0, num_cases>::init(m_arr, m_cases);
m_begin = m_arr.data();
m_end = m_begin + m_arr.size();
}
Tuple m_cases;
std::array<match_case_info, num_cases> m_arr;
std::function<void()> m_fun;
};
template <class MatchExpr, typename F>
default_behavior_impl<MatchExpr, F>*
new_default_behavior(const MatchExpr& mexpr, duration d, F f) {
return new default_behavior_impl<MatchExpr, F>(mexpr, d, f);
// eor = end of recursion
// ra = reorganize arguments
template <class R, class... Ts>
R* make_behavior_eor(std::tuple<Ts...> match_cases) {
return new R(std::move(match_cases));
}
template <class R, class... Ts, class F>
R* make_behavior_eor(std::tuple<Ts...> match_cases, timeout_definition<F>& td) {
return new R(std::move(match_cases), std::move(td));
}
template <class F, bool IsMC = std::is_base_of<match_case, F>::value>
struct lift_to_mctuple {
using type = std::tuple<trivial_match_case<F>>;
};
template <class T>
struct lift_to_mctuple<T, true> {
using type = std::tuple<T>;
};
template <class... Ts>
struct lift_to_mctuple<std::tuple<Ts...>, false> {
using type = std::tuple<Ts...>;
};
template <class F>
behavior_impl* new_default_behavior(duration d, F f) {
dummy_match_expr nop;
return new default_behavior_impl<dummy_match_expr, F>(nop, d, std::move(f));
struct lift_to_mctuple<timeout_definition<F>, false> {
using type = std::tuple<>;
};
template <class T, class... Ts>
struct join_std_tuples;
template <class T>
struct join_std_tuples<T> {
using type = T;
};
template <class... Ts, class... Us, class... Rs>
struct join_std_tuples<std::tuple<Ts...>, std::tuple<Us...>, Rs...>
: join_std_tuples<std::tuple<Ts..., Us...>, Rs...> {
// nop
};
// this function reorganizes its arguments to shift the timeout definition
// to the front (receives it at the tail)
template <class R, class F, class... Vs>
R* make_behavior_ra(timeout_definition<F>& tdef,
tail_argument_token&, Vs... vs) {
return make_behavior_eor<R>(std::tuple_cat(to_match_case_tuple(vs)...), tdef);
}
behavior_impl* new_default_behavior(duration d, std::function<void()> fun);
template <class R, class... Vs>
R* make_behavior_ra(tail_argument_token&, Vs... vs) {
return make_behavior_eor<R>(std::tuple_cat(to_match_case_tuple(vs)...));
}
using behavior_impl_ptr = intrusive_ptr<behavior_impl>;
// for some reason, this call is ambigious on GCC without enable_if
template <class R, class V, class... Vs>
typename std::enable_if<
!std::is_same<V, tail_argument_token>::value,
R*
>::type
make_behavior_ra(V& v, Vs&... vs) {
return make_behavior_ra<R>(vs..., v);
}
// implemented in message_handler.cpp
// message_handler combine(behavior_impl_ptr, behavior_impl_ptr);
// behavior_impl_ptr extract(const message_handler&);
// this function reorganizes its arguments to shift the timeout definition
// to the front (receives it at the tail)
template <class... Vs>
default_behavior_impl<
typename join_std_tuples<
typename lift_to_mctuple<Vs>::type...
>::type
>*
make_behavior(Vs&... vs) {
using result_type =
default_behavior_impl<
typename join_std_tuples<
typename lift_to_mctuple<Vs>::type...
>::type
>;
tail_argument_token eoa;
return make_behavior_ra<result_type>(vs..., eoa);
}
using behavior_impl_ptr = intrusive_ptr<behavior_impl>;
} // namespace detail
} // namespace caf
......
......@@ -56,6 +56,28 @@ struct il_right<int_list<Is...>, N> {
sizeof...(Is), Is...>::type;
};
template <bool Done, size_t Num, class List, long... Is>
struct il_take_impl;
template <class List, long... Is>
struct il_take_impl<true, 0, List, Is...> {
using type = List;
};
template <size_t Num, long... Rs, long I, long... Is>
struct il_take_impl<false, Num, int_list<Rs...>, I, Is...> {
using type = typename il_take_impl<Num == 1, Num - 1, int_list<Rs..., I>, Is...>::type;
};
template <class List, size_t N>
struct il_take;
template <long... Is, size_t N>
struct il_take<int_list<Is...>, N> {
using type = typename il_take_impl<N == 0, N, int_list<>, Is...>::type;
};
/**
* Creates indices for `List` beginning at `Pos`.
*/
......
......@@ -31,25 +31,21 @@ namespace detail {
template <class Left, typename Right>
struct left_or_right {
using type = Left;
};
template <class Right>
struct left_or_right<unit_t, Right> {
using type = Right;
};
template <class Right>
struct left_or_right<unit_t&, Right> {
using type = Right;
};
template <class Right>
struct left_or_right<const unit_t&, Right> {
using type = Right;
};
/**
......@@ -58,13 +54,11 @@ struct left_or_right<const unit_t&, Right> {
template <class Left, typename Right>
struct if_not_left {
using type = unit_t;
};
template <class Right>
struct if_not_left<unit_t, Right> {
using type = Right;
};
} // namespace detail
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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_DETAIL_MESSAGE_CASE_BUILDER_HPP
#define CAF_DETAIL_MESSAGE_CASE_BUILDER_HPP
#include <type_traits>
#include "caf/duration.hpp"
#include "caf/match_case.hpp"
#include "caf/timeout_definition.hpp"
namespace caf {
namespace detail {
class timeout_definition_builder {
public:
constexpr timeout_definition_builder(const duration& d) : m_tout(d) {
// nop
}
template <class F>
timeout_definition<F> operator>>(F f) const {
return {m_tout, std::move(f)};
}
private:
duration m_tout;
};
class message_case_builder { };
class trivial_match_case_builder : public message_case_builder {
public:
constexpr trivial_match_case_builder() {
// nop
}
template <class F>
trivial_match_case<F> operator>>(F f) const {
return {std::move(f)};
}
};
class catch_all_match_case_builder : public message_case_builder {
public:
constexpr catch_all_match_case_builder() {
// nop
}
const catch_all_match_case_builder& operator()() const {
return *this;
}
template <class F>
catch_all_match_case<F> operator>>(F f) const {
return {std::move(f)};
}
};
template <class Left, class Right>
class message_case_pair_builder : public message_case_builder {
public:
message_case_pair_builder(Left l, Right r)
: m_left(std::move(l)),
m_right(std::move(r)) {
// nop
}
template <class F>
auto operator>>(F f) const
-> std::tuple<decltype(*static_cast<Left*>(nullptr) >> f),
decltype(*static_cast<Right*>(nullptr) >> f)> {
return std::make_tuple(m_left >> f, m_right >> f);
}
private:
Left m_left;
Right m_right;
};
struct tuple_maker {
template <class... Ts>
inline auto operator()(Ts&&... args)
-> decltype(std::make_tuple(std::forward<Ts>(args)...)) {
return std::make_tuple(std::forward<Ts>(args)...);
}
};
struct variadic_ctor {};
template <class Expr, class Transformers, class Pattern>
struct get_advanced_match_case {
using ctrait = typename get_callable_trait<Expr>::type;
using filtered_pattern =
typename tl_filter_not_type<
Pattern,
anything
>::type;
using padded_transformers =
typename tl_pad_right<
Transformers,
tl_size<filtered_pattern>::value
>::type;
using base_signature =
typename tl_map<
filtered_pattern,
std::add_const,
std::add_lvalue_reference
>::type;
using padded_expr_args =
typename tl_map_conditional<
typename tl_pad_left<
typename ctrait::arg_types,
tl_size<filtered_pattern>::value
>::type,
std::is_lvalue_reference,
false,
std::add_const,
std::add_lvalue_reference
>::type;
// override base signature with required argument types of Expr
// and result types of transformation
using partial_fun_signature =
typename tl_zip<
typename tl_map<
padded_transformers,
map_to_result_type,
rm_optional,
std::add_lvalue_reference
>::type,
typename tl_zip<
padded_expr_args,
base_signature,
left_or_right
>::type,
left_or_right
>::type;
// 'inherit' mutable references from partial_fun_signature
// for arguments without transformation
using projection_signature =
typename tl_zip<
typename tl_zip<
padded_transformers,
partial_fun_signature,
if_not_left
>::type,
base_signature,
deduce_ref_type
>::type;
using type =
advanced_match_case_impl<
Expr,
Pattern,
padded_transformers,
projection_signature
>;
};
template <class X, class Y>
struct pattern_projection_zipper {
using type = Y;
};
template <class Y>
struct pattern_projection_zipper<anything, Y> {
using type = none_t;
};
template <class Projections, class Pattern,
bool UsesArgMatch =
std::is_same<
arg_match_t,
typename tl_back<Pattern>::type
>::value>
class advanced_match_case_builder : public message_case_builder {
public:
using guards_tuple =
typename tl_apply<
// discard projection if at the same index in Pattern is a wildcard
typename tl_filter_not_type<
typename tl_zip<Pattern, Projections, pattern_projection_zipper>::type,
none_t
>::type,
std::tuple
>::type;
advanced_match_case_builder() = default;
template <class... Fs>
advanced_match_case_builder(variadic_ctor, Fs... fs)
: m_guards(make_guards(Pattern{}, fs...)) {
// nop
}
advanced_match_case_builder(guards_tuple arg1) : m_guards(std::move(arg1)) {
// nop
}
template <class F>
typename get_advanced_match_case<F, Projections, Pattern>::type
operator>>(F f) const {
return {f, m_guards};
}
private:
template <class... Fs>
static guards_tuple make_guards(tail_argument_token&, Fs&... fs) {
return std::make_tuple(std::move(fs)...);
}
template <class T, class F, class... Fs>
static guards_tuple make_guards(std::pair<wrapped<T>, F> x, Fs&&... fs) {
return make_guards(std::forward<Fs>(fs)..., x.second);
}
template <class F, class... Fs>
static guards_tuple make_guards(std::pair<wrapped<anything>, F>, Fs&&... fs) {
return make_guards(std::forward<Fs>(fs)...);
}
template <class... Ts, class... Fs>
static guards_tuple make_guards(type_list<Ts...>, Fs&... fs) {
tail_argument_token eoa;
return make_guards(std::make_pair(wrapped<Ts>(), std::ref(fs))..., eoa);
}
guards_tuple m_guards;
};
template <class Projections, class Pattern>
struct advanced_match_case_builder<Projections, Pattern, true>
: public message_case_builder {
public:
using guards_tuple =
typename detail::tl_apply<
typename tl_pop_back<Projections>::type,
std::tuple
>::type;
using pattern = typename tl_pop_back<Pattern>::type;
advanced_match_case_builder() = default;
template <class... Fs>
advanced_match_case_builder(variadic_ctor, Fs... fs)
: m_guards(make_guards(Pattern{}, fs...)) {
// nop
}
advanced_match_case_builder(guards_tuple arg1) : m_guards(std::move(arg1)) {
// nop
}
template <class F>
typename get_advanced_match_case<
F,
Projections,
typename tl_concat<
pattern,
typename tl_map<
typename get_callable_trait<F>::arg_types,
std::decay
>::type
>::type
>::type
operator>>(F f) const {
using padding =
typename tl_apply<
typename tl_replicate<get_callable_trait<F>::num_args, unit_t>::type,
std::tuple
>::type;
return {f, std::tuple_cat(m_guards, padding{})};
}
private:
static guards_tuple make_guards(tail_argument_token&) {
return {};
}
template <class F, class... Fs>
static guards_tuple make_guards(std::pair<wrapped<arg_match_t>, F>,
tail_argument_token&, Fs&... fs) {
return std::make_tuple(std::move(fs)...);
}
template <class T, class F, class... Fs>
static guards_tuple make_guards(std::pair<wrapped<T>, F> x, Fs&&... fs) {
return make_guards(std::forward<Fs>(fs)..., x.second);
}
template <class F, class... Fs>
static guards_tuple make_guards(std::pair<wrapped<anything>, F>, Fs&&... fs) {
return make_guards(std::forward<Fs>(fs)...);
}
template <class... Ts, class... Fs>
static guards_tuple make_guards(type_list<Ts...>, Fs&... fs) {
tail_argument_token eoa;
return make_guards(std::make_pair(wrapped<Ts>(), std::ref(fs))..., eoa);
}
guards_tuple m_guards;
};
template <class Left, class Right>
typename std::enable_if<
std::is_base_of<message_case_builder, Left>::value
&& std::is_base_of<message_case_builder, Right>::value,
message_case_pair_builder<Left, Right>
>::type
operator||(Left l, Right r) {
return {l, r};
}
} // namespace detail
} // namespace caf
#endif // CAF_DETAIL_MESSAGE_CASE_BUILDER_HPP
......@@ -17,201 +17,120 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_DETAIL_LIFTED_FUN_HPP
#define CAF_DETAIL_LIFTED_FUN_HPP
#ifndef CAF_DETAIL_OPTIONAL_MESSAGE_VISITOR_HPP
#define CAF_DETAIL_OPTIONAL_MESSAGE_VISITOR_HPP
#include "caf/none.hpp"
#include "caf/unit.hpp"
#include "caf/optional.hpp"
#include "caf/skip_message.hpp"
#include "caf/static_visitor.hpp"
#include "caf/response_promise.hpp"
#include "caf/typed_response_promise.hpp"
#include "caf/detail/int_list.hpp"
#include "caf/detail/type_list.hpp"
#include "caf/detail/tuple_zip.hpp"
#include "caf/detail/apply_args.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/detail/left_or_right.hpp"
namespace caf {
namespace detail {
struct lifted_fun_zipper {
template <class F, typename T>
auto operator()(const F& fun, T& arg) -> decltype(fun(arg)) const {
return fun(arg);
}
// forward everything as reference if no guard/transformation is set
template <class T>
auto operator()(const unit_t&, T& arg) const -> decltype(std::ref(arg)) {
return std::ref(arg);
}
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;
};
template <class T>
T& unopt(T& v) {
return v;
}
struct is_response_promise : std::false_type { };
template <>
struct is_response_promise<response_promise> : std::true_type { };
template <class T>
struct is_response_promise<typed_response_promise<T>> : std::true_type { };
template <class T>
T& unopt(optional<T>& v) {
return *v;
}
inline bool has_none() {
return false;
}
template <class T, class... Ts>
bool has_none(const T&, const Ts&... vs) {
return has_none(vs...);
}
template <class T, class... Ts>
bool has_none(const optional<T>& v, const Ts&... vs) {
return !v || has_none(vs...);
}
// allows F to have fewer arguments than the lifted_fun calling it
template <class R, typename F>
class lifted_fun_invoker {
struct optional_message_visitor_enable_tpl {
static constexpr bool value =
!is_one_of<
typename std::remove_const<T>::type,
none_t,
unit_t,
skip_message_t,
optional<skip_message_t>
>::value
&& !is_message_id_wrapper<T>::value
&& !is_response_promise<T>::value;
};
class optional_message_visitor : public static_visitor<optional<message>> {
public:
using arg_types = typename get_callable_trait<F>::arg_types;
static constexpr size_t num_args = tl_size<arg_types>::value;
optional_message_visitor() = default;
using opt_msg = optional<message>;
lifted_fun_invoker(F& fun) : f(fun) {
// nop
inline opt_msg operator()(const none_t&) const {
return none;
}
template <class... Ts>
typename std::enable_if<sizeof...(Ts) == num_args, R>::type
operator()(Ts&... vs) const {
if (has_none(vs...)) {
return none;
}
return f(unopt(vs)...);
inline opt_msg operator()(const skip_message_t&) const {
return none;
}
template <class T, class... Ts>
typename std::enable_if<(sizeof...(Ts) + 1 > num_args), R>::type
operator()(T& v, Ts&... vs) const {
if (has_none(v)) {
inline opt_msg operator()(const unit_t&) const {
return message{};
}
inline opt_msg operator()(const optional<skip_message_t>& val) const {
if (val) {
return none;
}
return (*this)(vs...);
return message{};
}
private:
F& f;
};
template <class F>
class lifted_fun_invoker<bool, F> {
public:
using arg_types = typename get_callable_trait<F>::arg_types;
static constexpr size_t num_args = tl_size<arg_types>::value;
lifted_fun_invoker(F& fun) : f(fun) {
// nop
inline opt_msg operator()(const response_promise&) const {
return message{};
}
template <class... Ts>
typename std::enable_if<sizeof...(Ts) == num_args, bool>::type
operator()(Ts&&... vs) const {
if (has_none(vs...)) {
return false;
}
f(unopt(vs)...);
return true;
template <class T>
inline opt_msg operator()(const typed_response_promise<T>&) const {
return message{};
}
template <class T, class... Ts>
typename std::enable_if<(sizeof...(Ts) + 1 > num_args), bool>::type
operator()(T&& arg, Ts&&... vs) const {
if (has_none(arg)) {
return false;
}
return (*this)(vs...);
typename std::enable_if<
optional_message_visitor_enable_tpl<T>::value,
opt_msg
>::type
operator()(T& v, Ts&... vs) const {
return make_message(std::move(v), std::move(vs)...);
}
private:
F& f;
};
/**
* A lifted functor consists of a set of projections, a plain-old
* functor and its signature. Note that the signature of the lifted
* functor might differ from the underlying functor, because
* of the projections.
*/
template <class F, class ListOfProjections, class... Args>
class lifted_fun {
public:
using plain_result_type = typename get_callable_trait<F>::result_type;
using result_type =
typename std::conditional<
std::is_reference<plain_result_type>::value,
plain_result_type,
typename std::remove_const<plain_result_type>::type
>::type;
// Let F be "R (Ts...)" then lifted_fun<F...> returns optional<R>
// unless R is void in which case bool is returned
using optional_result_type =
typename std::conditional<
std::is_same<result_type, void>::value,
bool,
optional<result_type>
>::type;
using projections_list = ListOfProjections;
using projections = typename tl_apply<projections_list, std::tuple>::type;
using arg_types = type_list<Args...>;
lifted_fun() = default;
lifted_fun(const lifted_fun&) = default;
lifted_fun& operator=(lifted_fun&&) = default;
lifted_fun& operator=(const lifted_fun&) = default;
lifted_fun(F f) : m_fun(std::move(f)) {
// nop
template <class T>
typename std::enable_if<
is_message_id_wrapper<T>::value,
opt_msg
>::type
operator()(T& value) const {
return make_message(atom("MESSAGE_ID"),
value.get_message_id().integer_value());
}
lifted_fun(F f, projections ps) : m_fun(std::move(f)), m_ps(std::move(ps)) {
// nop
template <class L, class R>
opt_msg operator()(either_or_t<L, R>& value) const {
return std::move(value.value);
}
/**
* Invokes `fun` with a lifted_fun of `args....
*/
optional_result_type operator()(Args... args) {
auto indices = get_indices(m_ps);
lifted_fun_zipper zip;
lifted_fun_invoker<optional_result_type, F> invoke{m_fun};
return apply_args(invoke, indices,
tuple_zip(zip, indices, m_ps,
std::forward_as_tuple(args...)));
template <class... Ts>
opt_msg operator()(std::tuple<Ts...>& value) const {
return apply_args(*this, get_indices(value), value);
}
private:
F m_fun;
projections m_ps;
};
template <class F, class ListOfProjections, class List>
struct get_lifted_fun;
template <class F, class ListOfProjections, class... Ts>
struct get_lifted_fun<F, ListOfProjections, type_list<Ts...>> {
using type = lifted_fun<F, ListOfProjections, Ts...>;
};
} // namespace detail
} // namespace caf
#endif // CAF_DETAIL_LIFTED_FUN_HPP
#endif // CAF_DETAIL_OPTIONAL_MESSAGE_VISITOR_HPP
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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_DETAIL_TAIL_ARGUMENT_TOKEN
#define CAF_DETAIL_TAIL_ARGUMENT_TOKEN
namespace caf {
namespace detail {
struct tail_argument_token { };
} // namespace detail
} // namespace caf
#endif // CAF_DETAIL_TAIL_ARGUMENT_TOKEN
......@@ -17,8 +17,8 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_DETAIL_MATCHES_HPP
#define CAF_DETAIL_MATCHES_HPP
#ifndef CAF_DETAIL_TRY_MATCH_HPP
#define CAF_DETAIL_TRY_MATCH_HPP
#include <array>
#include <numeric>
......@@ -93,4 +93,4 @@ bool try_match(const message& msg, const meta_element* pattern_begin,
} // namespace detail
} // namespace caf
#endif // CAF_DETAIL_MATCHES_HPP
#endif // CAF_DETAIL_TRY_MATCH_HPP
......@@ -29,8 +29,8 @@ namespace detail {
template <class F, long... Is, class Tup0, class Tup1>
auto tuple_zip(F& f, detail::int_list<Is...>, Tup0&& tup0, Tup1&& tup1)
-> decltype(std::make_tuple(f(std::get<Is>(tup0), std::get<Is>(tup1))...)) {
return std::make_tuple(f(std::get<Is>(tup0), std::get<Is>(tup1))...);
-> decltype(std::make_tuple(f(get<Is>(tup0), get<Is>(tup1))...)) {
return std::make_tuple(f(get<Is>(tup0), get<Is>(tup1))...);
}
} // namespace detail
......
......@@ -223,12 +223,29 @@ struct tl_slice {
};
/**
* Zips two lists of equal size.
*
* Creates a list formed from the two lists `ListA` and `ListB,`
* e.g., tl_zip<type_list<int, double>, type_list<float, string>>::type
* is type_list<type_pair<int, float>, type_pair<double, string>>.
* Creates a new list containing the last `N` elements.
*/
template <class List, size_t NewSize, size_t OldSize = tl_size<List>::value>
struct tl_right {
static constexpr size_t first_idx = OldSize > NewSize ? OldSize - NewSize : 0;
using type = typename tl_slice<List, first_idx, OldSize>::type;
};
template <class List, size_t N>
struct tl_right<List, N, N> {
using type = List;
};
template <size_t N>
struct tl_right<empty_type_list, N, 0> {
using type = empty_type_list;
};
template <>
struct tl_right<empty_type_list, 0, 0> {
using type = empty_type_list;
};
template <class ListA, class ListB,
template <class, typename> class Fun = to_type_pair>
struct tl_zip_impl;
......@@ -241,7 +258,14 @@ struct tl_zip_impl<type_list<LhsElements...>, type_list<RhsElements...>, Fun> {
using type = type_list<typename Fun<LhsElements, RhsElements>::type...>;
};
template <class ListA, class ListB, template <class, typename> class Fun>
/**
* Zips two lists of equal size.
*
* Creates a list formed from the two lists `ListA` and `ListB,`
* e.g., tl_zip<type_list<int, double>, type_list<float, string>>::type
* is type_list<type_pair<int, float>, type_pair<double, string>>.
*/
template <class ListA, class ListB, template <class, class> class Fun>
struct tl_zip {
static constexpr size_t sizea = tl_size<ListA>::value;
static constexpr size_t sizeb = tl_size<ListB>::value;
......@@ -254,6 +278,19 @@ struct tl_zip {
>::type;
};
/**
* Equal to `zip(right(ListA, N), right(ListB, N), Fun)`.
*/
template <class ListA, class ListB, template <class, class> class Fun, size_t N>
struct tl_zip_right {
using type =
typename tl_zip_impl<
typename tl_right<ListA, N>::type,
typename tl_right<ListB, N>::type,
Fun
>::type;
};
template <class ListA, class ListB,
typename PadA = unit_t, typename PadB = unit_t,
template <class, typename> class Fun = to_type_pair>
......@@ -509,7 +546,14 @@ struct tl_push_front;
template <class... ListTs, class What>
struct tl_push_front<type_list<ListTs...>, What> {
using type = type_list<What, ListTs...>;
};
/**
* Alias for `tl_push_front<List, What>`.
*/
template <class What, class List>
struct tl_cons : tl_push_front<List, What> {
// nop
};
// list map(list, trait)
......@@ -760,21 +804,6 @@ struct tl_is_distinct {
tl_size<L>::value == tl_size<typename tl_distinct<L>::type>::value;
};
/**
* Creates a new list containing the last `N` elements.
*/
template <class List, size_t N>
struct tl_right {
static constexpr size_t list_size = tl_size<List>::value;
static constexpr size_t first_idx = (list_size > N) ? (list_size - N) : 0;
using type = typename tl_slice<List, first_idx, list_size>::type;
};
template <size_t N>
struct tl_right<empty_type_list, N> {
using type = empty_type_list;
};
// list resize(list, size, fill_type)
template <class List, bool OldSizeLessNewSize, size_t OldSize, size_t NewSize,
......@@ -993,6 +1022,16 @@ struct tl_equal {
&& tl_is_strict_subset<ListB, ListA>::value;
};
template <size_t N, class T>
struct tl_replicate {
using type = typename tl_cons<T, typename tl_replicate<N - 1, T>::type>::type;
};
template <class T>
struct tl_replicate<0, T> {
using type = type_list<>;
};
} // namespace detail
} // namespace caf
......
......@@ -382,6 +382,7 @@ struct get_callable_trait {
using result_type = typename type::result_type;
using arg_types = typename type::arg_types;
using fun_type = typename type::fun_type;
static constexpr size_t num_args = tl_size<arg_types>::value;
};
/**
......
......@@ -22,39 +22,41 @@
#include <memory>
#include "caf/atom.hpp"
#include "caf/detail/wrapped.hpp"
namespace caf {
namespace detail {
// strips `wrapped` and converts `atom_constant` to `atom_value`
template <class T>
struct unboxed {
using type = T;
};
template <class T>
struct unboxed<detail::wrapped<T>> {
using type = typename detail::wrapped<T>::type;
};
template <class T>
struct unboxed<detail::wrapped<T>(&)()> {
using type = typename detail::wrapped<T>::type;
};
template <class T>
struct unboxed<detail::wrapped<T>()> {
using type = typename detail::wrapped<T>::type;
};
template <class T>
struct unboxed<detail::wrapped<T>(*)()> {
using type = typename detail::wrapped<T>::type;
};
template <atom_value V>
struct unboxed<atom_constant<V>> {
using type = atom_value;
};
} // namespace detail
......
......@@ -26,13 +26,11 @@ namespace detail {
template <class T>
struct wrapped {
using type = T;
};
template <class T>
struct wrapped<wrapped<T>> {
using type = typename wrapped<T>::type;
};
} // namespace detail
......
......@@ -28,6 +28,9 @@ namespace caf {
template <class>
class intrusive_ptr;
template <class>
class optional;
// classes
class actor;
class group;
......
......@@ -34,7 +34,6 @@
#include "caf/behavior.hpp"
#include "caf/spawn_fwd.hpp"
#include "caf/message_id.hpp"
#include "caf/match_expr.hpp"
#include "caf/exit_reason.hpp"
#include "caf/typed_actor.hpp"
#include "caf/spawn_options.hpp"
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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_MATCH_CASE_HPP
#define CAF_MATCH_CASE_HPP
#include "caf/none.hpp"
#include "caf/optional.hpp"
#include "caf/skip_message.hpp"
#include "caf/match_case.hpp"
#include "caf/detail/int_list.hpp"
#include "caf/detail/try_match.hpp"
#include "caf/detail/type_list.hpp"
#include "caf/detail/tuple_zip.hpp"
#include "caf/detail/apply_args.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/detail/pseudo_tuple.hpp"
#include "caf/detail/left_or_right.hpp"
#include "caf/detail/optional_message_visitor.hpp"
namespace caf {
class match_case {
public:
enum result {
fall_through,
no_match,
match
};
match_case(bool has_wildcard, uint32_t token);
match_case(match_case&&) = default;
match_case(const match_case&) = default;
virtual ~match_case();
virtual result invoke(optional<message>&, message&) = 0;
inline uint32_t type_token() const {
return m_token;
}
inline bool has_wildcard() const {
return m_has_wildcard;
}
private:
bool m_has_wildcard;
uint32_t m_token;
};
struct match_case_zipper {
template <class F, typename T>
auto operator()(const F& fun, T& arg) -> decltype(fun(arg)) const {
return fun(arg);
}
// forward everything as reference if no guard/transformation is set
template <class T>
auto operator()(const unit_t&, T& arg) const -> decltype(std::ref(arg)) {
return std::ref(arg);
}
};
template <class T>
T& unopt(T& v) {
return v;
}
template <class T>
T& unopt(optional<T>& v) {
return *v;
}
struct has_none {
inline bool operator()() const {
return false;
}
template <class V, class... Vs>
bool operator()(const V&, const Vs&... vs) const {
return (*this)(vs...);
}
template <class V, class... Vs>
bool operator()(const optional<V>& v, const Vs&... vs) const {
return !v || (*this)(vs...);
}
};
template <bool IsVoid, class F>
class lfinvoker {
public:
lfinvoker(F& fun) : m_fun(fun) {
// nop
}
template <class... Vs>
typename detail::get_callable_trait<F>::result_type operator()(Vs&&... vs) {
return m_fun(unopt(std::forward<Vs>(vs))...);
}
private:
F& m_fun;
};
template <class F>
class lfinvoker<true, F> {
public:
lfinvoker(F& fun) : m_fun(fun) {
// nop
}
template <class... Vs>
unit_t operator()(Vs&&... vs) {
m_fun(unopt(std::forward<Vs>(vs))...);
return unit;
}
private:
F& m_fun;
};
template <class T>
struct projection_result {
using type = typename detail::get_callable_trait<T>::result_type;
};
template <>
struct projection_result<unit_t> {
using type = unit_t;
};
template <class F>
class trivial_match_case : public match_case {
public:
using fun_trait = typename detail::get_callable_trait<F>::type;
using plain_result_type = typename fun_trait::result_type;
using result_type =
typename std::conditional<
std::is_reference<plain_result_type>::value,
plain_result_type,
typename std::remove_const<plain_result_type>::type
>::type;
using arg_types = typename fun_trait::arg_types;
static constexpr bool is_manipulator =
detail::tl_exists<
arg_types,
detail::is_mutable_ref
>::value;
using pattern =
typename detail::tl_map<
arg_types,
std::decay
>::type;
using intermediate_tuple =
typename detail::tl_apply<
pattern,
detail::pseudo_tuple
>::type;
trivial_match_case(F f)
: match_case(false, detail::make_type_token_from_list<pattern>()),
m_fun(std::move(f)) {
// nop
}
match_case::result invoke(optional<message>& res, message& msg) override {
intermediate_tuple it;
detail::meta_elements<pattern> ms;
// check if try_match() reports success
if (!detail::try_match(msg, ms.arr.data(), ms.arr.size(), it.data)) {
return match_case::no_match;
}
// detach msg before invoking m_fun if needed
if (is_manipulator) {
msg.force_detach();
// update pointers in our intermediate tuple
for (size_t i = 0; i < msg.size(); ++i) {
// msg is guaranteed to be detached, hence we don't need to
// check this condition over and over again via mutable_at
it[i] = const_cast<void*>(msg.at(i));
}
}
lfinvoker<std::is_same<result_type, void>::value, F> fun{m_fun};
detail::optional_message_visitor omv;
auto funres = apply_args(fun, detail::get_indices(it), it);
res = omv(funres);
return match_case::match;
}
protected:
F m_fun;
};
template <class F>
class catch_all_match_case : public match_case {
public:
using plain_result_type = typename detail::get_callable_trait<F>::result_type;
using result_type =
typename std::conditional<
std::is_reference<plain_result_type>::value,
plain_result_type,
typename std::remove_const<plain_result_type>::type
>::type;
using arg_types = detail::type_list<>;
catch_all_match_case(F f)
: match_case(true, detail::make_type_token<>()),
m_fun(std::move(f)) {
// nop
}
match_case::result invoke(optional<message>& res, message&) override {
lfinvoker<std::is_same<result_type, void>::value, F> fun{m_fun};
auto fun_res = fun();
detail::optional_message_visitor omv;
res = omv(fun_res);
return match_case::match;
}
protected:
F m_fun;
};
template <class F, class Tuple>
class advanced_match_case : public match_case {
public:
using tuple_type = Tuple;
using base_type = advanced_match_case;
using result_type = typename detail::get_callable_trait<F>::result_type;
advanced_match_case(bool hw, uint32_t tt, F f)
: match_case(hw, tt),
m_fun(std::move(f)) {
// nop
}
virtual bool prepare_invoke(message& msg, tuple_type*) = 0;
// this function could as well be implemented in `match_case_impl`;
// however, dealing with all the template parameters in a debugger
// is just dreadful; this "hack" essentially hides all the ugly
// template boilterplate types when debugging CAF applications
match_case::result invoke(optional<message>& res, message& msg) override {
struct storage {
storage() : valid(false) {
// nop
}
~storage() {
if (valid) {
data.~tuple_type();
}
}
union { tuple_type data; };
bool valid;
};
storage st;
if (prepare_invoke(msg, &st.data)) {
st.valid = true;
lfinvoker<std::is_same<result_type, void>::value, F> fun{m_fun};
detail::optional_message_visitor omv;
auto funres = apply_args(fun, detail::get_indices(st.data), st.data);
res = omv(funres);
return match_case::match;
}
return match_case::no_match;
}
protected:
F m_fun;
};
template <class Pattern>
struct pattern_has_wildcard {
static constexpr bool value =
detail::tl_find<
Pattern,
anything
>::value != -1;
};
template <class Projections>
struct projection_is_trivial {
static constexpr bool value =
detail::tl_count_not<
Projections,
detail::tbind<std::is_same, unit_t>::template type
>::value == 0;
};
/*
* A lifted functor consists of a set of projections, a plain-old
* functor and its signature. Note that the signature of the lifted
* functor might differ from the underlying functor, because
* of the projections.
*/
template <class F, class Pattern, class Projections, class Signature>
class advanced_match_case_impl : public
advanced_match_case<
F,
typename detail::tl_apply<
typename detail::tl_zip_right<
typename detail::tl_map<
Projections,
projection_result
>::type,
typename detail::get_callable_trait<F>::arg_types,
detail::left_or_right,
detail::get_callable_trait<F>::num_args
>::type,
std::tuple
>::type> {
public:
using plain_result_type = typename detail::get_callable_trait<F>::result_type;
using result_type =
typename std::conditional<
std::is_reference<plain_result_type>::value,
plain_result_type,
typename std::remove_const<plain_result_type>::type
>::type;
using pattern = Pattern;
using filtered_pattern =
typename detail::tl_filter_not_type<
Pattern,
anything
>::type;
using intermediate_tuple =
typename detail::tl_apply<
filtered_pattern,
detail::pseudo_tuple
>::type;
static constexpr uint32_t static_type_token =
detail::make_type_token_from_list<pattern>();
// Let F be "R (Ts...)" then match_case<F...> returns optional<R>
// unless R is void in which case bool is returned
using optional_result_type =
typename std::conditional<
std::is_same<result_type, void>::value,
optional<unit_t>,
optional<result_type>
>::type;
using projections_list = Projections;
using projections =
typename detail::tl_apply<
projections_list,
std::tuple
>::type;
/*
* Needed for static type checking when assigning to a typed behavior.
*/
using arg_types = Signature;
using fun_args = typename detail::get_callable_trait<F>::arg_types;
static constexpr size_t num_fun_args = detail::tl_size<fun_args>::value;
static constexpr bool is_manipulator =
detail::tl_exists<
fun_args,
detail::is_mutable_ref
>::value;
using tuple_type =
typename detail::tl_apply<
typename detail::tl_zip_right<
typename detail::tl_map<
projections_list,
projection_result
>::type,
fun_args,
detail::left_or_right,
num_fun_args
>::type,
std::tuple
>::type;
using super = advanced_match_case<F, tuple_type>;
advanced_match_case_impl() = default;
advanced_match_case_impl(const advanced_match_case_impl&) = default;
advanced_match_case_impl& operator=(advanced_match_case_impl&&) = default;
advanced_match_case_impl& operator=(const advanced_match_case_impl&) = default;
advanced_match_case_impl(F f)
: super(pattern_has_wildcard<Pattern>::value, static_type_token,
std::move(f)) {
// nop
}
advanced_match_case_impl(F f, projections ps)
: super(pattern_has_wildcard<Pattern>::value, static_type_token,
std::move(f)),
m_ps(std::move(ps)) {
// nop
}
bool prepare_invoke(message& msg, tuple_type* out) {
// detach msg before invoking m_fun if needed
if (is_manipulator) {
msg.force_detach();
}
intermediate_tuple it;
detail::meta_elements<pattern> ms;
// check if try_match() reports success
if (!detail::try_match(msg, ms.arr.data(), ms.arr.size(), it.data)) {
return false;
}
match_case_zipper zip;
using indices_type = typename detail::il_indices<intermediate_tuple>::type;
//indices_type indices;
typename detail::il_take<indices_type, detail::tl_size<projections>::value - num_fun_args>::type lefts;
typename detail::il_right<indices_type, num_fun_args>::type rights;
has_none hn;
// check if guards of discarded arguments are fulfilled
auto lhs_tup = tuple_zip(zip, lefts, m_ps, it);
if (detail::apply_args(hn, detail::get_indices(lhs_tup), lhs_tup)) {
return false;
}
// zip remaining arguments into output tuple
new (out) tuple_type(tuple_zip(zip, rights, m_ps, it));
//tuple_type rhs_tup = tuple_zip(zip, rights, m_ps, it);
// check if remaining guards are fulfilled
if (detail::apply_args(hn, detail::get_indices(*out), *out)) {
out->~tuple_type();
return false;
}
return true;
}
private:
projections m_ps;
};
struct match_case_info {
bool has_wildcard;
uint32_t type_token;
match_case* ptr;
};
inline bool operator<(const match_case_info& x, const match_case_info& y) {
return x.type_token < y.type_token;
}
template <class F>
typename std::enable_if<
!std::is_base_of<match_case, F>::value,
std::tuple<trivial_match_case<F>>
>::type
to_match_case_tuple(F fun) {
return std::make_tuple(std::move(fun));
}
template <class MatchCase>
typename std::enable_if<
std::is_base_of<match_case, MatchCase>::value,
std::tuple<MatchCase&>
>::type
to_match_case_tuple(MatchCase& x) {
return std::tie(x);
}
template <class... Ts>
std::tuple<Ts...>& to_match_case_tuple(std::tuple<Ts...>& x) {
static_assert(detail::conjunction<
std::is_base_of<
match_case,
Ts
>::value...
>::value,
"to_match_case_tuple received a tuple of non-match_case Ts");
return x;
}
template <class T, class U>
typename std::enable_if<
std::is_base_of<match_case, T>::value || std::is_base_of<match_case, U>::value
>::type
operator,(T, U) {
static_assert(!std::is_same<T, T>::value,
"this syntax is not supported -> you probably did "
"something like 'return (...)' instead of 'return {...}'");
}
} // namespace caf
#endif // CAF_MATCH_CASE_HPP
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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_MATCH_EXPR_HPP
#define CAF_MATCH_EXPR_HPP
#include <vector>
#include "caf/none.hpp"
#include "caf/variant.hpp"
#include "caf/unit.hpp"
#include "caf/detail/int_list.hpp"
#include "caf/detail/type_list.hpp"
#include "caf/detail/purge_refs.hpp"
#include "caf/detail/apply_args.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/detail/left_or_right.hpp"
#include "caf/detail/try_match.hpp"
#include "caf/detail/type_list.hpp"
#include "caf/detail/lifted_fun.hpp"
#include "caf/detail/pseudo_tuple.hpp"
#include "caf/detail/behavior_impl.hpp"
namespace caf {
namespace detail {
template <class T1, typename T2>
T2& deduce_const(T1&, T2& rhs) {
return rhs;
}
template <class T1, typename T2>
const T2& deduce_const(const T1&, T2& rhs) {
return rhs;
}
template <class Expr, class Projecs, class Signature, class Pattern>
class match_expr_case : public get_lifted_fun<Expr, Projecs, Signature>::type {
public:
using super = typename get_lifted_fun<Expr, Projecs, Signature>::type;
template <class... Ts>
match_expr_case(Ts&&... args) : super(std::forward<Ts>(args)...) {
// nop
}
using pattern = Pattern;
using filtered_pattern =
typename detail::tl_filter_not_type<
Pattern,
anything
>::type;
static constexpr bool has_wildcard =
!std::is_same<
pattern,
filtered_pattern
>::value;
static constexpr uint32_t type_token = make_type_token_from_list<pattern>();
using intermediate_tuple =
typename detail::tl_apply<
filtered_pattern,
detail::pseudo_tuple
>::type;
};
template <class Expr, class Transformers, class Pattern>
struct get_case_ {
using ctrait = typename detail::get_callable_trait<Expr>::type;
using filtered_pattern =
typename detail::tl_filter_not_type<
Pattern,
anything
>::type;
using padded_transformers =
typename detail::tl_pad_right<
Transformers,
detail::tl_size<filtered_pattern>::value
>::type;
using base_signature =
typename detail::tl_map<
filtered_pattern,
std::add_const,
std::add_lvalue_reference
>::type;
using padded_expr_args =
typename detail::tl_map_conditional<
typename detail::tl_pad_left<
typename ctrait::arg_types,
detail::tl_size<filtered_pattern>::value
>::type,
std::is_lvalue_reference,
false,
std::add_const,
std::add_lvalue_reference
>::type;
// override base signature with required argument types of Expr
// and result types of transformation
using partial_fun_signature =
typename detail::tl_zip<
typename detail::tl_map<
padded_transformers,
detail::map_to_result_type,
detail::rm_optional,
std::add_lvalue_reference
>::type,
typename detail::tl_zip<
padded_expr_args,
base_signature,
detail::left_or_right
>::type,
detail::left_or_right
>::type;
// 'inherit' mutable references from partial_fun_signature
// for arguments without transformation
using projection_signature =
typename detail::tl_zip<
typename detail::tl_zip<
padded_transformers,
partial_fun_signature,
detail::if_not_left
>::type,
base_signature,
detail::deduce_ref_type
>::type;
using type =
match_expr_case<
Expr,
padded_transformers,
projection_signature,
Pattern
>;
};
template <bool Complete, class Expr, class Trans, class Pattern>
struct get_case {
using type = typename get_case_<Expr, Trans, Pattern>::type;
};
template <class Expr, class Trans, class Pattern>
struct get_case<false, Expr, Trans, Pattern> {
using lhs_pattern = typename detail::tl_pop_back<Pattern>::type;
using rhs_pattern =
typename detail::tl_map<
typename detail::get_callable_trait<Expr>::arg_types,
std::decay
>::type;
using type =
typename get_case_<
Expr,
Trans,
typename detail::tl_concat<
lhs_pattern,
rhs_pattern
>::type
>::type;
};
inline variant<none_t, unit_t> unroll_expr_result_unbox(bool value) {
if (value) {
return unit;
}
return none;
}
template <class T>
T& unroll_expr_result_unbox(optional<T>& opt) {
return *opt;
}
template <class Result>
struct unroll_expr {
Result operator()(message&) const {
// end of recursion
return none;
}
template <class F, class... Fs>
Result operator()(message& msg, F& f, Fs&... fs) const {
meta_elements<typename F::pattern> ms;
typename F::intermediate_tuple targs;
if ((F::has_wildcard || F::type_token == msg.type_token())
&& try_match(msg, ms.arr.data(), ms.arr.size(), targs.data)) {
auto is = detail::get_indices(targs);
auto res = detail::apply_args(f, is, deduce_const(msg, targs));
if (res) {
return std::move(unroll_expr_result_unbox(res));
}
}
return (*this)(msg, fs...);
}
};
template <bool IsManipulator, typename T0, typename T1>
struct mexpr_fwd_ {
using type = T1;
};
template <class T>
struct mexpr_fwd_<false, const T&, T> {
using type = std::reference_wrapper<const T>;
};
template <class T>
struct mexpr_fwd_<true, T&, T> {
using type = std::reference_wrapper<T>;
};
template <bool IsManipulator, typename T>
struct mexpr_fwd {
using type =
typename mexpr_fwd_<
IsManipulator,
T,
typename detail::implicit_conversions<
typename std::decay<T>::type
>::type
>::type;
};
inline void detach_if_needed(message& msg, std::true_type) {
msg.force_detach();
}
inline void detach_if_needed(const message&, std::false_type) {
// nop
}
template <class T>
struct is_manipulator_case {
// static constexpr bool value = T::second_type::manipulates_args;
using arg_types = typename T::arg_types;
static constexpr bool value = tl_exists<arg_types, is_mutable_ref>::value;
};
template <class T>
struct get_case_result {
// using type = typename T::second_type::result_type;
using type = typename T::result_type;
};
} // namespace detail
} // namespace caf
namespace caf {
template <class List>
struct match_result_from_type_list;
template <class... Ts>
struct match_result_from_type_list<detail::type_list<Ts...>> {
using type = variant<none_t, typename lift_void<Ts>::type...>;
};
/**
* A match expression encapsulating cases `Cs..., whereas
* each case is a `detail::match_expr_case<`...>.
*/
template <class... Cs>
class match_expr {
public:
static_assert(sizeof...(Cs) < 64, "too many functions");
using cases_list = detail::type_list<Cs...>;
using result_type =
typename match_result_from_type_list<
typename detail::tl_distinct<
typename detail::tl_map<
cases_list,
detail::get_case_result
>::type
>::type
>::type;
template <class T, class... Ts>
match_expr(T v, Ts&&... vs) : m_cases(std::move(v), std::forward<Ts>(vs)...) {
// nop
}
match_expr(match_expr&&) = default;
match_expr(const match_expr&) = default;
result_type operator()(message& msg) {
using mutator_token = std::integral_constant<bool,
detail::tl_exists<
cases_list,
detail::is_manipulator_case
>::value>;
detail::detach_if_needed(msg, mutator_token{});
auto indices = detail::get_indices(m_cases);
detail::unroll_expr<result_type> f;
return detail::apply_args_prefixed(f, indices, m_cases, msg);
}
template <class... Ds>
match_expr<Cs..., Ds...> or_else(const match_expr<Ds...>& other) const {
return {tuple_cat(m_cases, other.cases())};
}
/** @cond PRIVATE */
const std::tuple<Cs...>& cases() const {
return m_cases;
}
intrusive_ptr<detail::behavior_impl> as_behavior_impl() const {
// return new pfun_impl(*this);
auto lvoid = [] {};
using impl = detail::default_behavior_impl<match_expr, decltype(lvoid)>;
return new impl(*this, duration{}, lvoid);
}
/** @endcond */
private:
// structure: std::tuple<std::tuple<type_list<...>, ...>,
// std::tuple<type_list<...>, ...>,
// ...>
std::tuple<Cs...> m_cases;
};
template <class T>
struct is_match_expr : std::false_type { };
template <class... Cs>
struct is_match_expr<match_expr<Cs...>> : std::true_type { };
template <class... Lhs, class... Rhs>
match_expr<Lhs..., Rhs...> operator,(const match_expr<Lhs...>& lhs,
const match_expr<Rhs...>& rhs) {
return lhs.or_else(rhs);
}
template <class... Cs>
match_expr<Cs...>& match_expr_collect(match_expr<Cs...>& arg) {
return arg;
}
template <class... Cs>
match_expr<Cs...>&& match_expr_collect(match_expr<Cs...>&& arg) {
return std::move(arg);
}
template <class... Cs>
const match_expr<Cs...>& match_expr_collect(const match_expr<Cs...>& arg) {
return arg;
}
template <class T, class... Ts>
typename detail::tl_apply<
typename detail::tl_concat<
typename T::cases_list,
typename Ts::cases_list...
>::type,
match_expr
>::type
match_expr_collect(const T& arg, const Ts&... args) {
return {std::tuple_cat(arg.cases(), args.cases()...)};
}
namespace detail {
// implemented in message_handler.cpp
message_handler combine(behavior_impl_ptr, behavior_impl_ptr);
behavior_impl_ptr extract(const message_handler&);
template <class... Cs>
behavior_impl_ptr extract(const match_expr<Cs...>& arg) {
return arg.as_behavior_impl();
}
template <class... As, class... Bs>
match_expr<As..., Bs...> combine(const match_expr<As...>& lhs,
const match_expr<Bs...>& rhs) {
return lhs.or_else(rhs);
}
// forwards match_expr as match_expr as long as combining two match_expr,
// otherwise turns everything into behavior_impl_ptr
template <class... As, class... Bs>
const match_expr<As...>& combine_fwd(const match_expr<As...>& lhs,
const match_expr<Bs...>&) {
return lhs;
}
template <class T, typename U>
behavior_impl_ptr combine_fwd(T& lhs, U&) {
return extract(lhs);
}
template <class T>
behavior_impl_ptr match_expr_concat(const T& arg) {
return arg.as_behavior_impl();
}
template <class F>
behavior_impl_ptr match_expr_concat(const message_handler& arg0,
const timeout_definition<F>& arg) {
return extract(arg0)->copy(arg);
}
template <class... Cs, typename F>
behavior_impl_ptr match_expr_concat(const match_expr<Cs...>& arg0,
const timeout_definition<F>& arg) {
return new default_behavior_impl<match_expr<Cs...>, F>{arg0, arg};
}
template <class T0, typename T1, class... Ts>
behavior_impl_ptr match_expr_concat(const T0& arg0, const T1& arg1,
const Ts&... args) {
return match_expr_concat(combine(combine_fwd(arg0, arg1),
combine_fwd(arg1, arg0)),
args...);
}
// some more convenience functions
template <class T,
class E = typename std::enable_if<
is_callable<T>::value && !is_match_expr<T>::value
>::type>
match_expr<typename get_case<false, T, type_list<>, type_list<>>::type>
lift_to_match_expr(T arg) {
using result_type =
typename get_case<
false,
T,
detail::empty_type_list,
detail::empty_type_list
>::type;
return result_type{std::move(arg)};
}
template <class T,
class E = typename std::enable_if<
!is_callable<T>::value || is_match_expr<T>::value
>::type>
T lift_to_match_expr(T arg) {
return arg;
}
} // namespace detail
} // namespace caf
#endif // CAF_MATCH_EXPR_HPP
......@@ -26,6 +26,7 @@
#include <utility>
#include <type_traits>
#include "caf/fwd.hpp"
#include "caf/none.hpp"
#include "caf/intrusive_ptr.hpp"
......@@ -46,6 +47,8 @@ namespace caf {
*/
class message_handler {
public:
friend class behavior;
message_handler() = default;
message_handler(message_handler&&) = default;
message_handler(const message_handler&) = default;
......@@ -88,13 +91,17 @@ class message_handler {
/**
* Assigns new message handlers.
*/
template <class T, class... Ts>
void assign(T&& v, Ts&&... vs) {
m_impl = detail::match_expr_concat(
detail::lift_to_match_expr(std::forward<T>(v)),
detail::lift_to_match_expr(std::forward<Ts>(vs))...);
template <class... Vs>
void assign(Vs... vs) {
static_assert(sizeof...(Vs) > 0, "assign without arguments called");
m_impl = detail::make_behavior(vs...);
}
/**
* Equal to `*this = other`.
*/
void assign(message_handler other);
/**
* Runs this handler and returns its (optional) result.
*/
......@@ -110,7 +117,8 @@ class message_handler {
template <class... Ts>
typename std::conditional<
detail::disjunction<may_have_timeout<
typename std::decay<Ts>::type>::value...>::value,
typename std::decay<Ts>::type>::value...
>::value,
behavior,
message_handler
>::type
......@@ -124,35 +132,13 @@ class message_handler {
if (m_impl) {
return m_impl->or_else(tmp.as_behavior_impl());
}
return tmp;
return tmp.as_behavior_impl();
}
private:
impl_ptr m_impl;
};
/**
* Enables concatenation of message handlers by using a list
* of commata separated handlers.
* @relates message_handler
*/
template <class... Cases>
message_handler operator,(const match_expr<Cases...>& mexpr,
const message_handler& pfun) {
return mexpr.as_behavior_impl()->or_else(pfun.as_behavior_impl());
}
/**
* Enables concatenation of message handlers by using a list
* of commata separated handlers.
* @relates message_handler
*/
template <class... Cases>
message_handler operator,(const message_handler& pfun,
const match_expr<Cases...>& mexpr) {
return pfun.as_behavior_impl()->or_else(mexpr.as_behavior_impl());
}
} // namespace caf
#endif // CAF_PARTIAL_FUNCTION_HPP
......@@ -30,188 +30,25 @@
#include "caf/anything.hpp"
#include "caf/message.hpp"
#include "caf/duration.hpp"
#include "caf/match_expr.hpp"
#include "caf/skip_message.hpp"
#include "caf/may_have_timeout.hpp"
#include "caf/timeout_definition.hpp"
#include "caf/detail/boxed.hpp"
#include "caf/detail/unboxed.hpp"
#include "caf/detail/type_list.hpp"
#include "caf/detail/arg_match_t.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/detail/boxed.hpp"
#include "caf/detail/unboxed.hpp"
#include "caf/detail/tail_argument_token.hpp"
#include "caf/detail/implicit_conversions.hpp"
#include "caf/detail/message_case_builder.hpp"
namespace caf {
namespace detail {
template <bool IsFun, typename T>
struct add_ptr_to_fun_ {
using type = T*;
};
template <class T>
struct add_ptr_to_fun_<false, T> {
using type = T;
};
template <class T>
struct add_ptr_to_fun : add_ptr_to_fun_<std::is_function<T>::value, T> {};
template <bool ToVoid, typename T>
struct to_void_impl {
using type = unit_t;
};
template <class T>
struct to_void_impl<false, T> {
using type = typename add_ptr_to_fun<T>::type;
};
template <class T>
struct boxed_to_void : to_void_impl<is_boxed<T>::value, T> {};
template <class T>
struct boxed_to_void<std::function<
optional<T>(const T&)>> : to_void_impl<is_boxed<T>::value, T> {};
template <class T>
struct boxed_and_callable_to_void
: to_void_impl<is_boxed<T>::value || detail::is_callable<T>::value, T> {};
class behavior_rvalue_builder {
public:
constexpr behavior_rvalue_builder(const duration& d) : m_tout(d) {}
template <class F>
timeout_definition<F> operator>>(F&& f) const {
return {m_tout, std::forward<F>(f)};
}
private:
duration m_tout;
};
struct rvalue_builder_args_ctor {};
template <class Left, class Right>
struct disjunct_rvalue_builders {
public:
disjunct_rvalue_builders(Left l, Right r)
: m_left(std::move(l)), m_right(std::move(r)) {}
template <class Expr>
auto operator>>(Expr expr)
-> decltype((*(static_cast<Left*>(nullptr)) >> expr)
.or_else(*(static_cast<Right*>(nullptr)) >>
expr)) const {
return (m_left >> expr).or_else(m_right >> expr);
}
private:
Left m_left;
Right m_right;
};
struct tuple_maker {
template <class... Ts>
inline auto operator()(Ts&&... args)
-> decltype(std::make_tuple(std::forward<Ts>(args)...)) {
return std::make_tuple(std::forward<Ts>(args)...);
}
};
template <class Transformers, class Pattern>
struct rvalue_builder {
using back_type = typename detail::tl_back<Pattern>::type;
static constexpr bool is_complete =
!std::is_same<detail::arg_match_t, back_type>::value;
using fun_container =
typename detail::tl_apply<
Transformers,
std::tuple
>::type;
fun_container m_funs;
public:
rvalue_builder() = default;
template <class... Ts>
rvalue_builder(rvalue_builder_args_ctor, const Ts&... args)
: m_funs(args...) {}
rvalue_builder(fun_container arg1) : m_funs(std::move(arg1)) {}
template <class Expr>
match_expr<typename get_case<is_complete, Expr, Transformers, Pattern>::type>
operator>>(Expr expr) const {
using lifted_expr =
typename get_case<
is_complete,
Expr,
Transformers,
Pattern
>::type;
// adjust m_funs to exactly match expected projections in match case
using target = typename lifted_expr::projections_list;
using trimmed_projections = typename tl_trim<Transformers>::type;
tuple_maker f;
auto lhs = apply_args(f, get_indices(trimmed_projections{}), m_funs);
using rhs_type =
typename tl_apply<
typename tl_slice<
target,
tl_size<trimmed_projections>::value,
tl_size<target>::value
>::type,
std::tuple
>::type;
rhs_type rhs;
// done
return lifted_expr{std::move(expr), std::tuple_cat(lhs, rhs)};
}
template <class T, class P>
disjunct_rvalue_builders<rvalue_builder, rvalue_builder<T, P>>
operator||(rvalue_builder<T, P> other) const {
return {*this, std::move(other)};
}
};
template <bool IsCallable, typename T>
struct pattern_type_ {
using ctrait = detail::get_callable_trait<T>;
using args = typename ctrait::arg_types;
static_assert(detail::tl_size<args>::value == 1,
"only unary functions allowed");
using type =
typename std::decay<
typename detail::tl_head<args>::type
>::type;
};
template <class T>
struct pattern_type_<false, T> {
template <class T, bool IsFun = detail::is_callable<T>::value
&& !detail::is_boxed<T>::value>
struct pattern_type {
using type =
typename implicit_conversions<
typename std::decay<
......@@ -221,19 +58,17 @@ struct pattern_type_<false, T> {
};
template <class T>
struct pattern_type {
struct pattern_type<T, true> {
using ctrait = detail::get_callable_trait<T>;
using args = typename ctrait::arg_types;
static_assert(detail::tl_size<args>::value == 1,
"only unary functions are allowed as projections");
using type =
typename pattern_type_<
detail::is_callable<T>::value && !detail::is_boxed<T>::value,
T
typename std::decay<
typename detail::tl_head<args>::type
>::type;
};
template <atom_value V>
struct pattern_type<atom_constant<V>> {
using type = atom_value;
};
} // namespace detail
} // namespace caf
......@@ -242,80 +77,28 @@ namespace caf {
/**
* A wildcard that matches any number of any values.
*/
constexpr anything any_vals = anything{};
#ifdef CAF_DOCUMENTATION
/**
* A wildcard that matches the argument types
* of a given callback. Must be the last argument to {@link on()}.
* @see {@link math_actor_example.cpp Math Actor Example}
*/
constexpr __unspecified__ arg_match;
/**
* Left-hand side of a partial function expression.
*
* Equal to `on(arg_match).
*/
constexpr __unspecified__ on_arg_match;
constexpr auto any_vals = anything();
/**
* A wildcard that matches any value of type `T`.
* @see {@link math_actor_example.cpp Math Actor Example}
*/
template <class T>
__unspecified__ val();
/**
* Left-hand side of a partial function expression that matches values.
*
* This overload can be used with the wildcards {@link caf::val val},
* {@link caf::any_vals any_vals} and {@link caf::arg_match arg_match}.
*/
template <class T, class... Ts>
__unspecified__ on(const T& arg, const Ts&... args);
constexpr typename detail::boxed<T>::type val() {
return typename detail::boxed<T>::type();
}
/**
* Left-hand side of a partial function expression that matches types.
*
* This overload matches types only. The type {@link caf::anything anything}
* can be used as wildcard to match any number of elements of any types.
* A wildcard that matches the argument types
* of a given callback, must be the last argument to `on()`.
*/
template <class... Ts>
__unspecified__ on();
/**
* Left-hand side of a partial function expression that matches types.
*
* This overload matches up to four leading atoms.
* The type {@link caf::anything anything}
* can be used as wildcard to match any number of elements of any types.
*/
template <atom_value... Atoms, class... Ts>
__unspecified__ on();
constexpr auto arg_match = typename detail::boxed<detail::arg_match_t>::type();
/**
* Converts `arg` to a match expression by returning
* `on_arg_match >> arg if `arg` is a callable type,
* otherwise returns `arg`.
* Generates function objects from a binary predicate and a value.
*/
template <class T>
__unspecified__ lift_to_match_expr(T arg);
#else
template <class T>
constexpr typename detail::boxed<T>::type val() {
return typename detail::boxed<T>::type();
}
using boxed_arg_match_t = typename detail::boxed<detail::arg_match_t>::type;
constexpr boxed_arg_match_t arg_match = boxed_arg_match_t();
template <class T, typename Predicate>
std::function<optional<T>(const T&)> guarded(Predicate p, T value) {
template <class T, typename BinaryPredicate>
std::function<optional<T>(const T&)> guarded(BinaryPredicate p, T value) {
return [=](const T& other) -> optional<T> {
if (p(other, value)) {
return value;
......@@ -364,91 +147,81 @@ auto to_guard(const atom_constant<V>&) -> decltype(to_guard(V)) {
return to_guard(V);
}
template <class T, class... Ts>
auto on(const T& arg, const Ts&... args)
-> detail::rvalue_builder<
/**
* Returns a generator for `match_case` objects.
*/
template <class... Ts>
auto on(const Ts&... args)
-> detail::advanced_match_case_builder<
detail::type_list<
decltype(to_guard(arg)),
decltype(to_guard(args))...
>,
detail::type_list<
typename detail::pattern_type<T>::type,
typename detail::pattern_type<Ts>::type...>
typename detail::pattern_type<typename std::decay<Ts>::type>::type...>
> {
return {detail::rvalue_builder_args_ctor{}, to_guard(arg), to_guard(args)...};
return {detail::variadic_ctor{}, to_guard(args)...};
}
inline detail::rvalue_builder<detail::empty_type_list, detail::empty_type_list>
on() {
return {};
}
template <class T0, class... Ts>
detail::rvalue_builder<detail::empty_type_list, detail::type_list<T0, Ts...>>
on() {
return {};
/**
* Returns a generator for `match_case` objects.
*/
template <class T, class... Ts>
decltype(on(val<T>(), val<Ts>()...)) on() {
return on(val<T>(), val<Ts>()...);
}
/**
* Returns a generator for `match_case` objects.
*/
template <atom_value A0, class... Ts>
decltype(on(A0, val<Ts>()...)) on() {
return on(A0, val<Ts>()...);
}
/**
* Returns a generator for `match_case` objects.
*/
template <atom_value A0, atom_value A1, class... Ts>
decltype(on(A0, A1, val<Ts>()...)) on() {
return on(A0, A1, val<Ts>()...);
}
/**
* Returns a generator for `match_case` objects.
*/
template <atom_value A0, atom_value A1, atom_value A2, class... Ts>
decltype(on(A0, A1, A2, val<Ts>()...)) on() {
return on(A0, A1, A2, val<Ts>()...);
}
/**
* Returns a generator for `match_case` objects.
*/
template <atom_value A0, atom_value A1, atom_value A2, atom_value A3,
class... Ts>
decltype(on(A0, A1, A2, A3, val<Ts>()...)) on() {
return on(A0, A1, A2, A3, val<Ts>()...);
}
/**
* Returns a generator for timeouts.
*/
template <class Rep, class Period>
constexpr detail::behavior_rvalue_builder
constexpr detail::timeout_definition_builder
after(const std::chrono::duration<Rep, Period>& d) {
return {duration(d)};
}
inline decltype(on<anything>()) others() { return on<anything>(); }
// some more convenience
namespace detail {
class on_the_fly_rvalue_builder {
public:
constexpr on_the_fly_rvalue_builder() {}
template <class Expr>
match_expr<typename get_case<false, Expr, detail::empty_type_list,
detail::empty_type_list>::type>
operator>>(Expr expr) const {
using result_type =
typename get_case<
false,
Expr,
detail::empty_type_list,
detail::empty_type_list
>::type;
return result_type{std::move(expr)};
}
};
} // namespace detail
constexpr detail::on_the_fly_rvalue_builder on_arg_match;
/**
* Generates catch-all `match_case` objects.
*/
constexpr auto others = detail::catch_all_match_case_builder();
#endif // CAF_DOCUMENTATION
/**
* Semantically equal to `on(arg_match)`, but uses a (faster)
* special-purpose `match_case` implementation.
*/
constexpr auto on_arg_match = detail::trivial_match_case_builder();
} // namespace caf
......
......@@ -59,7 +59,6 @@ class invoke_policy {
inactive_timeout, // a currently inactive timeout
expired_sync_response, // a sync response that already timed out
timeout, // triggers currently active timeout
timeout_response, // triggers timeout of a sync message
ordinary, // an asynchronous message or sync. request
sync_response // a synchronous response
};
......@@ -76,7 +75,6 @@ class invoke_policy {
behavior& fun,
message_id awaited_response) {
CAF_LOG_TRACE("");
bool handle_sync_failure_on_mismatch = true;
switch (this->filter_msg(self, *node)) {
case msg_type::normal_exit:
CAF_LOG_DEBUG("dropped normal exit signal");
......@@ -105,25 +103,29 @@ class invoke_policy {
}
return im_success;
}
case msg_type::timeout_response:
handle_sync_failure_on_mismatch = false;
CAF_ANNOTATE_FALLTHROUGH;
case msg_type::sync_response:
CAF_LOG_DEBUG("handle as synchronous response: "
<< CAF_TARG(node->msg, to_string) << ", "
<< CAF_MARG(node->mid, integer_value) << ", "
<< CAF_MARG(awaited_response, integer_value));
<< CAF_TARG(node->msg, to_string) << ", "
<< CAF_MARG(node->mid, integer_value) << ", "
<< CAF_MARG(awaited_response, integer_value));
if (awaited_response.valid() && node->mid == awaited_response) {
bool is_sync_tout = node->msg.match_elements<sync_timeout_msg>();
node.swap(self->current_mailbox_element());
auto res = invoke_fun(self, fun);
if (!res && handle_sync_failure_on_mismatch) {
CAF_LOG_WARNING("sync failure occured in actor "
<< "with ID " << self->id());
self->handle_sync_failure();
}
node.swap(self->current_mailbox_element());
self->mark_arrived(awaited_response);
self->remove_handler(awaited_response);
node.swap(self->current_mailbox_element());
if (!res) {
if (is_sync_tout) {
CAF_LOG_WARNING("sync timeout occured in actor "
<< "with ID " << self->id());
self->handle_sync_timeout();
} else {
CAF_LOG_WARNING("sync failure occured in actor "
<< "with ID " << self->id());
self->handle_sync_failure();
}
}
return im_success;
}
return im_skipped;
......@@ -220,7 +222,7 @@ class invoke_policy {
response_promise fhdl = hdl ? *hdl : self->make_response_promise();
behavior inner = *ref_opt;
ref_opt->assign(
others() >> [=] {
others >> [=] {
// inner is const inside this lambda and mutable a C++14 feature
behavior cpy = inner;
auto inner_res = cpy(self->current_message());
......@@ -243,9 +245,6 @@ class invoke_policy {
const message& msg = node.msg;
auto mid = node.mid;
if (mid.is_response()) {
if (msg.match_elements<sync_timeout_msg>()) {
return msg_type::timeout_response;
}
return self->awaits(mid) ? msg_type::sync_response
: msg_type::expired_sync_response;
}
......
......@@ -68,17 +68,9 @@ class response_handle<Self, message, nonblocking_response_handle_tag> {
// nop
}
template <class T, class... Ts>
continue_helper then(T arg, Ts&&... args) const {
auto selfptr = m_self;
behavior bhvr{
std::move(arg),
std::forward<Ts>(args)...,
on<sync_timeout_msg>() >> [selfptr]() -> skip_message_t {
selfptr->handle_sync_timeout();
return skip_message();
}
};
template <class... Vs>
continue_helper then(Vs&&... vs) const {
behavior bhvr{std::forward<Vs>(vs)...};
m_self->bhvr_stack().push_back(std::move(bhvr), m_mid);
return {m_mid};
}
......@@ -113,17 +105,12 @@ class response_handle<Self, TypedOutputPair, nonblocking_response_handle_tag> {
static_assert(sizeof...(Fs) > 0, "at least one functor is requried");
static_assert(detail::conjunction<detail::is_callable<Fs>::value...>::value,
"all arguments must be callable");
static_assert(detail::conjunction<!is_match_expr<Fs>::value...>::value,
"match expressions are not allowed in this context");
static_assert(detail::conjunction<
!std::is_base_of<match_case, Fs>::value...
>::value,
"match cases are not allowed in this context");
detail::type_checker<TypedOutputPair, Fs...>::check();
auto selfptr = m_self;
behavior tmp{
fs...,
on<sync_timeout_msg>() >> [selfptr]() -> skip_message_t {
selfptr->handle_sync_timeout();
return {};
}
};
behavior tmp{std::move(fs)...};
m_self->bhvr_stack().push_back(std::move(tmp), m_mid);
return {m_mid};
}
......@@ -151,17 +138,9 @@ class response_handle<Self, message, blocking_response_handle_tag> {
m_self->dequeue_response(bhvr, m_mid);
}
template <class T, class... Ts>
void await(T arg, Ts&&... args) const {
auto selfptr = m_self;
behavior bhvr{
std::move(arg),
std::forward<Ts>(args)...,
on<sync_timeout_msg>() >> [selfptr]() -> skip_message_t {
selfptr->handle_sync_timeout();
return {};
}
};
template <class... Vs>
void await(Vs&&... vs) const {
behavior bhvr{std::forward<Vs>(vs)...};
m_self->dequeue_response(bhvr, m_mid);
}
......@@ -199,17 +178,12 @@ class response_handle<Self, OutputPair, blocking_response_handle_tag> {
"wrong number of functors");
static_assert(detail::conjunction<detail::is_callable<Fs>::value...>::value,
"all arguments must be callable");
static_assert(detail::conjunction<!is_match_expr<Fs>::value...>::value,
"match expressions are not allowed in this context");
static_assert(detail::conjunction<
!std::is_base_of<match_case, Fs>::value...
>::value,
"match cases are not allowed in this context");
detail::type_checker<OutputPair, Fs...>::check();
auto selfptr = m_self;
behavior tmp{
fs...,
on<sync_timeout_msg>() >> [selfptr]() -> skip_message_t {
selfptr->handle_sync_timeout();
return {};
}
};
behavior tmp{std::move(fs)...};
m_self->dequeue_response(tmp, m_mid);
}
......
......@@ -20,6 +20,8 @@
#ifndef CAF_MATCH_HINT_HPP
#define CAF_MATCH_HINT_HPP
#include <ostream>
namespace caf {
/**
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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_STATIC_VISITOR_HPP
#define CAF_STATIC_VISITOR_HPP
namespace caf {
template <class Result = void>
struct static_visitor {
using result_type = Result;
};
} // namespace caf
#endif // CAF_STATIC_VISITOR_HPP
......@@ -17,12 +17,11 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef TYPED_BEHAVIOR_HPP
#define TYPED_BEHAVIOR_HPP
#ifndef CAF_TYPED_BEHAVIOR_HPP
#define CAF_TYPED_BEHAVIOR_HPP
#include "caf/either.hpp"
#include "caf/behavior.hpp"
#include "caf/match_expr.hpp"
#include "caf/system_messages.hpp"
#include "caf/typed_continue_helper.hpp"
......@@ -179,13 +178,16 @@ class behavior_stack_based_impl;
template <class... Rs>
class typed_behavior {
public:
template <class... OtherRs>
friend class typed_actor;
template <class, class, class>
friend class mixin::behavior_stack_based_impl;
template <class...>
friend class functor_based_typed_actor;
public:
typed_behavior(typed_behavior&&) = default;
typed_behavior(const typed_behavior&) = default;
typed_behavior& operator=(typed_behavior&&) = default;
......@@ -193,17 +195,9 @@ class typed_behavior {
using signatures = detail::type_list<Rs...>;
template <class T, class... Ts>
typed_behavior(T arg, Ts&&... args) {
set(match_expr_collect(
detail::lift_to_match_expr(std::move(arg)),
detail::lift_to_match_expr(std::forward<Ts>(args))...));
}
template <class... Cs>
typed_behavior& operator=(match_expr<Cs...> expr) {
set(std::move(expr));
return *this;
template <class... Vs>
typed_behavior(Vs... vs) {
set(detail::make_behavior(vs...));
}
explicit operator bool() const {
......@@ -213,29 +207,33 @@ class typed_behavior {
/**
* Invokes the timeout callback.
*/
inline void handle_timeout() { m_bhvr.handle_timeout(); }
void handle_timeout() {
m_bhvr.handle_timeout();
}
/**
* Returns the duration after which receives using
* this behavior should time out.
*/
inline const duration& timeout() const { return m_bhvr.timeout(); }
const duration& timeout() const {
return m_bhvr.timeout();
}
private:
typed_behavior() = default;
behavior& unbox() { return m_bhvr; }
template <class... Cs>
void set(match_expr<Cs...>&& expr) {
template <class... Ts>
void set(detail::default_behavior_impl<std::tuple<Ts...>>* ptr) {
using mpi =
typename detail::tl_filter_not<
detail::type_list<typename detail::deduce_mpi<Cs>::type...>,
detail::type_list<typename detail::deduce_mpi<Ts>::type...>,
detail::is_hidden_msg_handler
>::type;
detail::static_asserter<signatures, mpi, detail::ctm>::verify_match();
// final (type-erasure) step
m_bhvr = std::move(expr);
m_bhvr.assign(static_cast<detail::behavior_impl*>(ptr));
}
behavior m_bhvr;
......@@ -243,4 +241,4 @@ class typed_behavior {
} // namespace caf
#endif // TYPED_BEHAVIOR_HPP
#endif // CAF_TYPED_BEHAVIOR_HPP
......@@ -20,6 +20,8 @@
#ifndef CAF_VARIANT_HPP
#define CAF_VARIANT_HPP
#include "caf/static_visitor.hpp"
#include "caf/detail/type_list.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/detail/variant_data.hpp"
......@@ -296,11 +298,6 @@ const T* get(const variant<Us...>* value) {
return get<T>(const_cast<variant<Us...>*>(value));
}
template <class Result = void>
struct static_visitor {
using result_type = Result;
};
/**
* @relates variant
*/
......
......@@ -107,7 +107,7 @@ class timer_actor : public detail::proper_actor<blocking_actor,
[&](const exit_msg&) {
done = true;
},
others() >> [&] {
others >> [&] {
CAF_LOG_ERROR("unexpected: " << to_string(msg_ptr->msg));
}
};
......@@ -183,7 +183,7 @@ void printer_loop(blocking_actor* self) {
[&](const exit_msg&) {
running = false;
},
others() >> [&] {
others >> [&] {
std::cerr << "*** unexpected: " << to_string(self->current_message())
<< std::endl;
}
......
......@@ -28,4 +28,17 @@ behavior::behavior(const message_handler& mh) : m_impl(mh.as_behavior_impl()) {
// nop
}
void behavior::assign(message_handler other) {
m_impl.swap(other.m_impl);
}
void behavior::assign(behavior other) {
m_impl.swap(other.m_impl);
}
void behavior::assign(detail::behavior_impl* ptr) {
CAF_REQUIRE(ptr != nullptr);
m_impl.reset(ptr);
}
} // namespace caf
......@@ -26,7 +26,7 @@ namespace detail {
namespace {
class combinator : public behavior_impl {
class combinator final : public behavior_impl {
public:
bhvr_invoke_result invoke(message& arg) {
auto res = first->invoke(arg);
......@@ -50,6 +50,12 @@ class combinator : public behavior_impl {
// nop
}
protected:
match_case** get_cases(size_t&) {
// never called
return nullptr;
}
private:
pointer first;
pointer second;
......@@ -61,7 +67,24 @@ behavior_impl::~behavior_impl() {
// nop
}
behavior_impl::behavior_impl(duration tout) : m_timeout(tout) {
behavior_impl::behavior_impl(duration tout)
: m_timeout(tout),
m_begin(nullptr),
m_end(nullptr) {
// nop
}
bhvr_invoke_result behavior_impl::invoke(message& msg) {
auto msg_token = msg.type_token();
bhvr_invoke_result res;
std::find_if(m_begin, m_end, [&](const match_case_info& x) {
return (x.has_wildcard || x.type_token == msg_token)
&& x.ptr->invoke(res, msg) != match_case::no_match;
});
return res;
}
void behavior_impl::handle_timeout() {
// nop
}
......@@ -70,10 +93,5 @@ behavior_impl::pointer behavior_impl::or_else(const pointer& other) {
return new combinator(this, other);
}
behavior_impl* new_default_behavior(duration d, std::function<void()> fun) {
dummy_match_expr nop;
return new default_behavior_impl<dummy_match_expr>(nop, d, std::move(fun));
}
} // namespace detail
} // namespace caf
......@@ -159,7 +159,7 @@ class local_broker : public event_based_actor {
}
}
},
others() >> [=] {
others >> [=] {
auto msg = current_message();
CAF_LOGC_TRACE("caf::local_broker", "init$others",
CAF_TARG(msg, to_string));
......@@ -248,7 +248,7 @@ class proxy_broker : public event_based_actor {
}
behavior make_behavior() {
return {others() >> [=] {
return {others >> [=] {
m_group->send_all_subscribers(current_sender(), current_message(),
host());
}};
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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/match_case.hpp"
namespace caf {
match_case::~match_case() {
// nop
}
match_case::match_case(bool hw, uint32_t tt) : m_has_wildcard(hw), m_token(tt) {
// nop
}
} // namespace caf
......@@ -28,30 +28,8 @@ message_handler::message_handler(impl_ptr ptr) : m_impl(std::move(ptr)) {
// nop
}
void detail::behavior_impl::handle_timeout() {
// nop
}
} // namespace caf
namespace caf {
namespace detail {
behavior_impl_ptr combine(behavior_impl_ptr lhs, const message_handler& rhs) {
return lhs->or_else(rhs.as_behavior_impl());
}
behavior_impl_ptr combine(const message_handler& lhs, behavior_impl_ptr rhs) {
return lhs.as_behavior_impl()->or_else(rhs);
}
message_handler combine(behavior_impl_ptr lhs, behavior_impl_ptr rhs) {
return lhs->or_else(rhs);
}
behavior_impl_ptr extract(const message_handler& arg) {
return arg.as_behavior_impl();
void message_handler::assign(message_handler what) {
m_impl.swap(what.m_impl);
}
} // namespace util
} // namespace caf
......@@ -216,7 +216,7 @@ behavior basp_broker::make_behavior() {
return make_message(ok_atom::value, request_id);
},
// catch-all error handler
others() >> [=] {
others >> [=] {
CAF_LOG_ERROR("received unexpected message: "
<< to_string(current_message()));
}
......
......@@ -340,12 +340,12 @@ void broker::close_all() {
}
}
bool broker::valid(connection_handle handle) {
return m_scribes.count(handle) > 0;
bool broker::valid(connection_handle hdl) {
return m_scribes.count(hdl) > 0;
}
bool broker::valid(accept_handle handle) {
return m_doormen.count(handle) > 0;
bool broker::valid(accept_handle hdl) {
return m_doormen.count(hdl) > 0;
}
std::vector<connection_handle> broker::connections() const {
......
......@@ -21,7 +21,6 @@ add_unit_test(atom)
add_unit_test(metaprogramming)
add_unit_test(intrusive_containers)
add_unit_test(match)
add_unit_test(match_expr)
add_unit_test(message)
add_unit_test(serialization)
add_unit_test(uniform_type)
......
......@@ -31,7 +31,7 @@ behavior ping_behavior(local_actor* self, size_t num_pings) {
}
return make_message(ping_atom::value, value);
},
others() >> [=] {
others >> [=] {
CAF_LOGF_ERROR("unexpected; " << to_string(self->current_message()));
self->quit(exit_reason::user_shutdown);
}
......@@ -44,7 +44,7 @@ behavior pong_behavior(local_actor* self) {
CAF_PRINT("received {'ping', " << value << "}");
return make_message(pong_atom::value, value + 1);
},
others() >> [=] {
others >> [=] {
CAF_LOGF_ERROR("unexpected; " << to_string(self->current_sender()));
self->quit(exit_reason::user_shutdown);
}
......
......@@ -38,7 +38,7 @@ void testee::on_exit() {
behavior testee::make_behavior() {
return {
others() >> [=] {
others >> [=] {
return current_message();
}
};
......
......@@ -102,7 +102,7 @@ int main() {
CAF_CHECK(matched_pattern[0] && matched_pattern[1] && matched_pattern[2]);
self->receive(
// "erase" message { atom("b"), atom("a"), atom("c"), 23.f }
others() >> CAF_CHECKPOINT_CB(),
others >> CAF_CHECKPOINT_CB(),
after(std::chrono::seconds(0)) >> CAF_UNEXPECTED_TOUT_CB()
);
atom_value x = atom("abc");
......@@ -112,7 +112,7 @@ int main() {
self->send(self, msg);
self->receive(
on(atom("abc")) >> CAF_CHECKPOINT_CB(),
others() >> CAF_UNEXPECTED_MSG_CB_REF(self)
others >> CAF_UNEXPECTED_MSG_CB_REF(self)
);
test_typed_atom_interface();
return CAF_TEST_RESULT();
......
......@@ -46,9 +46,9 @@ void ping(event_based_actor* self, size_t num_pings) {
}
return std::make_tuple(ping_atom::value, value + 1);
},
others() >> CAF_UNEXPECTED_MSG_CB(self));
others >> CAF_UNEXPECTED_MSG_CB(self));
},
others() >> CAF_UNEXPECTED_MSG_CB(self)
others >> CAF_UNEXPECTED_MSG_CB(self)
);
}
......@@ -67,12 +67,12 @@ void pong(event_based_actor* self) {
CAF_PRINT("received down_msg{" << dm.reason << "}");
self->quit(dm.reason);
},
others() >> CAF_UNEXPECTED_MSG_CB(self)
others >> CAF_UNEXPECTED_MSG_CB(self)
);
// reply to 'ping'
return std::make_tuple(pong_atom::value, value);
},
others() >> CAF_UNEXPECTED_MSG_CB(self));
others >> CAF_UNEXPECTED_MSG_CB(self));
}
void peer_fun(broker* self, connection_handle hdl, const actor& buddy) {
......@@ -125,7 +125,7 @@ void peer_fun(broker* self, connection_handle hdl, const actor& buddy) {
self->quit(dm.reason);
}
},
others() >> CAF_UNEXPECTED_MSG_CB(self)
others >> CAF_UNEXPECTED_MSG_CB(self)
);
}
......@@ -141,7 +141,7 @@ behavior peer_acceptor_fun(broker* self, const actor& buddy) {
on(atom("publish")) >> [=] {
return self->add_tcp_doorman(0, "127.0.0.1").second;
},
others() >> CAF_UNEXPECTED_MSG_CB(self)
others >> CAF_UNEXPECTED_MSG_CB(self)
};
}
......@@ -186,7 +186,7 @@ int main(int argc, char** argv) {
on() >> [&] {
run_server(true, argv[0]);
},
others() >> [&] {
others >> [&] {
cerr << "usage: " << argv[0] << " [-c PORT]" << endl;
}
});
......
......@@ -41,7 +41,7 @@ void test_constructor_attach() {
quit(reason);
}
},
others() >> [=] {
others >> [=] {
forward_to(m_testee);
}
};
......
......@@ -14,7 +14,7 @@ class exception_testee : public event_based_actor {
}
behavior make_behavior() override {
return {
others() >> [] {
others >> [] {
throw std::runtime_error("whatever");
}
};
......
......@@ -49,8 +49,8 @@ void fill_mb(message_builder& mb, const T& v, const Ts&... vs) {
fill_mb(mb.append(v), vs...);
}
template <class Expr, class... Ts>
ptrdiff_t invoked(Expr& expr, const Ts&... args) {
template <class... Ts>
ptrdiff_t invoked(message_handler expr, const Ts&... args) {
vector<message> msgs;
msgs.push_back(make_message(args...));
message_builder mb;
......@@ -106,9 +106,7 @@ void test_custom_projections() {
guard_called = true;
return arg;
};
auto expr = (
on(guard) >> f(0)
);
auto expr = on(guard) >> f(0);
CAF_CHECK_EQUAL(invoked(expr, 42), 0);
CAF_CHECK_EQUAL(guard_called, true);
}
......
#include "test.hpp"
#include "caf/all.hpp"
#include "caf/detail/safe_equal.hpp"
using std::cout;
using std::endl;
using namespace caf;
#define CAF_CHECK_VARIANT(Handler, Message, Type, Expected) \
{ \
auto msg = Message; \
auto res = Handler(msg); \
if (get<Type>(&res) == nullptr) { \
CAF_FAILURE("result has invalid type"); \
} else if (!caf::detail::safe_equal(*get<Type>(&res), Expected)) { \
CAF_FAILURE("expected " << Expected << " found " << *get<Type>(&res)); \
} else { \
CAF_CHECKPOINT(); \
} \
} \
static_cast<void>(0)
#define CAF_CHECK_VARIANT_UNIT(Handler, Message) \
{ \
auto msg = Message; \
auto res = Handler(msg); \
if (get<unit_t>(&res) == nullptr) { \
CAF_FAILURE("result has invalid type"); \
} else { \
CAF_CHECKPOINT(); \
} \
} \
static_cast<void>(0)
#define CAF_CHECK_OPT_MSG(Handler, Message, Type, Expected) \
{ \
auto msg = Message; \
auto res = Handler(msg); \
if (!res) { \
CAF_FAILURE("result is none"); \
} else if (!res->match_element<Type>(0)) { \
CAF_FAILURE("result has invalid type: expected: " \
<< typeid(Expected).name() \
<< ", found: " << typeid(Type).name()); \
} else if (!caf::detail::safe_equal(res->get_as<Type>(0), Expected)) { \
CAF_FAILURE("expected " << Expected << " found " \
<< res->get_as<Type>(0)); \
} else { \
CAF_CHECKPOINT(); \
} \
} \
static_cast<void>(0)
#define CAF_CHECK_OPT_MSG_VOID(Call) \
{ \
auto res = Call; \
if (!res) { \
CAF_FAILURE("result has invalid type: optional is none"); \
} else if (!res->empty()) { \
CAF_FAILURE("result has invalid type: tuple is not empty"); \
} else { \
CAF_CHECKPOINT(); \
} \
} \
static_cast<void>(0)
#define CAF_CHECK_OPT_MSG_NONE(Call) \
{ \
auto res = Call; \
if (res) { \
if (res->empty()) { \
CAF_FAILURE("result has invalid type: expected none, " \
<< "found an empty tuple"); \
} else { \
CAF_FAILURE("result has invalid type: expected none, " \
<< "found a non-empty tuple "); \
} \
} else { \
CAF_CHECKPOINT(); \
} \
} \
static_cast<void>(0)
struct printer {
inline void operator()() const{
cout << endl;
}
template<typename V, typename... Vs>
void operator()(const optional<V>& v, const Vs&... vs) const {
if (v) {
cout << *v << " ";
} else {
cout << "[none] ";
}
(*this)(vs...);
}
template<typename V, typename... Vs>
void operator()(const V& v, const Vs&... vs) const {
cout << v << " ";
(*this)(vs...);
}
};
int main() {
CAF_TEST(test_match_expr);
auto g = guarded(std::equal_to<int>{}, 5);
auto t0 = std::make_tuple(unit, g, unit);
auto t1 = std::make_tuple(4, 5, 6);
auto t2 = std::make_tuple(5, 6, 7);
detail::lifted_fun_zipper zip;
auto is = detail::get_indices(t2);
auto r0 = detail::tuple_zip(zip, is, t0, t1);
printer p;
detail::apply_args(p, is, r0);
{ // --- types only ---
// check on() usage
auto m0 = on<int>() >> [](int) {};
// auto m0r0 = m0(make_message(1));
CAF_CHECK_VARIANT_UNIT(m0, make_message(1));
// check lifted functor
auto m1 = detail::lift_to_match_expr([](float) {});
CAF_CHECK_VARIANT_UNIT(m1, make_message(1.f));
// check _.or_else(_)
auto m2 = m0.or_else(m1);
CAF_CHECK_VARIANT_UNIT(m2, make_message(1));
CAF_CHECK_VARIANT_UNIT(m2, make_message(1.f));
// check use of match_expr_concat
auto m3 = detail::match_expr_concat(
m0, m1, detail::lift_to_match_expr([](double) {}));
CAF_CHECK_OPT_MSG_VOID(m3->invoke(make_message(1)));
CAF_CHECK_OPT_MSG_VOID(m3->invoke(make_message(1.f)));
CAF_CHECK_OPT_MSG_VOID(m3->invoke(make_message(1.)));
CAF_CHECK_OPT_MSG_NONE(m3->invoke(make_message("1")));
}
{ // --- same with guards ---
auto m0 = on(1) >> [](int i) { CAF_CHECK_EQUAL(i, 1); };
CAF_CHECK_VARIANT_UNIT(m0, make_message(1));
// check lifted functor
auto m1 = on(1.f) >> [](float) {};
CAF_CHECK_VARIANT_UNIT(m1, make_message(1.f));
// check _.or_else(_)
auto m2 = m0.or_else(m1);
CAF_CHECK_VARIANT_UNIT(m2, make_message(1));
CAF_CHECK_VARIANT_UNIT(m2, make_message(1.f));
// check use of match_expr_concat
auto m3 = detail::match_expr_concat(m0, m1, on(1.) >> [](double) {});
CAF_CHECK_OPT_MSG_VOID(m3->invoke(make_message(1)));
CAF_CHECK_OPT_MSG_NONE(m3->invoke(make_message(2)));
CAF_CHECK_OPT_MSG_VOID(m3->invoke(make_message(1.f)));
CAF_CHECK_OPT_MSG_NONE(m3->invoke(make_message(2.f)));
CAF_CHECK_OPT_MSG_VOID(m3->invoke(make_message(1.)));
CAF_CHECK_OPT_MSG_NONE(m3->invoke(make_message(2.)));
CAF_CHECK_OPT_MSG_NONE(m3->invoke(make_message("1")));
}
{ // --- mixin it up with message_handler
// check on() usage
message_handler m0{on<int>() >> [](int) {}};
CAF_CHECK_OPT_MSG_VOID(m0(make_message(1)));
// check lifted functor
auto m1 = detail::lift_to_match_expr([](float) {});
CAF_CHECK_VARIANT_UNIT(m1, make_message(1.f));
// check _.or_else(_)
auto m2 = m0.or_else(m1);
CAF_CHECK_OPT_MSG_VOID(m2(make_message(1)));
CAF_CHECK_OPT_MSG_VOID(m2(make_message(1.f)));
// check use of match_expr_concat
auto m3 = detail::match_expr_concat(
m0, m1, detail::lift_to_match_expr([](double) {}));
CAF_CHECK_OPT_MSG_VOID(m3->invoke(make_message(1)));
CAF_CHECK_OPT_MSG_VOID(m3->invoke(make_message(1.)));
CAF_CHECK_OPT_MSG_VOID(m3->invoke(make_message(1.f)));
}
{ // --- use match_expr with result ---
auto m4 = on<int>() >> [](int i) { return i; };
CAF_CHECK_VARIANT(m4, make_message(42), int, 42);
auto m5 = on<float>() >> [](float f) { return f; };
CAF_CHECK_VARIANT(m5, make_message(4.2f), float, 4.2f);
auto m6 = m4.or_else(m5);
CAF_CHECK_VARIANT(m6, make_message(4.2f), float, 4.2f);
CAF_CHECK_VARIANT(m6, make_message(42), int, 42);
}
{ // --- storing some match_expr in a behavior ---
behavior m5{on(1) >> [] { return 2; }, on(1.f) >> [] { return 2.f; },
on(1.) >> [] { return 2.; }};
CAF_CHECK_OPT_MSG(m5, make_message(1), int, 2);
CAF_CHECK_OPT_MSG(m5, make_message(1.), double, 2.);
CAF_CHECK_OPT_MSG(m5, make_message(1.f), float, 2.f);
}
return CAF_TEST_RESULT();
}
......@@ -26,7 +26,7 @@ testee::~testee() {
behavior testee::make_behavior() {
return {
others() >> [=] {
others >> [=] {
CAF_CHECK_EQUAL(current_message().cvals()->get_reference_count(), 2);
quit();
return std::move(current_message());
......@@ -68,7 +68,7 @@ behavior tester::make_behavior() {
CAF_CHECK_EQUAL(current_message().cvals()->get_reference_count(), 1);
quit();
},
others() >> CAF_UNEXPECTED_MSG_CB(this)
others >> CAF_UNEXPECTED_MSG_CB(this)
};
}
......
......@@ -38,19 +38,14 @@ void test_or_else() {
CAF_PRINT("run_testee: handle_a.or_else(handle_b), on(\"c\") ...");
run_testee(
spawn([=] {
return (
handle_a.or_else(handle_b),
on("c") >> [] { return 3; }
);
return handle_a.or_else(handle_b).or_else(on("c") >> [] { return 3; });
})
);
CAF_PRINT("run_testee: on(\"a\") ..., handle_b.or_else(handle_c)");
run_testee(
spawn([=] {
return (
on("a") >> [] { return 1; },
handle_b.or_else(handle_c)
);
return message_handler{on("a") >> [] { return 1; }}.
or_else(handle_b).or_else(handle_c);
})
);
}
......
......@@ -27,7 +27,7 @@ using string_pair = std::pair<std::string, std::string>;
using actor_vector = vector<actor>;
void reflector(event_based_actor* self) {
self->become(others() >> [=] {
self->become(others >> [=] {
CAF_PRINT("reflect and quit");
self->quit();
return self->current_message();
......@@ -71,7 +71,7 @@ void spawn5_server_impl(event_based_actor* self, actor client, group grp) {
self->quit();
}
},
others() >> [=] {
others >> [=] {
CAF_UNEXPECTED_MSG(self);
self->quit(exit_reason::user_defined);
},
......@@ -91,7 +91,7 @@ void spawn5_server_impl(event_based_actor* self, actor client, group grp) {
}
);
},
others() >> [=] {
others >> [=] {
CAF_UNEXPECTED_MSG(self);
self->quit(exit_reason::user_defined);
},
......@@ -423,7 +423,7 @@ int main(int argc, char** argv) {
on() >> [&] {
test_remote_actor(argv[0], true);
},
others() >> [&] {
others >> [&] {
CAF_PRINTERR("usage: " << argv[0] << " [-s PORT|-c PORT1 PORT2 GROUP_PORT]");
}
});
......
......@@ -10,7 +10,7 @@ using namespace caf;
void test_serial_reply() {
auto mirror_behavior = [=](event_based_actor* self) {
self->become(others() >> [=]() -> message {
self->become(others >> [=]() -> message {
CAF_PRINT("return self->current_message()");
return self->current_message();
});
......@@ -63,7 +63,7 @@ void test_serial_reply() {
on(atom("hiho")) >> [] {
CAF_CHECKPOINT();
},
others() >> CAF_UNEXPECTED_MSG_CB_REF(self)
others >> CAF_UNEXPECTED_MSG_CB_REF(self)
);
self->send_exit(master, exit_reason::user_shutdown);
}
......
......@@ -7,7 +7,7 @@ using namespace caf;
void test_simple_reply_response() {
auto s = spawn([](event_based_actor* self) -> behavior {
return (
others() >> [=]() -> message {
others >> [=]() -> message {
CAF_CHECK(self->current_message() == make_message(ok_atom::value));
self->quit();
return self->current_message();
......@@ -17,7 +17,7 @@ void test_simple_reply_response() {
scoped_actor self;
self->send(s, ok_atom::value);
self->receive(
others() >> [&] {
others >> [&] {
CAF_CHECK(self->current_message() == make_message(ok_atom::value));
}
);
......
......@@ -106,7 +106,7 @@ actor spawn_event_testee2(actor parent) {
struct chopstick : public sb_actor<chopstick> {
behavior taken_by(actor whom) {
return (
return {
on<atom("take")>() >> [=] {
return atom("busy");
},
......@@ -116,7 +116,7 @@ struct chopstick : public sb_actor<chopstick> {
on(atom("break")) >> [=] {
quit();
}
);
};
}
behavior available;
......@@ -271,7 +271,7 @@ echo_actor::~echo_actor() {
behavior echo_actor::make_behavior() {
return {
others() >> [=]() -> message {
others >> [=]() -> message {
quit(exit_reason::normal);
return current_message();
}
......@@ -295,7 +295,7 @@ simple_mirror::~simple_mirror() {
behavior simple_mirror::make_behavior() {
return {
others() >> [=] {
others >> [=] {
CAF_CHECKPOINT();
return current_message();
}
......@@ -306,7 +306,7 @@ behavior high_priority_testee(event_based_actor* self) {
self->send(self, atom("b"));
self->send(message_priority::high, self, atom("a"));
// 'a' must be self->received before 'b'
return (
return {
on(atom("b")) >> [=] {
CAF_FAILURE("received 'b' before 'a'");
self->quit();
......@@ -318,11 +318,11 @@ behavior high_priority_testee(event_based_actor* self) {
CAF_CHECKPOINT();
self->quit();
},
others() >> CAF_UNEXPECTED_MSG_CB(self)
others >> CAF_UNEXPECTED_MSG_CB(self)
);
},
others() >> CAF_UNEXPECTED_MSG_CB(self)
);
others >> CAF_UNEXPECTED_MSG_CB(self)
};
}
struct high_priority_testee_class : event_based_actor {
......@@ -349,13 +349,13 @@ struct slave : event_based_actor {
behavior make_behavior() override {
link_to(master);
trap_exit(true);
return (
return {
[=](const exit_msg& msg) {
CAF_PRINT("slave: received exit message");
quit(msg.reason);
},
others() >> CAF_UNEXPECTED_MSG_CB(this)
);
others >> CAF_UNEXPECTED_MSG_CB(this)
};
}
actor master;
......@@ -382,7 +382,7 @@ void test_spawn() {
CAF_PRINT("test self->receive with zero timeout");
self->receive (
others() >> CAF_UNEXPECTED_MSG_CB_REF(self),
others >> CAF_UNEXPECTED_MSG_CB_REF(self),
after(chrono::seconds(0)) >> [] { /* mailbox empty */ }
);
self->await_all_other_actors_done();
......@@ -393,7 +393,7 @@ void test_spawn() {
self->send(mirror, "hello mirror");
self->receive (
on("hello mirror") >> CAF_CHECKPOINT_CB(),
others() >> CAF_UNEXPECTED_MSG_CB_REF(self)
others >> CAF_UNEXPECTED_MSG_CB_REF(self)
);
self->send_exit(mirror, exit_reason::user_shutdown);
self->receive (
......@@ -403,7 +403,7 @@ void test_spawn() {
}
else { CAF_UNEXPECTED_MSG_CB_REF(self); }
},
others() >> CAF_UNEXPECTED_MSG_CB_REF(self)
others >> CAF_UNEXPECTED_MSG_CB_REF(self)
);
self->await_all_other_actors_done();
CAF_CHECKPOINT();
......@@ -414,7 +414,7 @@ void test_spawn() {
self->send(mirror, "hello mirror");
self->receive (
on("hello mirror") >> CAF_CHECKPOINT_CB(),
others() >> CAF_UNEXPECTED_MSG_CB_REF(self)
others >> CAF_UNEXPECTED_MSG_CB_REF(self)
);
self->send_exit(mirror, exit_reason::user_shutdown);
self->receive (
......@@ -424,7 +424,7 @@ void test_spawn() {
}
else { CAF_UNEXPECTED_MSG(self); }
},
others() >> CAF_UNEXPECTED_MSG_CB_REF(self)
others >> CAF_UNEXPECTED_MSG_CB_REF(self)
);
self->await_all_other_actors_done();
CAF_CHECKPOINT();
......@@ -436,7 +436,7 @@ void test_spawn() {
self->send(mirror, "hello mirror");
self->receive (
on("hello mirror") >> CAF_CHECKPOINT_CB(),
others() >> CAF_UNEXPECTED_MSG_CB_REF(self)
others >> CAF_UNEXPECTED_MSG_CB_REF(self)
);
self->send_exit(mirror, exit_reason::user_shutdown);
self->receive (
......@@ -446,7 +446,7 @@ void test_spawn() {
}
else { CAF_UNEXPECTED_MSG(self); }
},
others() >> CAF_UNEXPECTED_MSG_CB_REF(self)
others >> CAF_UNEXPECTED_MSG_CB_REF(self)
);
self->await_all_other_actors_done();
CAF_CHECKPOINT();
......@@ -457,7 +457,7 @@ void test_spawn() {
self->send(mecho, "hello echo");
self->receive (
on("hello echo") >> [] { },
others() >> CAF_UNEXPECTED_MSG_CB_REF(self)
others >> CAF_UNEXPECTED_MSG_CB_REF(self)
);
self->await_all_other_actors_done();
CAF_CHECKPOINT();
......@@ -488,7 +488,7 @@ void test_spawn() {
self->send(cstk, atom("put"), self);
self->send(cstk, atom("break"));
},
others() >> CAF_UNEXPECTED_MSG_CB_REF(self)
others >> CAF_UNEXPECTED_MSG_CB_REF(self)
);
self->await_all_other_actors_done();
CAF_CHECKPOINT();
......@@ -509,7 +509,7 @@ void test_spawn() {
}
);
},
others() >> CAF_UNEXPECTED_MSG_CB_REF(s)
others >> CAF_UNEXPECTED_MSG_CB_REF(s)
);
});
self->monitor(sync_testee);
......@@ -537,7 +537,7 @@ void test_spawn() {
self->sync_send(sync_testee, "!?").await(
on<sync_exited_msg>() >> CAF_CHECKPOINT_CB(),
others() >> CAF_UNEXPECTED_MSG_CB_REF(self),
others >> CAF_UNEXPECTED_MSG_CB_REF(self),
after(chrono::milliseconds(1)) >> CAF_UNEXPECTED_TOUT_CB()
);
......@@ -572,7 +572,7 @@ void test_spawn() {
self->send(bob, 1, "hello actor");
self->receive (
on(4, "hello actor from Bob from Joe") >> CAF_CHECKPOINT_CB(),
others() >> CAF_UNEXPECTED_MSG_CB_REF(self)
others >> CAF_UNEXPECTED_MSG_CB_REF(self)
);
// kill joe and bob
auto poison_pill = make_message(atom("done"));
......@@ -595,7 +595,7 @@ void test_spawn() {
m_pal = spawn<kr34t0r>("Bob", this);
}
return {
others() >> [=] {
others >> [=] {
// forward message and die
send(m_pal, current_message());
quit();
......@@ -661,7 +661,7 @@ void test_spawn() {
}
behavior make_behavior() override {
return {
others() >> CAF_UNEXPECTED_MSG_CB(this)
others >> CAF_UNEXPECTED_MSG_CB(this)
};
}
};
......@@ -697,7 +697,7 @@ void test_spawn() {
CAF_CHECK(val == atom("FooBar"));
flags |= 0x08;
},
others() >> [&]() {
others >> [&]() {
CAF_FAILURE("unexpected message: " << to_string(self->current_message()));
},
after(chrono::milliseconds(500)) >> [&]() {
......@@ -808,7 +808,7 @@ void test_constructor_attach() {
quit(reason);
}
},
others() >> [=] {
others >> [=] {
forward_to(m_testee);
}
};
......@@ -829,7 +829,7 @@ class exception_testee : public event_based_actor {
}
behavior make_behavior() override {
return {
others() >> [] {
others >> [] {
throw std::runtime_error("whatever");
}
};
......@@ -929,7 +929,7 @@ int main() {
// test setting exit reasons for scoped actors
{ // lifetime scope of self
scoped_actor self;
self->spawn<linked>([]() -> behavior { return others() >> [] {}; });
self->spawn<linked>([]() -> behavior { return others >> [] {}; });
self->planned_exit_reason(exit_reason::user_defined);
}
await_all_actors_done();
......
......@@ -25,7 +25,7 @@ using hi_there_atom = atom_constant<atom("HiThere")>;
struct sync_mirror : event_based_actor {
behavior make_behavior() override {
return {
others() >> [=] {
others >> [=] {
return current_message();
}
};
......@@ -97,7 +97,7 @@ class A : public popular_actor {
}
);
},
others() >> [=] {
others >> [=] {
report_failure();
}
};
......@@ -112,7 +112,7 @@ class B : public popular_actor {
behavior make_behavior() override {
return {
others() >> [=] {
others >> [=] {
CAF_CHECKPOINT();
forward_to(buddy());
quit();
......@@ -157,9 +157,9 @@ class D : public popular_actor {
behavior make_behavior() override {
return {
others() >> [=] {
others >> [=] {
return sync_send(buddy(), std::move(current_message())).then(
others() >> [=]() -> message {
others >> [=]() -> message {
quit();
return std::move(current_message());
}
......@@ -199,11 +199,11 @@ class server : public event_based_actor {
unbecome(); // await next idle message
},
on(idle_atom::value) >> skip_message,
others() >> die
others >> die
);
},
on(request_atom::value) >> skip_message,
others() >> die
others >> die
};
}
};
......@@ -263,7 +263,7 @@ void test_sync_send() {
[&](const down_msg& dm) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::user_shutdown);
},
others() >> CAF_UNEXPECTED_MSG_CB_REF(self)
others >> CAF_UNEXPECTED_MSG_CB_REF(self)
);
auto mirror = spawn<sync_mirror>();
bool continuation_called = false;
......@@ -307,7 +307,7 @@ void test_sync_send() {
CAF_CHECKPOINT();
self->timed_sync_send(self, milliseconds(50), no_way_atom::value).await(
on<sync_timeout_msg>() >> CAF_CHECKPOINT_CB(),
others() >> CAF_UNEXPECTED_MSG_CB_REF(self)
others >> CAF_UNEXPECTED_MSG_CB_REF(self)
);
// we should have received two DOWN messages with normal exit reason
// plus 'NoWay'
......@@ -321,13 +321,13 @@ void test_sync_send() {
CAF_PRINT("trigger \"actor did not reply to a "
"synchronous request message\"");
},
others() >> CAF_UNEXPECTED_MSG_CB_REF(self),
others >> CAF_UNEXPECTED_MSG_CB_REF(self),
after(milliseconds(0)) >> CAF_UNEXPECTED_TOUT_CB()
);
CAF_CHECKPOINT();
// mailbox should be empty now
self->receive(
others() >> CAF_UNEXPECTED_MSG_CB_REF(self),
others >> CAF_UNEXPECTED_MSG_CB_REF(self),
after(milliseconds(0)) >> CAF_CHECKPOINT_CB()
);
// check wheter continuations are invoked correctly
......@@ -371,7 +371,7 @@ void test_sync_send() {
CAF_CHECKPOINT();
CAF_CHECK_EQUAL(s->current_sender(), work);
},
others() >> [&] {
others >> [&] {
CAF_PRINTERR("unexpected message: " << to_string(s->current_message()));
}
);
......@@ -383,19 +383,19 @@ void test_sync_send() {
CAF_CHECKPOINT();
CAF_CHECK_EQUAL(s->current_sender(), work);
},
others() >> CAF_UNEXPECTED_MSG_CB(s)
others >> CAF_UNEXPECTED_MSG_CB(s)
);
s->send(s, "Ever danced with the devil in the pale moonlight?");
// response: {'EXIT', exit_reason::user_shutdown}
s->receive_loop(
others() >> CAF_UNEXPECTED_MSG_CB(s)
others >> CAF_UNEXPECTED_MSG_CB(s)
);
});
self->receive(
[&](const down_msg& dm) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::user_shutdown);
},
others() >> CAF_UNEXPECTED_MSG_CB_REF(self)
others >> CAF_UNEXPECTED_MSG_CB_REF(self)
);
}
......
......@@ -38,7 +38,7 @@ class dummy : public event_based_actor {
}
behavior make_behavior() override {
return {
others() >> CAF_UNEXPECTED_MSG_CB(this)
others >> CAF_UNEXPECTED_MSG_CB(this)
};
}
};
......
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