Commit 2ced36ba authored by Dominik Charousset's avatar Dominik Charousset

Simplify pattern matching implementation

Remove template-based pattern matching implementation.  The removed code is too
complex and as a result too hard to maintain in the long run. Also, the main
use case for wildcards is `others()`. Other use cases seem rare (and often
unlikely) and thus do not justify the optimizations in the first place..
parent 9abec963
...@@ -26,166 +26,38 @@ ...@@ -26,166 +26,38 @@
#include "caf/wildcard_position.hpp" #include "caf/wildcard_position.hpp"
#include "caf/detail/type_list.hpp" #include "caf/detail/type_list.hpp"
#include "caf/detail/pseudo_tuple.hpp"
namespace caf { namespace caf {
namespace detail { namespace detail {
template <wildcard_position, class Tuple, class...> template <class Pattern, class FilteredPattern>
struct matcher; struct matcher;
template <class Tuple, class... T> template <class... Ts, class... Us>
struct matcher<wildcard_position::nil, Tuple, T...> { struct matcher<type_list<Ts...>, type_list<Us...>> {
bool operator()(const Tuple& tup) const {
if (not tup.dynamically_typed()) {
// statically typed tuples return &typeid(type_list<T...>)
// as type token
return typeid(detail::type_list<T...>)== *(tup.type_token());
}
// always use a full dynamic match for dynamic typed tuples
else if (tup.size() == sizeof...(T)) {
auto& tarr = static_types_array<T...>::arr;
return std::equal(tup.begin(), tup.end(), tarr.begin(),
types_only_eq);
}
return false;
}
bool operator()(const Tuple& tup, std::vector<size_t>& mv) const {
if ((*this)(tup)) {
mv.resize(sizeof...(T));
std::iota(mv.begin(), mv.end(), 0);
return true;
}
return false;
}
};
template <class Tuple, class... T>
struct matcher<wildcard_position::trailing, Tuple, T...> {
static constexpr size_t size = sizeof...(T) - 1;
bool operator()(const Tuple& tup) const {
if (tup.size() >= size) {
auto& tarr = static_types_array<T...>::arr;
auto begin = tup.begin();
return std::equal(begin, begin + size, tarr.begin(), types_only_eq);
}
return false;
}
bool operator()(const Tuple& tup, std::vector<size_t>& mv) const {
if ((*this)(tup)) {
mv.resize(size);
std::iota(mv.begin(), mv.end(), 0);
return true;
}
return false;
}
};
template <class Tuple>
struct matcher<wildcard_position::leading, Tuple, anything> {
bool operator()(const Tuple&) const { return true; }
bool operator()(const Tuple&, std::vector<size_t>&) const { return true; }
};
template <class Tuple, class... T>
struct matcher<wildcard_position::leading, Tuple, T...> {
static constexpr size_t size = sizeof...(T) - 1;
bool operator()(const Tuple& tup) const {
auto tup_size = tup.size();
if (tup_size >= size) {
auto& tarr = static_types_array<T...>::arr;
auto begin = tup.begin();
begin += (tup_size - size);
return std::equal(begin, tup.end(),
(tarr.begin() + 1), // skip 'anything'
types_only_eq);
}
return false;
}
bool operator()(const Tuple& tup, std::vector<size_t>& mv) const {
if ((*this)(tup)) {
mv.resize(size);
std::iota(mv.begin(), mv.end(), tup.size() - size);
return true;
}
return false;
}
};
template <class Tuple, class... T>
struct matcher<wildcard_position::in_between, Tuple, T...> {
static constexpr int signed_wc_pos =
detail::tl_find<detail::type_list<T...>, anything>::value;
static constexpr size_t size = sizeof...(T);
static constexpr size_t wc_pos = static_cast<size_t>(signed_wc_pos);
static_assert(signed_wc_pos > 0 && wc_pos < (sizeof...(T) - 1),
"illegal wildcard position");
bool operator()(const Tuple& tup) const {
auto tup_size = tup.size();
if (tup_size >= (size - 1)) {
auto& tarr = static_types_array<T...>::arr;
// first range [0, X1)
auto begin = tup.begin();
auto end = begin + wc_pos;
if (std::equal(begin, end, tarr.begin(), types_only_eq)) {
// second range [X2, N)
begin = end = tup.end();
begin -= (size - (wc_pos + 1));
auto arr_begin = tarr.begin() + (wc_pos + 1);
return std::equal(begin, end, arr_begin, types_only_eq);
}
}
return false;
}
bool operator()(const Tuple& tup, std::vector<size_t>& mv) const {
if ((*this)(tup)) {
// first range
mv.resize(size - 1);
auto begin = mv.begin();
std::iota(begin, begin + wc_pos, 0);
// second range
begin = mv.begin() + wc_pos;
std::iota(begin, mv.end(), tup.size() - (size - (wc_pos + 1)));
return true;
}
return false;
}
};
template <class Tuple, class... T>
struct matcher<wildcard_position::multiple, Tuple, T...> {
static constexpr size_t wc_count =
detail::tl_count<detail::type_list<T...>, is_anything>::value;
static_assert(sizeof...(T) > wc_count, "only wildcards given");
template <class TupleIter, class PatternIter, class Push, class Commit, template <class TupleIter, class PatternIter, class Push, class Commit,
class Rollback> class Rollback>
bool operator()(TupleIter tbegin, TupleIter tend, PatternIter pbegin, bool operator()(TupleIter tbegin, TupleIter tend, PatternIter pbegin,
PatternIter pend, Push& push, Commit& commit, PatternIter pend, Push& push, Commit& commit,
Rollback& rollback) const { Rollback& rollback) const {
while (!(pbegin == pend && tbegin == tend)) { while (!(pbegin == pend && tbegin == tend)) {
if (pbegin == pend) { if (pbegin == pend) {
// reached end of pattern while some values remain unmatched // reached end of pattern while some values remain unmatched
return false; return false;
} else if (*pbegin == nullptr) { // nullptr == wildcard (anything) }
if (*pbegin == nullptr) { // nullptr == wildcard (anything)
// perform submatching // perform submatching
++pbegin; ++pbegin;
// always true at the end of the pattern // always true at the end of the pattern
if (pbegin == pend) return true; if (pbegin == pend) {
return true;
}
// safe current mapping as fallback // safe current mapping as fallback
commit(); commit();
// iterate over tuple values until we found a match // iterate over tuple values until we found a match
for (; tbegin != tend; ++tbegin) { for (; tbegin != tend; ++tbegin) {
if (match(tbegin, tend, pbegin, pend, push, commit, if ((*this)(tbegin, tend, pbegin, pend, push, commit, rollback)) {
rollback)) {
return true; return true;
} }
// restore mapping to fallback (delete invalid mappings) // restore mapping to fallback (delete invalid mappings)
...@@ -194,56 +66,43 @@ struct matcher<wildcard_position::multiple, Tuple, T...> { ...@@ -194,56 +66,43 @@ struct matcher<wildcard_position::multiple, Tuple, T...> {
return false; // no submatch found return false; // no submatch found
} }
// compare types // compare types
else if (tbegin.type() == *pbegin) if (tbegin.type() != *pbegin) {
push(tbegin); // type mismatch
// no match
else
return false; return false;
}
// next iteration // next iteration
push(tbegin);
++tbegin; ++tbegin;
++pbegin; ++pbegin;
} }
return true; // pbegin == pend && tbegin == tend return true; // pbegin == pend && tbegin == tend
} }
bool operator()(const Tuple& tup) const { bool operator()(const message& tup, pseudo_tuple<Us...>* out) const {
auto& tarr = static_types_array<T...>::arr; auto& tarr = static_types_array<Ts...>::arr;
if (tup.size() >= (sizeof...(T) - wc_count)) { if (sizeof...(Us) == 0) {
auto fpush = [](const typename Tuple::const_iterator&) {}; // this pattern only has wildcards and thus always matches
auto fcommit = [] {}; return true;
auto frollback = [] {};
return match(tup.begin(), tup.end(), tarr.begin(), tarr.end(),
fpush, fcommit, frollback);
} }
return false; if (tup.size() < sizeof...(Us)) {
} return false;
}
template <class MappingVector> if (out) {
bool operator()(const Tuple& tup, MappingVector& mv) const { size_t pos = 0;
auto& tarr = static_types_array<T...>::arr; size_t fallback_pos = 0;
if (tup.size() >= (sizeof...(T) - wc_count)) { auto fpush = [&](const typename message::const_iterator& iter) {
size_t commited_size = 0; (*out)[pos++] = const_cast<void*>(iter.value());
auto fpush = [&](const typename Tuple::const_iterator& iter) {
mv.push_back(iter.position());
}; };
auto fcommit = [&] { commited_size = mv.size(); }; auto fcommit = [&] { fallback_pos = pos; };
auto frollback = [&] { mv.resize(commited_size); }; auto frollback = [&] { pos = fallback_pos; };
return match(tup.begin(), tup.end(), tarr.begin(), tarr.end(), return (*this)(tup.begin(), tup.end(), tarr.begin(), tarr.end(), fpush,
fpush, fcommit, frollback); fcommit, frollback);
} }
return false; auto no_push = [](const typename message::const_iterator&) { };
auto nop = [] { };
return (*this)(tup.begin(), tup.end(), tarr.begin(), tarr.end(), no_push,
nop, nop);
} }
};
template <class Tuple, class List>
struct select_matcher;
template <class Tuple, class... Ts>
struct select_matcher<Tuple, detail::type_list<Ts...>> {
using type = matcher<get_wildcard_position<detail::type_list<Ts...>>(),
Tuple, Ts...>;
}; };
} // namespace detail } // namespace detail
......
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
#include "caf/detail/type_traits.hpp" #include "caf/detail/type_traits.hpp"
#include "caf/detail/left_or_right.hpp" #include "caf/detail/left_or_right.hpp"
#include "caf/detail/matches.hpp" #include "caf/detail/matcher.hpp"
#include "caf/detail/type_list.hpp" #include "caf/detail/type_list.hpp"
#include "caf/detail/lifted_fun.hpp" #include "caf/detail/lifted_fun.hpp"
#include "caf/detail/pseudo_tuple.hpp" #include "caf/detail/pseudo_tuple.hpp"
...@@ -60,262 +60,6 @@ const T2& deduce_const(const T1&, T2& rhs) { ...@@ -60,262 +60,6 @@ const T2& deduce_const(const T1&, T2& rhs) {
return rhs; return rhs;
} }
template <class Filtered>
struct invoke_util_base {
using tuple_type = typename detail::tl_apply<Filtered, pseudo_tuple>::type;
};
// covers wildcard_position::multiple and wildcard_position::in_between
template <wildcard_position, class Pattern, class FilteredPattern>
struct invoke_util_impl : invoke_util_base<FilteredPattern> {
using super = invoke_util_base<FilteredPattern>;
template <class Tuple>
static bool can_invoke(const std::type_info& type_token, const Tuple& tup) {
typename select_matcher<Tuple, Pattern>::type mimpl;
return type_token == typeid(FilteredPattern) || mimpl(tup);
}
template <class PtrType, class Tuple>
static bool prepare_invoke(typename super::tuple_type& result,
const std::type_info& type_token, bool, PtrType*,
Tuple& tup) {
using mimpl =
typename select_matcher<
typename std::remove_const<Tuple>::type,
Pattern
>::type;
std::vector<size_t> mv;
if (type_token == typeid(FilteredPattern)) {
for (size_t i = 0; i < detail::tl_size<FilteredPattern>::value; ++i) {
result[i] = const_cast<void*>(tup.at(i));
}
return true;
} else if (mimpl(tup, mv)) {
for (size_t i = 0; i < detail::tl_size<FilteredPattern>::value; ++i) {
result[i] = const_cast<void*>(tup.at(mv[i]));
}
return true;
}
return false;
}
};
template <>
struct invoke_util_impl<wildcard_position::nil, detail::empty_type_list,
detail::empty_type_list>
: invoke_util_base<detail::empty_type_list> {
using super = invoke_util_base<detail::empty_type_list>;
template <class PtrType, class Tuple>
static bool prepare_invoke(typename super::tuple_type&,
const std::type_info& type_token, bool, PtrType*,
Tuple& tup) {
return can_invoke(type_token, tup);
}
template <class Tuple>
static bool can_invoke(const std::type_info& arg_types, const Tuple&) {
return arg_types == typeid(detail::empty_type_list);
}
};
template <class Pattern, class... Ts>
struct invoke_util_impl<wildcard_position::nil, Pattern,
detail::type_list<Ts...>>
: invoke_util_base<detail::type_list<Ts...>> {
using super = invoke_util_base<detail::type_list<Ts...>>;
using tuple_type = typename super::tuple_type;
using native_data_type = std::tuple<Ts...>;
using arr_type = typename detail::static_types_array<Ts...>;
template <class Tup>
static bool prepare_invoke(std::false_type, tuple_type&, Tup&) {
return false;
}
template <class Tup>
static bool prepare_invoke(std::true_type, tuple_type& result, Tup& tup) {
for (size_t i = 0; i < sizeof...(Ts); ++i) {
result[i] = const_cast<void*>(tup.at(i));
}
return true;
}
template <class PtrType, class Tuple>
static bool prepare_invoke(tuple_type& result, const std::type_info&,
bool, PtrType*, Tuple& tup,
typename std::enable_if<
!std::is_same<
typename std::remove_const<Tuple>::type,
detail::message_data
>::value
>::type* = 0) {
std::integral_constant<
bool, detail::tl_binary_forall<
typename detail::tl_map<typename Tuple::types,
detail::purge_refs>::type,
detail::type_list<Ts...>, std::is_same>::value> token;
return prepare_invoke(token, result, tup);
}
template <class PtrType, class Tuple>
static bool prepare_invoke(typename super::tuple_type& res,
const std::type_info& arg_types,
bool dynamically_typed, PtrType* native_arg,
Tuple& tup,
typename std::enable_if<
std::is_same<
typename std::remove_const<Tuple>::type,
detail::message_data
>::value
>::type* = 0) {
auto fill_from_tup = [&] {
for (size_t i = 0; i < sizeof...(Ts); ++i) {
res[i] = const_cast<void*>(tup.at(i));
}
};
if (arg_types == typeid(detail::type_list<Ts...>)) {
if (native_arg == nullptr) {
fill_from_tup();
return true;
}
using cast_type =
typename std::conditional<
std::is_const<PtrType>::value,
const native_data_type*,
native_data_type*
>::type;
auto p = reinterpret_cast<cast_type>(native_arg);
for (size_t i = 0; i < sizeof...(Ts); ++i) {
res[i] = const_cast<void*>(tup_ptr_access<0, sizeof...(Ts)>::get(i, *p));
}
return true;
} else if (dynamically_typed) {
auto& arr = arr_type::arr;
if (tup.size() != sizeof...(Ts)) {
return false;
}
for (size_t i = 0; i < sizeof...(Ts); ++i) {
if (arr[i] != tup.type_at(i)) {
return false;
}
}
fill_from_tup();
return true;
}
return false;
}
template <class Tuple>
static bool can_invoke(const std::type_info& arg_types, const Tuple&) {
return arg_types == typeid(detail::type_list<Ts...>);
}
};
template <>
struct invoke_util_impl<wildcard_position::leading, detail::type_list<anything>,
detail::empty_type_list>
: invoke_util_base<detail::empty_type_list> {
using super = invoke_util_base<detail::empty_type_list>;
template <class Tuple>
static bool can_invoke(const std::type_info&, const Tuple&) {
return true;
}
template <class PtrType, typename Tuple>
static bool prepare_invoke(typename super::tuple_type&, const std::type_info&,
bool, PtrType*, Tuple&) {
return true;
}
};
template <class Pattern, class... Ts>
struct invoke_util_impl<wildcard_position::trailing, Pattern,
detail::type_list<Ts...>>
: invoke_util_base<detail::type_list<Ts...>> {
using super = invoke_util_base<detail::type_list<Ts...>>;
template <class Tuple>
static bool can_invoke(const std::type_info& arg_types, const Tuple& tup) {
if (arg_types == typeid(detail::type_list<Ts...>)) {
return true;
}
using arr_type = detail::static_types_array<Ts...>;
auto& arr = arr_type::arr;
if (tup.size() < sizeof...(Ts)) {
return false;
}
for (size_t i = 0; i < sizeof...(Ts); ++i) {
if (arr[i] != tup.type_at(i)) {
return false;
}
}
return true;
}
template <class PtrType, class Tuple>
static bool prepare_invoke(typename super::tuple_type& result,
const std::type_info& arg_types, bool, PtrType*,
Tuple& tup) {
if (!can_invoke(arg_types, tup)) return false;
for (size_t i = 0; i < sizeof...(Ts); ++i) {
result[i] = const_cast<void*>(tup.at(i));
}
return true;
}
};
template <class Pattern, class... Ts>
struct invoke_util_impl<wildcard_position::leading, Pattern,
detail::type_list<Ts...>>
: invoke_util_base<detail::type_list<Ts...>> {
using super = invoke_util_base<detail::type_list<Ts...>>;
template <class Tuple>
static bool can_invoke(const std::type_info& arg_types, const Tuple& tup) {
if (arg_types == typeid(detail::type_list<Ts...>)) {
return true;
}
using arr_type = detail::static_types_array<Ts...>;
auto& arr = arr_type::arr;
if (tup.size() < sizeof...(Ts)) return false;
size_t i = tup.size() - sizeof...(Ts);
size_t j = 0;
while (j < sizeof...(Ts)) {
if (arr[j++] != tup.type_at(i++)) {
return false;
}
}
return true;
}
template <class PtrType, class Tuple>
static bool prepare_invoke(typename super::tuple_type& result,
const std::type_info& arg_types, bool, PtrType*,
Tuple& tup) {
if (!can_invoke(arg_types, tup)) return false;
size_t i = tup.size() - sizeof...(Ts);
size_t j = 0;
while (j < sizeof...(Ts)) {
result[j++] = const_cast<void*>(tup.at(i++));
}
return true;
}
};
template <class Pattern>
struct invoke_util : invoke_util_impl<get_wildcard_position<Pattern>(), Pattern,
typename detail::tl_filter_not_type<
Pattern,
anything>::type
> {
};
template <class Expr, class Projecs, class Signature, class Pattern> template <class Expr, class Projecs, class Signature, class Pattern>
class match_expr_case : public get_lifted_fun<Expr, Projecs, Signature>::type { class match_expr_case : public get_lifted_fun<Expr, Projecs, Signature>::type {
public: public:
...@@ -324,7 +68,17 @@ class match_expr_case : public get_lifted_fun<Expr, Projecs, Signature>::type { ...@@ -324,7 +68,17 @@ class match_expr_case : public get_lifted_fun<Expr, Projecs, Signature>::type {
match_expr_case(Ts&&... args) : super(std::forward<Ts>(args)...) { match_expr_case(Ts&&... args) : super(std::forward<Ts>(args)...) {
// nop // nop
} }
using pattern_type = Pattern; 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;
}; };
template <class Expr, class Transformers, class Pattern> template <class Expr, class Transformers, class Pattern>
...@@ -453,33 +207,32 @@ T& unroll_expr_result_unbox(optional<T>& opt) { ...@@ -453,33 +207,32 @@ T& unroll_expr_result_unbox(optional<T>& opt) {
return *opt; return *opt;
} }
template <class Result, class PPFPs, typename PtrType, class Tuple> template <class Result, class PPFPs, class Msg>
Result unroll_expr(PPFPs&, uint64_t, minus1l, const std::type_info&, bool, Result unroll_expr(PPFPs&, uint64_t, minus1l, Msg&) {
PtrType*, Tuple&) { // end of recursion
return none; return none;
} }
template <class Result, class PPFPs, long N, typename PtrType, class Tuple> template <class Result, class PPFPs, long N, class Msg>
Result unroll_expr(PPFPs& fs, uint64_t bitmask, long_constant<N>, Result unroll_expr(PPFPs& fs, uint64_t bitmask, long_constant<N>, Msg& msg) {
const std::type_info& type_token, bool is_dynamic, { // recursively evaluate sub expressions
PtrType* ptr, Tuple& tup) { Result res = unroll_expr<Result>(fs, bitmask, long_constant<N - 1>{}, msg);
/* recursively evaluate sub expressions */ {
Result res = unroll_expr<Result>(fs, bitmask, long_constant<N - 1>{},
type_token, is_dynamic, ptr, tup);
if (!get<none_t>(&res)) { if (!get<none_t>(&res)) {
return res; return res;
} }
} }
if ((bitmask & (0x01 << N)) == 0) return none; if ((bitmask & (0x01 << N)) == 0) {
// case is disabled via bitmask
return none;
}
auto& f = get<N>(fs); auto& f = get<N>(fs);
using Fun = typename std::decay<decltype(f)>::type; using ft = typename std::decay<decltype(f)>::type;
using pattern_type = typename Fun::pattern_type; detail::matcher<typename ft::pattern, typename ft::filtered_pattern> match;
//using policy = detail::invoke_util<pattern_type>; typename ft::intermediate_tuple targs;
typedef detail::invoke_util<pattern_type> policy; // using fails on GCC 4.7 if (match(msg, &targs)) {
typename policy::tuple_type targs; //if (policy::prepare_invoke(targs, type_token, is_dynamic, ptr, tup)) {
if (policy::prepare_invoke(targs, type_token, is_dynamic, ptr, tup)) {
auto is = detail::get_indices(targs); auto is = detail::get_indices(targs);
auto res = detail::apply_args(f, is, deduce_const(tup, targs)); auto res = detail::apply_args(f, is, deduce_const(msg, targs));
if (unroll_expr_result_valid(res)) { if (unroll_expr_result_valid(res)) {
return std::move(unroll_expr_result_unbox(res)); return std::move(unroll_expr_result_unbox(res));
} }
...@@ -496,10 +249,9 @@ template <class Case, long N, class Tuple> ...@@ -496,10 +249,9 @@ template <class Case, long N, class Tuple>
uint64_t calc_bitmask(Case& fs, long_constant<N>, uint64_t calc_bitmask(Case& fs, long_constant<N>,
const std::type_info& tinf, const Tuple& tup) { const std::type_info& tinf, const Tuple& tup) {
auto& f = get<N>(fs); auto& f = get<N>(fs);
using Fun = typename std::decay<decltype(f)>::type; using ft = typename std::decay<decltype(f)>::type;
using pattern_type = typename Fun::pattern_type; detail::matcher<typename ft::pattern, typename ft::filtered_pattern> match;
using policy = detail::invoke_util<pattern_type>; uint64_t result = match(tup, nullptr) ? (0x01 << N) : 0x00;
uint64_t result = policy::can_invoke(tinf, tup) ? (0x01 << N) : 0x00;
return result | calc_bitmask(fs, long_constant<N - 1l>(), tinf, tup); return result | calc_bitmask(fs, long_constant<N - 1l>(), tinf, tup);
} }
...@@ -717,21 +469,15 @@ class match_expr { ...@@ -717,21 +469,15 @@ class match_expr {
m_cache_begin = m_cache_end = 0; m_cache_begin = m_cache_end = 0;
} }
template <class Tuple> template <class Msg>
result_type apply(Tuple& tup) { result_type apply(Msg& msg) {
idx_token_type idx_token; idx_token_type idx_token;
std::integral_constant<bool, has_manipulator> mutator_token; std::integral_constant<bool, has_manipulator> mutator_token;
// returns either a reference or a new object // returns either a reference or a new object
using detached = decltype(detail::detach_if_needed(tup, mutator_token)); using detached = decltype(detail::detach_if_needed(msg, mutator_token));
detached tref = detail::detach_if_needed(tup, mutator_token); detached mref = detail::detach_if_needed(msg, mutator_token);
auto& vals = tref.vals(); auto bitmask = get_cache_entry(mref.type_token(), mref);
auto ndp = detail::fetch_native_data(tref, mutator_token); return detail::unroll_expr<result_type>(m_cases, bitmask, idx_token, mref);
auto token_ptr = tref.type_token();
auto bitmask = get_cache_entry(token_ptr, tref);
auto dynamically_typed = tref.dynamically_typed();
return detail::unroll_expr<result_type>(m_cases, bitmask, idx_token,
*token_ptr, dynamically_typed,
ndp, *vals);
} }
}; };
......
...@@ -20,6 +20,7 @@ add_unit_test(variant) ...@@ -20,6 +20,7 @@ add_unit_test(variant)
add_unit_test(atom) add_unit_test(atom)
add_unit_test(metaprogramming) add_unit_test(metaprogramming)
add_unit_test(intrusive_containers) add_unit_test(intrusive_containers)
add_unit_test(match)
add_unit_test(serialization) add_unit_test(serialization)
add_unit_test(uniform_type) add_unit_test(uniform_type)
add_unit_test(fixed_vector) add_unit_test(fixed_vector)
......
...@@ -2,482 +2,144 @@ ...@@ -2,482 +2,144 @@
#include <functional> #include <functional>
#include "test.hpp" #include "test.hpp"
#include "cppa/cppa.hpp" #include "caf/all.hpp"
#include "cppa/on.hpp"
#include "cppa/match.hpp"
#include "cppa/announce.hpp"
#include "cppa/to_string.hpp"
using namespace caf;
using namespace std; using namespace std;
using namespace cppa;
using namespace std::placeholders;
using namespace cppa::placeholders;
bool is_even(int i) { return i % 2 == 0; } optional<int> even_int(int i) {
if (i % 2 == 0) {
return i;
}
return none;
}
bool vec_sorted(const std::vector<int>& vec) { optional<const vector<int>&> sorted_vec(const vector<int>& vec) {
return std::is_sorted(vec.begin(), vec.end()); if (is_sorted(vec.begin(), vec.end())) {
return vec;
}
return none;
} }
vector<string> kvp_split(const string& str) { function<optional<string>(const string&)> starts_with(const string& s) {
return [=](const string& str) -> optional<string> {
cout << "starts_with guard called" << endl;
if (str.size() > s.size() && str.compare(0, s.size(), s) == 0) {
auto res = str.substr(s.size());
return res;
}
return none;
};
}
optional<vector<string>> kvp_split(const string& str) {
auto pos = str.find('='); auto pos = str.find('=');
if (pos != string::npos && pos == str.rfind('=')) { if (pos != string::npos && pos == str.rfind('=')) {
return vector<string>{str.substr(0, pos), str.substr(pos+1)}; return vector<string>{str.substr(0, pos), str.substr(pos+1)};
} }
return vector<string>{}; return none;
} }
optional<int> toint(const string& str) { optional<int> toint(const string& str) {
char* endptr = nullptr; char* endptr = nullptr;
int result = static_cast<int>(strtol(str.c_str(), &endptr, 10)); int result = static_cast<int>(strtol(str.c_str(), &endptr, 10));
if (endptr != nullptr && *endptr == '\0') return result; if (endptr != nullptr && *endptr == '\0') {
return result;
}
return none; return none;
} }
template<size_t Pos, size_t Max> namespace {
struct index_token { };
template<size_t Max>
void foobar(index_token<Max, Max>) { }
template<size_t Pos, size_t Max>
void foobar(index_token<Pos, Max>) {
cout << "foobar<" << Pos << "," << Max << ">" << endl;
foobar(index_token<Pos+1, Max>{});
}
void test_simple_guards() {
auto ascending = [](int a, int b, int c) -> bool {
return a < b && b < c;
};
auto expr0_a = gcall(ascending, _x1, _x2, _x3);
CPPA_CHECK(ge_invoke(expr0_a, 1, 2, 3));
CPPA_CHECK(!ge_invoke(expr0_a, 3, 2, 1));
int ival0 = 2;
auto expr0_b = gcall(ascending, _x1, std::ref(ival0), _x2);
CPPA_CHECK(ge_invoke(expr0_b, 1, 3));
++ival0;
CPPA_CHECK(!ge_invoke(expr0_b, 1, 3));
auto expr0_c = gcall(ascending, 10, _x1, 30);
CPPA_CHECK(!ge_invoke(expr0_c, 10));
CPPA_CHECK(ge_invoke(expr0_c, 11));
// user-defined expressions using '+', '<', '%'
auto expr1 = _x1 + _x2;
auto expr2 = _x1 + _x2 < _x3;
auto expr3 = _x1 % _x2 == 0;
CPPA_CHECK_EQUAL((ge_invoke(expr1, 2, 3)), 5);
CPPA_CHECK(expr2(1, 2, 4));
CPPA_CHECK_EQUAL((ge_invoke(expr1, std::string{"1"}, std::string{"2"})), "12");
CPPA_CHECK(expr3(100, 2));
// user-defined expressions using '||'
auto expr4 = _x1 == "-h" || _x1 == "--help";
CPPA_CHECK(ge_invoke(expr4, string("-h")));
CPPA_CHECK(ge_invoke(expr4, string("--help")));
CPPA_CHECK(!ge_invoke(expr4, string("-g")));
}
void test_container_guards() { bool s_invoked[] = {false, false, false, false};
auto expr5 = _x1.starts_with("--");
CPPA_CHECK(ge_invoke(expr5, string("--help")));
CPPA_CHECK(!ge_invoke(expr5, string("-help")));
// container search (x in y)
vector<string> vec1{"hello", "world"};
auto expr6 = _x1.in(vec1);
CPPA_CHECK(ge_invoke(expr6, string("hello")));
CPPA_CHECK(ge_invoke(expr6, string("world")));
CPPA_CHECK(!ge_invoke(expr6, string("hello world")));
auto expr7 = _x1.in(std::ref(vec1));
CPPA_CHECK(ge_invoke(expr7, string("hello")));
CPPA_CHECK(ge_invoke(expr7, string("world")));
CPPA_CHECK(!ge_invoke(expr7, string("hello world")));
vec1.emplace_back("hello world");
CPPA_CHECK(!ge_invoke(expr6, string("hello world")));
CPPA_CHECK(ge_invoke(expr7, string("hello world")));
// container search (x in y) for initializer lists
auto expr11 = _x1.in({"one", "two"});
CPPA_CHECK(ge_invoke(expr11, string("one")));
CPPA_CHECK(ge_invoke(expr11, string("two")));
CPPA_CHECK(!ge_invoke(expr11, string("three")));
// container search (x not in y)
auto expr12 = _x1.not_in({"hello", "world"});
CPPA_CHECK(ge_invoke(expr12, string("foo")));
CPPA_CHECK(!ge_invoke(expr12, string("hello")));
// user-defined function to evaluate whether container is sorted
std::vector<int> expr21_vec_a{1, 2, 3};
std::vector<int> expr21_vec_b{1, 0, 2};
auto expr21 = gcall(vec_sorted, _x1);
CPPA_CHECK(ge_invoke(expr21, expr21_vec_a));
CPPA_CHECK(!ge_invoke(expr21, expr21_vec_b));
auto expr22 = _x1.empty() && _x2.not_empty();
CPPA_CHECK(ge_invoke(expr22, std::string(""), std::string("abc")));
}
void test_user_defined_expressions() { } // namespace <anonymous>
int ival = 5;
auto expr8 = _x1 == ival;
auto expr9 = _x1 == std::ref(ival);
CPPA_CHECK(ge_invoke(expr8, 5));
CPPA_CHECK(ge_invoke(expr9, 5));
ival = 10;
CPPA_CHECK(!ge_invoke(expr9, 5));
CPPA_CHECK(ge_invoke(expr9, 10));
// user-defined expressions w/ wide range of operators
auto expr13 = _x1 * _x2 < _x3 - _x4;
CPPA_CHECK(ge_invoke(expr13, 1, 1, 4, 2));
auto expr14 = _x1 + _x2;
static_assert(std::is_same<decltype(ge_invoke(expr14, 1, 2)), int>::value,
"wrong return type");
CPPA_CHECK_EQUAL((ge_invoke(expr14, 2, 3)), 5);
auto expr15 = _x1 + _x2 + _x3;
static_assert(std::is_same<decltype(ge_invoke(expr15, 1, 2, 3)), int>::value,
"wrong return type");
CPPA_CHECK_EQUAL((ge_invoke(expr15, 7, 10, 25)), 42);
// user-defined operations on containers such as 'size' and 'front'
std::string expr16_str;// = "expr16";
auto expr16_a = _x1.size();
auto expr16_b = _x1.front() == 'e';
CPPA_CHECK_EQUAL((ge_invoke(expr16_b, expr16_str)), false);
CPPA_CHECK_EQUAL((ge_invoke(expr16_a, expr16_str)), 0);
expr16_str = "expr16";
CPPA_CHECK_EQUAL((ge_invoke(expr16_b, expr16_str)), true);
CPPA_CHECK_EQUAL((ge_invoke(expr16_a, expr16_str)), expr16_str.size());
expr16_str.front() = '_';
CPPA_CHECK_EQUAL((ge_invoke(expr16_b, expr16_str)), false);
}
void test_gref() { void reset() {
int expr17_value = 42; fill(begin(s_invoked), end(s_invoked), false);
auto expr17 = gref(expr17_value) == 42;
CPPA_CHECK_EQUAL(ge_invoke(expr17), true);
expr17_value = 0;
CPPA_CHECK_EQUAL(ge_invoke(expr17), false);
int expr18_value = 42;
auto expr18_a = gref(expr18_value) == 42;
CPPA_CHECK_EQUAL(ge_invoke(expr18_a), true);
expr18_value = 0;
CPPA_CHECK_EQUAL(ge_invoke(expr18_a), false);
auto expr18_b = gref(expr18_value) == _x1;
auto expr18_c = std::ref(expr18_value) == _x1;
CPPA_CHECK_EQUAL((ge_invoke(expr18_b, 0)), true);
CPPA_CHECK_EQUAL((ge_invoke(expr18_c, 0)), true);
bool enable_case1 = true;
auto expr19 = (
on<anything>().when(gref(enable_case1) == false) >> [] { return 1; },
on<anything>() >> [] { return 2; }
);
message expr19_tup = make_cow_tuple("hello guard!");
auto res19 = expr19(expr19_tup);
CPPA_CHECK(get<int>(&res19) && get<int>(res19) == 2);
message_handler expr20 = expr19;
enable_case1 = false;
CPPA_CHECK(expr20(expr19_tup) == make_message(1));
message_handler expr21 {
on(atom("add"), arg_match) >> [](int a, int b) {
return a + b;
}
};
CPPA_CHECK(expr21(make_message(atom("add"), 1, 2)) == make_message(3));
CPPA_CHECK(!expr21(make_message(atom("sub"), 1, 2)));
} }
void test_match_function() { template <class Expr, class... Ts>
auto res0 = match(5) ( bool invoked(int idx, Expr& expr, Ts&&... args) {
on_arg_match.when(_x1 < 6) >> [&](int i) { expr(make_message(forward<Ts>(args)...));
CPPA_CHECK_EQUAL(i, 5); auto res = s_invoked[idx];
} s_invoked[idx] = false;
); auto e = end(s_invoked);
CPPA_CHECK(!get<none_t>(&res0)); res = res && find(begin(s_invoked), e, true) == e;
auto res1 = match("value=42") ( reset();
on(kvp_split).when(_x1.not_empty()) >> [&](const vector<string>& vec) { return res;
CPPA_CHECK_EQUAL("value", vec[0]);
CPPA_CHECK_EQUAL("42", vec[1]);
}
);
CPPA_CHECK(!get<none_t>(&res1));
auto res2 = match("42") (
on(toint) >> [&](int i) {
CPPA_CHECK_EQUAL(42, i);
}
);
CPPA_CHECK(!get<none_t>(&res2));
auto res3 = match("abc") (
on<string>().when(_x1 == "abc") >> [&] { }
);
CPPA_CHECK(!get<none_t>(&res3));
CPPA_CHECK(get<unit_t>(&res3));
// match vectors
auto res4 = match(std::vector<int>{1, 2, 3}) (
on<int, int, int>().when( _x1 + _x2 + _x3 == 6
&& _x2(is_even)
&& _x3 % 2 == 1 ) >> [&] { }
);
CPPA_CHECK(!get<none_t>(&res4));
vector<string> vec{"a", "b", "c"};
auto res5 = match(vec) (
on("a", "b", val<string>) >> [](string&) -> string {
return "C";
}
);
CPPA_CHECK_EQUAL(get<string>(res5), "C");
} }
void test_match_each() { template <class Expr, class... Ts>
string sum; bool not_invoked(Expr& expr, Ts&&... args) {
vector<string> sum_args = { "-h", "--version", "-wtf" }; expr(make_message(forward<Ts>(args)...));
auto iter1 = match_each(begin(sum_args), end(sum_args)) ( auto e = end(s_invoked);
on<string>().when(_x1.in({"-h", "--help"})) >> [&](const string& s) { auto res = find(begin(s_invoked), e, true) == e;
sum += s; fill(begin(s_invoked), end(s_invoked), false);
}, return res;
on<string>().when(_x1 == "-v" || _x1 == "--version") >> [&](const string& s) {
sum += s;
},
on<string>().when(_x1.starts_with("-")) >> [&](const string& str) {
match_each(str.begin() + 1, str.end()) (
on<char>().when(_x1.in({'w', 't', 'f'})) >> [&](char c) {
sum += c;
},
on<char>() >> CPPA_FAILURE_CB("guard didn't match"),
others() >> CPPA_FAILURE_CB("unexpected match")
);
}
);
CPPA_CHECK(iter1 == end(sum_args));
CPPA_CHECK_EQUAL(sum, "-h--versionwtf");
auto iter2 = match_each(begin(sum_args), end(sum_args)) (
on("-h") >> [] { }
);
CPPA_CHECK(iter2 == (begin(sum_args) + 1));
} }
void test_match_stream() { function<void()> f(int idx) {
bool success = false; return [=] {
istringstream iss("hello world"); s_invoked[idx] = true;
success = match_stream<string>(iss) (
on("hello", "world") >> CPPA_CHECKPOINT_CB()
);
CPPA_CHECK_EQUAL(success, true);
auto extract_name = [](const string& kvp) -> optional<string> {
auto vec = split(kvp, '=');
if (vec.size() == 2 && vec.front() == "--name") return vec.back();
return none;
}; };
const char* svec[] = {"-n", "foo", "--name=bar", "-p", "2"};
success = match_stream<string>(begin(svec), end(svec)) (
(on("-n", arg_match) || on(extract_name)) >> [](const string& name) -> bool {
CPPA_CHECK(name == "foo" || name == "bar");
return name == "foo" || name == "bar";
},
on("-p", toint) >> [&](int port) {
CPPA_CHECK_EQUAL(port, 2);
},
on_arg_match >> [](const string& arg) -> skip_message_t {
CPPA_FAILURE("unexpected string: " << arg);
return {};
}
);
CPPA_CHECK_EQUAL(success, true);
} }
void test_behavior() { void test_atoms() {
std::string last_invoked_fun; {
# define bhvr_check(pf, tup, expected_result, str) { \ auto expr = on(atom("hi")) >> f(0);
last_invoked_fun = ""; \ CAF_CHECK(invoked(0, expr, atom("hi")));
CPPA_CHECK_EQUAL(static_cast<bool>(pf(tup)), expected_result); \ CAF_CHECK(not_invoked(expr, atom("ho")));
CPPA_CHECK_EQUAL(last_invoked_fun, str); \ CAF_CHECK(not_invoked(expr, atom("hi"), atom("hi")));
CAF_CHECK(not_invoked(expr, "hi"));
} }
auto not_42 = [](int i) -> optional<int> {
if (i != 42) return i;
return none;
};
behavior bhvr1 {
on(not_42) >> [&](int) {
last_invoked_fun = "<int>@1";
},
on<float>() >> [&] {
last_invoked_fun = "<float>@2";
},
on<int>() >> [&] {
last_invoked_fun = "<int>@3";
},
others() >> [&] {
last_invoked_fun = "<*>@4";
}
};
bhvr_check(bhvr1, make_message(42), true, "<int>@3");
bhvr_check(bhvr1, make_message(24), true, "<int>@1");
bhvr_check(bhvr1, make_message(2.f), true, "<float>@2");
bhvr_check(bhvr1, make_message(""), true, "<*>@4");
message_handler pf0 {
on<int, int>() >> [&] { last_invoked_fun = "<int, int>@1"; },
on<float>() >> [&] { last_invoked_fun = "<float>@2"; }
};
auto pf1 = pf0.or_else (
on<int, int>() >> [&] { last_invoked_fun = "<int, int>@3"; },
on<string>() >> [&] { last_invoked_fun = "<string>@4"; }
);
bhvr_check(pf0, make_message(1, 2), true, "<int, int>@1");
bhvr_check(pf1, make_message(1, 2), true, "<int, int>@1");
bhvr_check(pf0, make_message("hi"), false, "");
bhvr_check(pf1, make_message("hi"), true, "<string>@4");
message_handler pf11 {
on_arg_match >> [&](int) { last_invoked_fun = "<int>@1"; }
};
message_handler pf12 {
on_arg_match >> [&](int) { last_invoked_fun = "<int>@2"; },
on_arg_match >> [&](float) { last_invoked_fun = "<float>@2"; }
};
auto pf13 = pf11.or_else(pf12);
bhvr_check(pf13, make_message(42), true, "<int>@1");
bhvr_check(pf13, make_message(42.24f), true, "<float>@2");
}
void test_pattern_matching() {
auto res = match(make_message(42, 4.2f)) (
on(42, arg_match) >> [](double d) {
return d;
},
on(42, arg_match) >> [](float f) {
return f;
}
);
CPPA_CHECK(get<float>(&res) && util::safe_equal(get<float>(res), 4.2f));
auto res2 = match(make_message(23, 4.2f)) (
on(42, arg_match) >> [](double d) {
return d;
},
on(42, arg_match) >> [](float f) {
return f;
}
);
CPPA_CHECK(get<none_t>(&res2));
}
inline void make_dynamically_typed_impl(detail::object_array&) { }
template<typename T0, typename... Ts>
void make_dynamically_typed_impl(detail::object_array& arr, T0&& arg0, Ts&&... args) {
using type = typename detail::strip_and_convert<T0>::type;
arr.push_back(make_uniform_value<type>(uniform_typeid<type>(),
std::forward<T0>(arg0)));
make_dynamically_typed_impl(arr, std::forward<Ts>(args)...);
}
template<typename... Ts>
message make_dynamically_typed(Ts&&... args) {
auto oarr = new detail::object_array;
make_dynamically_typed_impl(*oarr, std::forward<Ts>(args)...);
return message{static_cast<message::raw_ptr>(oarr)};
} }
void test_wildcards() { void test_custom_projections() {
// leading wildcard // check whether projection is called
auto expr0 = ( {
on(any_vals, arg_match) >> [](int a, int b) { bool guard_called = false;
CPPA_CHECK_EQUAL(a, 1); auto guard = [&](int arg) -> optional<int> {
CPPA_CHECK_EQUAL(b, 2); guard_called = true;
} return arg;
); };
CPPA_CHECK_NOT(expr0(1)); auto expr = (
CPPA_CHECK(expr0(1, 2)); on(guard) >> f(0)
CPPA_CHECK(expr0(0, 1, 2)); );
message_handler pf0 = expr0; CAF_CHECK(invoked(0, expr, 42));
CPPA_CHECK(not pf0(make_message(1))); CAF_CHECK(guard_called);
CPPA_CHECK(pf0(make_message(1, 2))); }
CPPA_CHECK(pf0(make_message(0, 1, 2))); // check forwarding optional<const string&> from guard
CPPA_CHECK(not pf0(make_dynamically_typed(1))); {
CPPA_CHECK(pf0(make_dynamically_typed(1, 2))); auto expr = (
CPPA_CHECK(pf0(make_dynamically_typed(0, 1, 2))); on(starts_with("--")) >> [](const string& str) {
behavior bhvr0 = expr0; CAF_CHECK_EQUAL(str, "help");
CPPA_CHECK(bhvr0(make_message(1, 2))); s_invoked[0] = true;
CPPA_CHECK(bhvr0(make_message(0, 1, 2))); }
CPPA_CHECK(bhvr0(make_dynamically_typed(1, 2))); );
CPPA_CHECK(bhvr0(make_dynamically_typed(0, 1, 2))); CAF_CHECK(invoked(0, expr, "--help"));
// wildcard in between CAF_CHECK(not_invoked(expr, "-help"));
message_handler pf1 { CAF_CHECK(not_invoked(expr, "--help", "--help"));
on<int, anything, int>() >> [](int a, int b) { CAF_CHECK(not_invoked(expr, 42));
CPPA_CHECK_EQUAL(a, 10); }
CPPA_CHECK_EQUAL(b, 20); // check projection
} {
}; auto expr = (
CPPA_CHECK(pf1(make_message(10, 20))); on(toint) >> [](int i) {
CPPA_CHECK(pf1(make_message(10, 15, 20))); CAF_CHECK_EQUAL(i, 42);
CPPA_CHECK(pf1(make_message(10, "hello world", 15, 20))); s_invoked[0] = true;
CPPA_CHECK(pf1(make_dynamically_typed(10, 20))); }
CPPA_CHECK(pf1(make_dynamically_typed(10, 15, 20))); );
CPPA_CHECK(pf1(make_dynamically_typed(10, "hello world", 15, 20))); CAF_CHECK(invoked(0, expr, "42"));
// multiple wildcards: one leading CAF_CHECK(not_invoked(expr, "42f"));
message_handler pf2 { CAF_CHECK(not_invoked(expr, "42", "42"));
on<anything, int, anything, int>() >> [](int a, int b) { CAF_CHECK(not_invoked(expr, 42));
CPPA_CHECK_EQUAL(a, 10); }
CPPA_CHECK_EQUAL(b, 20);
}
};
CPPA_CHECK(pf2(make_message(10, 20)));
CPPA_CHECK(pf2(make_message(10, 15, 20)));
CPPA_CHECK(pf2(make_message(10, "hello world", 15, 20)));
CPPA_CHECK(pf2(make_message("hello world", 10, 20)));
CPPA_CHECK(pf2(make_message("hello", 10, "world", 15, 20)));
CPPA_CHECK(pf2(make_dynamically_typed(10, 20)));
CPPA_CHECK(pf2(make_dynamically_typed(10, 15, 20)));
CPPA_CHECK(pf2(make_dynamically_typed(10, "hello world", 15, 20)));
CPPA_CHECK(pf2(make_dynamically_typed("hello world", 10, 20)));
CPPA_CHECK(pf2(make_dynamically_typed("hello", 10, "world", 15, 20)));
// multiple wildcards: in between
message_handler pf3 {
on<int, anything, int, anything, int>() >> [](int a, int b, int c) {
CPPA_CHECK_EQUAL(a, 10);
CPPA_CHECK_EQUAL(b, 20);
CPPA_CHECK_EQUAL(c, 30);
}
};
CPPA_CHECK(pf3(make_message(10, 20, 30)));
CPPA_CHECK(pf3(make_message(10, 20, 25, 30)));
CPPA_CHECK(pf3(make_message(10, "hello", 20, "world", 30)));
CPPA_CHECK(pf3(make_message(10, "hello", 20, "world", 1, 2, 30)));
CPPA_CHECK(pf3(make_dynamically_typed(10, 20, 30)));
CPPA_CHECK(pf3(make_dynamically_typed(10, 20, 25, 30)));
CPPA_CHECK(pf3(make_dynamically_typed(10, "hello", 20, "world", 30)));
CPPA_CHECK(pf3(make_dynamically_typed(10, "hello", 20, "world", 1, 2, 30)));
// multiple wildcards: one trailing
message_handler pf4 {
on<int, anything, int, anything>() >> [](int a, int b) {
CPPA_CHECK_EQUAL(a, 10);
CPPA_CHECK_EQUAL(b, 20);
}
};
CPPA_CHECK(pf4(make_message(10, 20, 30)));
CPPA_CHECK(pf4(make_message(10, 20, 25, 30)));
CPPA_CHECK(pf4(make_message(10, "hello", 20, "world", 30)));
CPPA_CHECK(pf4(make_message(10, "hello", 20, "world", 1, 2, 30)));
CPPA_CHECK(pf4(make_dynamically_typed(10, 20, 30)));
CPPA_CHECK(pf4(make_dynamically_typed(10, 20, 25, 30)));
CPPA_CHECK(pf4(make_dynamically_typed(10, "hello", 20, "world", 30)));
CPPA_CHECK(pf4(make_dynamically_typed(10, "hello", 20, "world", 1, 2, 30)));
// multiple wildcards: leading and trailing
message_handler pf5 {
on<anything, int, anything>() >> [](int a) {
CPPA_CHECK_EQUAL(a, 10);
}
};
CPPA_CHECK(pf5(make_message(10, 20, 30)));
CPPA_CHECK(pf5(make_message("hello", 10, "world", 30)));
CPPA_CHECK(pf5(make_message("hello", "world", 10)));
CPPA_CHECK(pf5(make_dynamically_typed(10, 20, 30)));
CPPA_CHECK(pf5(make_dynamically_typed("hello", 10, "world", 30)));
CPPA_CHECK(pf5(make_dynamically_typed("hello", "world", 10)));
} }
int main() { int main() {
CPPA_TEST(test_match); CAF_TEST(test_match);
test_wildcards(); test_atoms();
test_simple_guards(); test_custom_projections();
test_container_guards(); return CAF_TEST_RESULT();
test_user_defined_expressions();
test_gref();
test_match_function();
test_match_each();
test_match_stream();
test_behavior();
test_pattern_matching();
return CPPA_TEST_RESULT();
} }
...@@ -301,7 +301,11 @@ void test_sync_send() { ...@@ -301,7 +301,11 @@ void test_sync_send() {
}); });
self->on_sync_failure(CAF_UNEXPECTED_MSG_CB_REF(self)); self->on_sync_failure(CAF_UNEXPECTED_MSG_CB_REF(self));
self->timed_sync_send(c, std::chrono::milliseconds(500), atom("HiThere")) self->timed_sync_send(c, std::chrono::milliseconds(500), atom("HiThere"))
.await(CAF_FAILURE_CB("C replied to 'HiThere'!")); .await(
on(val<atom_value>) >> [&] {
cout << "C did reply to 'HiThere'" << endl;
}
);
CAF_CHECK_EQUAL(timeout_occured, true); CAF_CHECK_EQUAL(timeout_occured, true);
self->on_sync_failure(CAF_UNEXPECTED_MSG_CB_REF(self)); self->on_sync_failure(CAF_UNEXPECTED_MSG_CB_REF(self));
self->sync_send(c, atom("gogo")).await(CAF_CHECKPOINT_CB()); self->sync_send(c, atom("gogo")).await(CAF_CHECKPOINT_CB());
......
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