Commit 1c447b62 authored by Dominik Charousset's avatar Dominik Charousset

Add atom_constant for atom-based typed interfaces

parent 8351e0f3
......@@ -59,6 +59,7 @@ set (LIBCAF_CORE_SRCS
src/message_builder.cpp
src/message_data.cpp
src/message_handler.cpp
src/message_iterator.cpp
src/node_id.cpp
src/ref_counted.cpp
src/response_promise.cpp
......@@ -74,6 +75,7 @@ set (LIBCAF_CORE_SRCS
src/string_algorithms.cpp
src/string_serialization.cpp
src/sync_request_bouncer.cpp
src/try_match.cpp
src/uniform_type_info.cpp
src/uniform_type_info_map.cpp)
......
......@@ -21,6 +21,7 @@
#define CAF_ATOM_HPP
#include <string>
#include <type_traits>
#include "caf/detail/atom_val.hpp"
......@@ -46,6 +47,9 @@ constexpr atom_value atom(char const (&str)[Size]) {
return static_cast<atom_value>(detail::atom_val(str, 0xF));
}
template <atom_value Value>
using atom_constant = std::integral_constant<atom_value, Value>;
} // namespace caf
#endif // CAF_ATOM_HPP
......@@ -31,7 +31,6 @@
#include "caf/uniform_type_info.hpp"
#include "caf/detail/type_list.hpp"
#include "caf/detail/message_iterator.hpp"
namespace caf {
......@@ -74,7 +73,7 @@ class message_data : public ref_counted {
bool equals(const message_data& other) const;
using const_iterator = message_iterator<message_data>;
using const_iterator = message_iterator;
inline const_iterator begin() const {
return {this};
......@@ -133,37 +132,6 @@ class message_data : public ref_counted {
};
struct full_eq_type {
constexpr full_eq_type() {}
template <class Tuple>
inline bool operator()(const message_iterator<Tuple>& lhs,
const message_iterator<Tuple>& rhs) const {
return lhs.type() == rhs.type() &&
lhs.type()->equals(lhs.value(), rhs.value());
}
};
struct types_only_eq_type {
constexpr types_only_eq_type() {}
template <class Tuple>
inline bool operator()(const message_iterator<Tuple>& lhs,
const uniform_type_info* rhs) const {
return lhs.type() == rhs;
}
template <class Tuple>
inline bool operator()(const uniform_type_info* lhs,
const message_iterator<Tuple>& rhs) const {
return lhs == rhs.type();
}
};
namespace {
constexpr full_eq_type full_eq;
constexpr types_only_eq_type types_only_eq;
} // namespace <anonymous>
std::string get_tuple_type_names(const detail::message_data&);
} // namespace detail
......
......@@ -17,53 +17,41 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_DETAIL_TUPLE_ITERATOR_HPP
#define CAF_DETAIL_TUPLE_ITERATOR_HPP
#ifndef CAF_DETAIL_MESSAGE_ITERATOR_HPP
#define CAF_DETAIL_MESSAGE_ITERATOR_HPP
#include <cstddef>
#include "caf/config.hpp"
#include "caf/fwd.hpp"
namespace caf {
namespace detail {
template <class Tuple>
class message_iterator {
size_t m_pos;
const Tuple* m_tuple;
class message_data;
class message_iterator {
public:
using pointer = message_data*;
using const_pointer = const message_data*;
inline message_iterator(const Tuple* tup, size_t pos = 0)
: m_pos(pos), m_tuple(tup) {}
message_iterator(const_pointer data, size_t pos = 0);
message_iterator(const message_iterator&) = default;
message_iterator& operator=(const message_iterator&) = default;
inline bool operator==(const message_iterator& other) const {
CAF_REQUIRE(other.m_tuple == other.m_tuple);
return other.m_pos == m_pos;
}
inline bool operator!=(const message_iterator& other) const {
return !(*this == other);
}
inline message_iterator& operator++() {
++m_pos;
return *this;
}
inline message_iterator& operator--() {
CAF_REQUIRE(m_pos > 0);
--m_pos;
return *this;
}
inline message_iterator operator+(size_t offset) {
return {m_tuple, m_pos + offset};
return {m_data, m_pos + offset};
}
inline message_iterator& operator+=(size_t offset) {
......@@ -72,29 +60,46 @@ class message_iterator {
}
inline message_iterator operator-(size_t offset) {
CAF_REQUIRE(m_pos >= offset);
return {m_tuple, m_pos - offset};
return {m_data, m_pos - offset};
}
inline message_iterator& operator-=(size_t offset) {
CAF_REQUIRE(m_pos >= offset);
m_pos -= offset;
return *this;
}
inline size_t position() const { return m_pos; }
inline const void* value() const { return m_tuple->at(m_pos); }
inline size_t position() const {
return m_pos;
}
inline const uniform_type_info* type() const {
return m_tuple->type_at(m_pos);
inline const_pointer data() const {
return m_data;
}
inline message_iterator& operator*() { return *this; }
const void* value() const;
const uniform_type_info* type() const;
inline message_iterator& operator*() {
return *this;
}
private:
size_t m_pos;
const message_data* m_data;
};
inline bool operator==(const message_iterator& lhs,
const message_iterator& rhs) {
return lhs.data() == rhs.data() && lhs.position() == rhs.position();
}
inline bool operator!=(const message_iterator& lhs,
const message_iterator& rhs) {
return !(lhs == rhs);
}
} // namespace detail
} // namespace caf
#endif // CAF_DETAIL_TUPLE_ITERATOR_HPP
#endif // CAF_DETAIL_MESSAGE_ITERATOR_HPP
......@@ -47,15 +47,17 @@ template <size_t N, class... Ts>
const typename detail::type_at<N, Ts...>::type&
get(const detail::pseudo_tuple<Ts...>& tv) {
static_assert(N < sizeof...(Ts), "N >= tv.size()");
return *reinterpret_cast<const typename detail::type_at<N, Ts...>::type*>(
tv.at(N));
auto vp = tv.at(N);
CAF_REQUIRE(vp != nullptr);
return *reinterpret_cast<const typename detail::type_at<N, Ts...>::type*>(vp);
}
template <size_t N, class... Ts>
typename detail::type_at<N, Ts...>::type& get(detail::pseudo_tuple<Ts...>& tv) {
static_assert(N < sizeof...(Ts), "N >= tv.size()");
return *reinterpret_cast<typename detail::type_at<N, Ts...>::type*>(
tv.mutable_at(N));
auto vp = tv.mutable_at(N);
CAF_REQUIRE(vp != nullptr);
return *reinterpret_cast<typename detail::type_at<N, Ts...>::type*>(vp);
}
} // namespace detail
......
......@@ -20,7 +20,9 @@
#ifndef CAF_DETAIL_MATCHES_HPP
#define CAF_DETAIL_MATCHES_HPP
#include <array>
#include <numeric>
#include <typeinfo>
#include "caf/message.hpp"
#include "caf/wildcard_position.hpp"
......@@ -31,80 +33,75 @@
namespace caf {
namespace detail {
template <class Pattern, class FilteredPattern>
struct matcher;
template <class... Ts, class... Us>
struct matcher<type_list<Ts...>, type_list<Us...>> {
template <class TupleIter, class PatternIter, class Push, class Commit,
class Rollback>
bool operator()(TupleIter tbegin, TupleIter tend, PatternIter pbegin,
PatternIter pend, Push& push, Commit& commit,
Rollback& rollback) const {
while (!(pbegin == pend && tbegin == tend)) {
if (pbegin == pend) {
// reached end of pattern while some values remain unmatched
return false;
}
if (*pbegin == nullptr) { // nullptr == wildcard (anything)
// perform submatching
++pbegin;
// always true at the end of the pattern
if (pbegin == pend) {
return true;
}
// safe current mapping as fallback
commit();
// iterate over tuple values until we found a match
for (; tbegin != tend; ++tbegin) {
if ((*this)(tbegin, tend, pbegin, pend, push, commit, rollback)) {
return true;
}
// restore mapping to fallback (delete invalid mappings)
rollback();
}
return false; // no submatch found
}
// compare types
if (tbegin.type() != *pbegin) {
// type mismatch
return false;
}
// next iteration
push(tbegin);
++tbegin;
++pbegin;
}
return true; // pbegin == pend && tbegin == tend
bool match_element(const std::type_info* type, const message_iterator& iter,
void** storage);
template <class T>
bool match_integral_constant_element(const std::type_info* type,
const message_iterator& iter,
void** storage) {
auto value = T::value;
auto uti = iter.type();
if (!uti->equal_to(*type) || !uti->equals(iter.value(), &value)) {
return false;
}
if (storage) {
// This assignment implicitly casts `T*` to `integral_constant<T, V>*`.
// This type violation could theoretically cause undefined behavior.
// However, `T::value` does have an address that is guaranteed to be valid
// throughout the runtime of the program and the integral constant
// objects does not have any members. Hence, this is nonetheless safe.
auto ptr = reinterpret_cast<const void*>(&T::value);
// Again, this const cast is always safe because we will never derefence
// this pointer since `integral_constant<T, V>` has no members.
*storage = const_cast<void*>(ptr);
}
return true;
}
struct meta_element {
const std::type_info* type;
bool (*fun)(const std::type_info*, const message_iterator&, void**);
};
template <class T>
struct meta_element_factory {
static meta_element create() {
return {&typeid(T), match_element};
}
};
bool operator()(const message& tup, pseudo_tuple<Us...>* out) const {
auto& tarr = static_types_array<Ts...>::arr;
if (sizeof...(Us) == 0) {
// this pattern only has wildcards and thus always matches
return true;
}
if (tup.size() < sizeof...(Us)) {
return false;
}
if (out) {
size_t pos = 0;
size_t fallback_pos = 0;
auto fpush = [&](const typename message::const_iterator& iter) {
(*out)[pos++] = const_cast<void*>(iter.value());
};
auto fcommit = [&] { fallback_pos = pos; };
auto frollback = [&] { pos = fallback_pos; };
return (*this)(tup.begin(), tup.end(), tarr.begin(), tarr.end(), fpush,
fcommit, frollback);
}
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 T, T V>
struct meta_element_factory<std::integral_constant<T, V>> {
static meta_element create() {
return {&typeid(T),
match_integral_constant_element<std::integral_constant<T, V>>};
}
};
template <>
struct meta_element_factory<anything> {
static meta_element create() {
return {nullptr, nullptr};
}
};
template <class TypeList>
struct meta_elements;
template <class... Ts>
struct meta_elements<type_list<Ts...>> {
std::array<meta_element, sizeof...(Ts)> arr;
meta_elements() : arr{{meta_element_factory<Ts>::create()...}} {
// nop
}
};
bool try_match(const message& msg,
const meta_element* pattern_begin,
size_t pattern_size,
void** out = nullptr);
} // namespace detail
} // namespace caf
......
......@@ -481,6 +481,16 @@ struct is_optional<optional<T>> : std::true_type {
// no members
};
template <class T>
struct is_integral_constant : std::false_type {
// no members
};
template <class T, T V>
struct is_integral_constant<std::integral_constant<T, V>> : std::true_type {
// no members
};
} // namespace detail
} // namespace caf
......
......@@ -138,7 +138,9 @@ struct type_checker<type_pair<Opt1, Opt2>, F1, F2> {
template <class A, class B, template <class, class> class Predicate>
struct static_asserter {
static_assert(Predicate<A, B>::value, "exact match needed");
static void verify_match() {
static_assert(Predicate<A, B>::value, "exact match needed");
}
};
template <class T>
......@@ -163,15 +165,21 @@ struct deduce_lifted_output_type<type_list<typed_continue_helper<R>>> {
template <class Signatures, typename InputTypes>
struct deduce_output_type {
static constexpr int input_pos = tl_find_if<
Signatures,
input_is<InputTypes>::template eval
>::value;
static_assert(input_pos >= 0, "typed actor does not support given input");
static_assert(tl_find<
InputTypes,
atom_value
>::value == -1,
"atom(...) notation is not sufficient for static type "
"checking, please use atom_constant instead in this context");
static constexpr int input_pos =
tl_find_if<
Signatures,
input_is<InputTypes>::template eval
>::value;
static_assert(input_pos != -1, "typed actor does not support given input");
using signature = typename tl_at<Signatures, input_pos>::type;
using type =
detail::type_pair<typename signature::output_opt1_types,
typename signature::output_opt2_types>;
using type = detail::type_pair<typename signature::output_opt1_types,
typename signature::output_opt2_types>;
};
template <class... Ts>
......
......@@ -34,7 +34,7 @@
#include "caf/detail/type_traits.hpp"
#include "caf/detail/left_or_right.hpp"
#include "caf/detail/matcher.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"
......@@ -227,10 +227,9 @@ Result unroll_expr(PPFPs& fs, uint64_t bitmask, long_constant<N>, Msg& msg) {
}
auto& f = get<N>(fs);
using ft = typename std::decay<decltype(f)>::type;
detail::matcher<typename ft::pattern, typename ft::filtered_pattern> match;
meta_elements<typename ft::pattern> ms;
typename ft::intermediate_tuple targs;
if (match(msg, &targs)) {
//if (policy::prepare_invoke(targs, type_token, is_dynamic, ptr, tup)) {
if (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 (unroll_expr_result_valid(res)) {
......@@ -240,19 +239,21 @@ Result unroll_expr(PPFPs& fs, uint64_t bitmask, long_constant<N>, Msg& msg) {
return none;
}
template <class PPFPs, class Tuple>
uint64_t calc_bitmask(PPFPs&, minus1l, const std::type_info&, const Tuple&) {
template <class PPFPs>
uint64_t calc_bitmask(PPFPs&, minus1l, const std::type_info&, const message&) {
return 0x00;
}
template <class Case, long N, class Tuple>
template <class Case, long N>
uint64_t calc_bitmask(Case& fs, long_constant<N>,
const std::type_info& tinf, const Tuple& tup) {
const std::type_info& tinf, const message& msg) {
auto& f = get<N>(fs);
using ft = typename std::decay<decltype(f)>::type;
detail::matcher<typename ft::pattern, typename ft::filtered_pattern> match;
uint64_t result = match(tup, nullptr) ? (0x01 << N) : 0x00;
return result | calc_bitmask(fs, long_constant<N - 1l>(), tinf, tup);
meta_elements<typename ft::pattern> ms;
uint64_t result = try_match(msg, ms.arr.data(), ms.arr.size(), nullptr)
? (0x01 << N)
: 0x00;
return result | calc_bitmask(fs, long_constant<N - 1l>(), tinf, msg);
}
template <bool IsManipulator, typename T0, typename T1>
......
......@@ -181,16 +181,12 @@ class message {
/**
* Returns an iterator to the beginning.
*/
inline const_iterator begin() const {
return m_vals->begin();
}
const_iterator begin() const;
/**
* Returns an iterator to the end.
*/
inline const_iterator end() const {
return m_vals->end();
}
const_iterator end() const;
/**
* Returns a copy-on-write pointer to the internal data.
......@@ -285,6 +281,16 @@ inline bool operator!=(const message& lhs, const message& rhs) {
return !(lhs == rhs);
}
template <class T>
struct lift_message_element {
using type = T;
};
template <class T, T V>
struct lift_message_element<std::integral_constant<T, V>> {
using type = T;
};
/**
* Creates a new `message` containing the elements `args...`.
* @relates message
......@@ -296,10 +302,14 @@ typename std::enable_if<
message
>::type
make_message(T&& arg, Ts&&... args) {
using namespace detail;
using data = tuple_vals<typename strip_and_convert<T>::type,
typename strip_and_convert<Ts>::type...>;
auto ptr = new data(std::forward<T>(arg), std::forward<Ts>(args)...);
using storage
= detail::tuple_vals<typename lift_message_element<
typename detail::strip_and_convert<T>::type
>::type,
typename lift_message_element<
typename detail::strip_and_convert<Ts>::type
>::type...>;
auto ptr = new storage(std::forward<T>(arg), std::forward<Ts>(args)...);
return message{detail::message_data::ptr{ptr}};
}
......
......@@ -74,10 +74,12 @@ struct typed_mpi<detail::type_list<Is...>,
static std::string static_type_name() {
std::string input[] = {type_name_access<Is>::get()...};
std::string output_opt1[] = {type_name_access<Ls>::get()...};
std::string output_opt2[] = {type_name_access<Rs>::get()...};
// Rs... is allowed to be empty, hence we need to add a dummy element
// to make sure this array is not of size 0 (to prevent compiler errors)
std::string output_opt2[] = {std::string(), type_name_access<Rs>::get()...};
return replies_to_type_name(sizeof...(Is), input,
sizeof...(Ls), output_opt1,
sizeof...(Rs), output_opt2);
sizeof...(Rs), output_opt2 + 1);
}
};
......
......@@ -233,7 +233,7 @@ class typed_behavior {
detail::type_list<typename detail::deduce_mpi<Cs>::type...>,
detail::is_hidden_msg_handler
>::type;
detail::static_asserter<signatures, mpi, detail::ctm> asserter;
detail::static_asserter<signatures, mpi, detail::ctm>::verify_match();
// final (type-erasure) step
m_bhvr = std::move(expr);
}
......
......@@ -133,8 +133,11 @@ auto moving_tuple_cast(message& tup)
}
}
// same for nil, leading, and trailing
if (std::equal(sub.begin(), sub.end(),
arr_pos, detail::types_only_eq)) {
auto eq = [](const detail::message_iterator& lhs,
const uniform_type_info* rhs) {
return lhs.type() == rhs;
};
if (std::equal(sub.begin(), sub.end(), arr_pos, eq)) {
return result_type::from(sub);
}
return none;
......
......@@ -103,4 +103,15 @@ bool message::dynamically_typed() const {
return m_vals ? m_vals->dynamically_typed() : false;
}
message::const_iterator message::begin() const {
return m_vals ? m_vals->begin() : const_iterator{nullptr, 0};
}
/**
* Returns an iterator to the end.
*/
message::const_iterator message::end() const {
return m_vals ? m_vals->end() : const_iterator{nullptr, 0};
}
} // namespace caf
......@@ -27,9 +27,13 @@ message_data::message_data(bool is_dynamic) : m_is_dynamic(is_dynamic) {
}
bool message_data::equals(const message_data& other) const {
auto full_eq = [](const message_iterator& lhs, const message_iterator& rhs) {
return lhs.type() == rhs.type()
&& lhs.type()->equals(lhs.value(), rhs.value());
};
return this == &other
|| (size() == other.size()
&& std::equal(begin(), end(), other.begin(), detail::full_eq));
&& std::equal(begin(), end(), other.begin(), full_eq));
}
message_data::message_data(const message_data& other)
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2014 *
* 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/detail/message_iterator.hpp"
#include "caf/detail/message_data.hpp"
namespace caf {
namespace detail {
message_iterator::message_iterator(const_pointer data, size_t pos)
: m_pos(pos),
m_data(data) {
// nop
}
const void* message_iterator::value() const {
return m_data->at(m_pos);
}
const uniform_type_info* message_iterator::type() const {
return m_data->type_at(m_pos);
}
} // namespace detail
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2014 *
* 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/detail/try_match.hpp"
namespace caf {
namespace detail {
using pattern_iterator = const meta_element*;
bool is_wildcard(const meta_element& me) {
return me.type == nullptr;
}
bool match_element(const std::type_info* type, const message_iterator& iter,
void** storage) {
if (!iter.type()->equal_to(*type)) {
return false;
}
if (storage) {
*storage = const_cast<void*>(iter.value());
}
return true;
}
class set_commit_rollback {
public:
using pointer = void**;
set_commit_rollback(const set_commit_rollback&) = delete;
set_commit_rollback& operator=(const set_commit_rollback&) = delete;
set_commit_rollback(pointer ptr) : m_data(ptr), m_pos(0), m_fallback_pos(0) {
// nop
}
inline void inc() {
++m_pos;
}
inline pointer current() {
return m_data? &m_data[m_pos] : nullptr;
}
inline void commit() {
m_fallback_pos = m_pos;
}
inline void rollback() {
m_pos = m_fallback_pos;
}
private:
pointer m_data;
size_t m_pos;
size_t m_fallback_pos;
};
bool try_match(message_iterator mbegin, message_iterator mend,
pattern_iterator pbegin, pattern_iterator pend,
set_commit_rollback& storage) {
while (mbegin != mend) {
if (pbegin == pend) {
return false;
}
if (is_wildcard(*pbegin)) {
// perform submatching
++pbegin;
// always true at the end of the pattern
if (pbegin == pend) {
return true;
}
// safe current mapping as fallback
storage.commit();
// iterate over remaining values until we found a match
for (; mbegin != mend; ++mbegin) {
if (try_match(mbegin, mend, pbegin, pend, storage)) {
return true;
}
// restore mapping to fallback (delete invalid mappings)
storage.rollback();
}
return false; // no submatch found
}
// inspect current element
if (!pbegin->fun(pbegin->type, mbegin, storage.current())) {
// type mismatch
return false;
}
// next iteration
storage.inc();
++mbegin;
++pbegin;
}
// we found a match if we've inspected each element and consumed
// the whole pattern (or the remainder consists of wildcards only)
return std::all_of(pbegin, pend, is_wildcard);
}
bool try_match(const message& msg, pattern_iterator pb, size_t ps, void** out) {
set_commit_rollback scr{out};
auto res = try_match(msg.begin(), msg.end(), pb, pb + ps, scr);
return res;
}
} // namespace detail
} // namespace caf
......@@ -650,10 +650,7 @@ class default_meta_message : public uniform_type_info {
return false;
}
bool equals(const void* instance1, const void* instance2) const override {
auto& lhs = *cast(instance1);
auto& rhs = *cast(instance2);
full_eq_type cmp;
return std::equal(lhs.begin(), lhs.end(), rhs.begin(), cmp);
return *cast(instance1) == *cast(instance2);
}
private:
......
......@@ -21,6 +21,29 @@ using namespace caf;
namespace {
constexpr auto s_foo = atom("FooBar");
using abc_atom = atom_constant<atom("abc")>;
}
using testee = typed_actor<replies_to<abc_atom>::with<int>>;
testee::behavior_type testee_impl(testee::pointer self) {
return {
[=](abc_atom) {
self->quit();
return 42;
}
};
}
void test_typed_atom_interface() {
scoped_actor self;
auto tst = spawn_typed(testee_impl);
self->sync_send(tst, abc_atom()).await(
[](int i) {
CAF_CHECK_EQUAL(i, 42);
},
CAF_UNEXPECTED_MSG_CB_REF(self)
);
}
template <atom_value AtomValue, class... Types>
......@@ -36,7 +59,6 @@ struct send_to_self {
m_self->send(m_self, std::forward<Ts>(args)...);
}
blocking_actor* m_self;
};
int main() {
......@@ -53,22 +75,19 @@ int main() {
send_to_self f{self.get()};
f(atom("foo"), static_cast<uint32_t>(42));
f(atom(":Attach"), atom(":Baz"), "cstring");
// m(atom("b"), atom("a"), atom("c"), 23.f, 1.f, 1.f);
f(1.f);
f(atom("a"), atom("b"), atom("c"), 23.f);
bool matched_pattern[3] = {false, false, false};
int i = 0;
CAF_CHECKPOINT();
for (i = 0; i < 3; ++i) {
self->receive(
//}
// self->receive_for(i, 3) (
on(atom("foo"), arg_match) >> [&](uint32_t value) {
CAF_CHECKPOINT();
matched_pattern[0] = true;
CAF_CHECK_EQUAL(value, 42);
},
on(atom(":Attach"), atom(":Baz"), arg_match) >>
[&](const string& str) {
on(atom(":Attach"), atom(":Baz"), arg_match) >> [&](const string& str) {
CAF_CHECKPOINT();
matched_pattern[1] = true;
CAF_CHECK_EQUAL(str, "cstring");
......@@ -83,6 +102,17 @@ int main() {
self->receive(
// "erase" message { atom("b"), atom("a"), atom("c"), 23.f }
others() >> CAF_CHECKPOINT_CB(),
after(std::chrono::seconds(0)) >> CAF_UNEXPECTED_TOUT_CB());
after(std::chrono::seconds(0)) >> CAF_UNEXPECTED_TOUT_CB()
);
atom_value x = atom("abc");
atom_value y = abc_atom();
CAF_CHECK_EQUAL(x, y);
auto msg = make_message(abc_atom());
self->send(self, msg);
self->receive(
on(atom("abc")) >> CAF_CHECKPOINT_CB(),
others() >> CAF_UNEXPECTED_MSG_CB_REF(self)
);
test_typed_atom_interface();
return CAF_TEST_RESULT();
}
......@@ -630,6 +630,141 @@ void counting_actor(event_based_actor* self) {
CAF_CHECK_EQUAL(self->mailbox().count(), 200);
}
// tests attach_functor() inside of an actor's constructor
void test_constructor_attach() {
class testee : public event_based_actor {
public:
testee(actor buddy) : m_buddy(buddy) {
attach_functor([=](uint32_t reason) {
send(m_buddy, atom("done"), reason);
});
}
behavior make_behavior() {
return {
on(atom("die")) >> [=] {
quit(exit_reason::user_shutdown);
}
};
}
private:
actor m_buddy;
};
class spawner : public event_based_actor {
public:
spawner() : m_downs(0) {
}
behavior make_behavior() {
m_testee = spawn<testee, monitored>(this);
return {
[=](const down_msg& msg) {
CAF_CHECK_EQUAL(msg.reason, exit_reason::user_shutdown);
if (++m_downs == 2) {
quit(msg.reason);
}
},
on(atom("done"), arg_match) >> [=](uint32_t reason) {
CAF_CHECK_EQUAL(reason, exit_reason::user_shutdown);
if (++m_downs == 2) {
quit(reason);
}
},
others() >> [=] {
forward_to(m_testee);
}
};
}
private:
int m_downs;
actor m_testee;
};
anon_send(spawn<spawner>(), atom("die"));
}
class exception_testee : public event_based_actor {
public:
exception_testee() {
set_exception_handler([](const std::exception_ptr& eptr) -> optional<uint32_t> {
return exit_reason::user_defined + 2;
});
}
behavior make_behavior() override {
return {
others() >> [] {
throw std::runtime_error("whatever");
}
};
}
};
void test_custom_exception_handler() {
auto handler = [](const std::exception_ptr& eptr) -> optional<uint32_t> {
try {
std::rethrow_exception(eptr);
}
catch (std::runtime_error&) {
return exit_reason::user_defined;
}
catch (...) {
// "fall through"
}
return exit_reason::user_defined + 1;
};
scoped_actor self;
auto testee1 = self->spawn<monitored>([=](event_based_actor* eb_self) {
eb_self->set_exception_handler(handler);
throw std::runtime_error("ping");
});
auto testee2 = self->spawn<monitored>([=](event_based_actor* eb_self) {
eb_self->set_exception_handler(handler);
throw std::logic_error("pong");
});
auto testee3 = self->spawn<exception_testee, monitored>();
self->send(testee3, "foo");
// receive all down messages
auto i = 0;
self->receive_for(i, 3)(
[&](const down_msg& dm) {
if (dm.source == testee1) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::user_defined);
}
else if (dm.source == testee2) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::user_defined + 1);
}
else if (dm.source == testee3) {
CAF_CHECK_EQUAL(dm.reason, exit_reason::user_defined + 2);
}
else {
CAF_CHECK(false); // report error
}
}
);
}
using abc_atom = atom_constant<atom("abc")>;
using typed_testee = typed_actor<replies_to<abc_atom>::with<std::string>>;
typed_testee::behavior_type testee(typed_testee::pointer self) {
return {
[](abc_atom) {
CAF_PRINT("received abc_atom");
return "abc";
}
};
}
void test_typed_testee() {
CAF_PRINT("test_typed_testee");
scoped_actor self;
auto x = spawn_typed(testee);
self->sync_send(x, abc_atom()).await(
[](const std::string& str) {
CAF_CHECK_EQUAL(str, "abc");
}
);
self->send_exit(x, exit_reason::user_shutdown);
}
} // namespace <anonymous>
int main() {
......@@ -641,6 +776,10 @@ int main() {
CAF_CHECKPOINT();
await_all_actors_done();
CAF_CHECKPOINT();
test_typed_testee();
CAF_CHECKPOINT();
await_all_actors_done();
CAF_CHECKPOINT();
// test setting exit reasons for scoped actors
{ // lifetime scope of self
scoped_actor self;
......
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