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 ...@@ -59,6 +59,7 @@ set (LIBCAF_CORE_SRCS
src/message_builder.cpp src/message_builder.cpp
src/message_data.cpp src/message_data.cpp
src/message_handler.cpp src/message_handler.cpp
src/message_iterator.cpp
src/node_id.cpp src/node_id.cpp
src/ref_counted.cpp src/ref_counted.cpp
src/response_promise.cpp src/response_promise.cpp
...@@ -74,6 +75,7 @@ set (LIBCAF_CORE_SRCS ...@@ -74,6 +75,7 @@ set (LIBCAF_CORE_SRCS
src/string_algorithms.cpp src/string_algorithms.cpp
src/string_serialization.cpp src/string_serialization.cpp
src/sync_request_bouncer.cpp src/sync_request_bouncer.cpp
src/try_match.cpp
src/uniform_type_info.cpp src/uniform_type_info.cpp
src/uniform_type_info_map.cpp) src/uniform_type_info_map.cpp)
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#define CAF_ATOM_HPP #define CAF_ATOM_HPP
#include <string> #include <string>
#include <type_traits>
#include "caf/detail/atom_val.hpp" #include "caf/detail/atom_val.hpp"
...@@ -46,6 +47,9 @@ constexpr atom_value atom(char const (&str)[Size]) { ...@@ -46,6 +47,9 @@ constexpr atom_value atom(char const (&str)[Size]) {
return static_cast<atom_value>(detail::atom_val(str, 0xF)); 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 } // namespace caf
#endif // CAF_ATOM_HPP #endif // CAF_ATOM_HPP
...@@ -31,7 +31,6 @@ ...@@ -31,7 +31,6 @@
#include "caf/uniform_type_info.hpp" #include "caf/uniform_type_info.hpp"
#include "caf/detail/type_list.hpp" #include "caf/detail/type_list.hpp"
#include "caf/detail/message_iterator.hpp" #include "caf/detail/message_iterator.hpp"
namespace caf { namespace caf {
...@@ -74,7 +73,7 @@ class message_data : public ref_counted { ...@@ -74,7 +73,7 @@ class message_data : public ref_counted {
bool equals(const message_data& other) const; bool equals(const message_data& other) const;
using const_iterator = message_iterator<message_data>; using const_iterator = message_iterator;
inline const_iterator begin() const { inline const_iterator begin() const {
return {this}; return {this};
...@@ -133,37 +132,6 @@ class message_data : public ref_counted { ...@@ -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&); std::string get_tuple_type_names(const detail::message_data&);
} // namespace detail } // namespace detail
......
...@@ -17,53 +17,41 @@ ...@@ -17,53 +17,41 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
#ifndef CAF_DETAIL_TUPLE_ITERATOR_HPP #ifndef CAF_DETAIL_MESSAGE_ITERATOR_HPP
#define CAF_DETAIL_TUPLE_ITERATOR_HPP #define CAF_DETAIL_MESSAGE_ITERATOR_HPP
#include <cstddef> #include <cstddef>
#include "caf/config.hpp" #include "caf/fwd.hpp"
namespace caf { namespace caf {
namespace detail { namespace detail {
template <class Tuple> class message_data;
class message_iterator {
size_t m_pos;
const Tuple* m_tuple;
class message_iterator {
public: public:
using pointer = message_data*;
using const_pointer = const message_data*;
inline message_iterator(const Tuple* tup, size_t pos = 0) message_iterator(const_pointer data, size_t pos = 0);
: m_pos(pos), m_tuple(tup) {}
message_iterator(const message_iterator&) = default; message_iterator(const message_iterator&) = default;
message_iterator& operator=(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++() { inline message_iterator& operator++() {
++m_pos; ++m_pos;
return *this; return *this;
} }
inline message_iterator& operator--() { inline message_iterator& operator--() {
CAF_REQUIRE(m_pos > 0);
--m_pos; --m_pos;
return *this; return *this;
} }
inline message_iterator operator+(size_t offset) { 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) { inline message_iterator& operator+=(size_t offset) {
...@@ -72,29 +60,46 @@ class message_iterator { ...@@ -72,29 +60,46 @@ class message_iterator {
} }
inline message_iterator operator-(size_t offset) { inline message_iterator operator-(size_t offset) {
CAF_REQUIRE(m_pos >= offset); return {m_data, m_pos - offset};
return {m_tuple, m_pos - offset};
} }
inline message_iterator& operator-=(size_t offset) { inline message_iterator& operator-=(size_t offset) {
CAF_REQUIRE(m_pos >= offset);
m_pos -= offset; m_pos -= offset;
return *this; return *this;
} }
inline size_t position() const { return m_pos; } inline size_t position() const {
return m_pos;
inline const void* value() const { return m_tuple->at(m_pos); } }
inline const uniform_type_info* type() const { inline const_pointer data() const {
return m_tuple->type_at(m_pos); 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 detail
} // namespace caf } // namespace caf
#endif // CAF_DETAIL_TUPLE_ITERATOR_HPP #endif // CAF_DETAIL_MESSAGE_ITERATOR_HPP
...@@ -47,15 +47,17 @@ template <size_t N, class... Ts> ...@@ -47,15 +47,17 @@ template <size_t N, class... Ts>
const typename detail::type_at<N, Ts...>::type& const typename detail::type_at<N, Ts...>::type&
get(const detail::pseudo_tuple<Ts...>& tv) { get(const detail::pseudo_tuple<Ts...>& tv) {
static_assert(N < sizeof...(Ts), "N >= tv.size()"); static_assert(N < sizeof...(Ts), "N >= tv.size()");
return *reinterpret_cast<const typename detail::type_at<N, Ts...>::type*>( auto vp = tv.at(N);
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> template <size_t N, class... Ts>
typename detail::type_at<N, Ts...>::type& get(detail::pseudo_tuple<Ts...>& tv) { typename detail::type_at<N, Ts...>::type& get(detail::pseudo_tuple<Ts...>& tv) {
static_assert(N < sizeof...(Ts), "N >= tv.size()"); static_assert(N < sizeof...(Ts), "N >= tv.size()");
return *reinterpret_cast<typename detail::type_at<N, Ts...>::type*>( auto vp = tv.mutable_at(N);
tv.mutable_at(N)); CAF_REQUIRE(vp != nullptr);
return *reinterpret_cast<typename detail::type_at<N, Ts...>::type*>(vp);
} }
} // namespace detail } // namespace detail
......
...@@ -20,7 +20,9 @@ ...@@ -20,7 +20,9 @@
#ifndef CAF_DETAIL_MATCHES_HPP #ifndef CAF_DETAIL_MATCHES_HPP
#define CAF_DETAIL_MATCHES_HPP #define CAF_DETAIL_MATCHES_HPP
#include <array>
#include <numeric> #include <numeric>
#include <typeinfo>
#include "caf/message.hpp" #include "caf/message.hpp"
#include "caf/wildcard_position.hpp" #include "caf/wildcard_position.hpp"
...@@ -31,80 +33,75 @@ ...@@ -31,80 +33,75 @@
namespace caf { namespace caf {
namespace detail { namespace detail {
template <class Pattern, class FilteredPattern> bool match_element(const std::type_info* type, const message_iterator& iter,
struct matcher; void** storage);
template <class... Ts, class... Us> template <class T>
struct matcher<type_list<Ts...>, type_list<Us...>> { bool match_integral_constant_element(const std::type_info* type,
template <class TupleIter, class PatternIter, class Push, class Commit, const message_iterator& iter,
class Rollback> void** storage) {
bool operator()(TupleIter tbegin, TupleIter tend, PatternIter pbegin, auto value = T::value;
PatternIter pend, Push& push, Commit& commit, auto uti = iter.type();
Rollback& rollback) const { if (!uti->equal_to(*type) || !uti->equals(iter.value(), &value)) {
while (!(pbegin == pend && tbegin == tend)) { return false;
if (pbegin == pend) { }
// reached end of pattern while some values remain unmatched if (storage) {
return false; // This assignment implicitly casts `T*` to `integral_constant<T, V>*`.
} // This type violation could theoretically cause undefined behavior.
if (*pbegin == nullptr) { // nullptr == wildcard (anything) // However, `T::value` does have an address that is guaranteed to be valid
// perform submatching // throughout the runtime of the program and the integral constant
++pbegin; // objects does not have any members. Hence, this is nonetheless safe.
// always true at the end of the pattern auto ptr = reinterpret_cast<const void*>(&T::value);
if (pbegin == pend) { // Again, this const cast is always safe because we will never derefence
return true; // this pointer since `integral_constant<T, V>` has no members.
} *storage = const_cast<void*>(ptr);
// safe current mapping as fallback }
commit(); return true;
// iterate over tuple values until we found a match }
for (; tbegin != tend; ++tbegin) {
if ((*this)(tbegin, tend, pbegin, pend, push, commit, rollback)) { struct meta_element {
return true; const std::type_info* type;
} bool (*fun)(const std::type_info*, const message_iterator&, void**);
// restore mapping to fallback (delete invalid mappings) };
rollback();
} template <class T>
return false; // no submatch found struct meta_element_factory {
} static meta_element create() {
// compare types return {&typeid(T), match_element};
if (tbegin.type() != *pbegin) {
// type mismatch
return false;
}
// next iteration
push(tbegin);
++tbegin;
++pbegin;
}
return true; // pbegin == pend && tbegin == tend
} }
};
bool operator()(const message& tup, pseudo_tuple<Us...>* out) const { template <class T, T V>
auto& tarr = static_types_array<Ts...>::arr; struct meta_element_factory<std::integral_constant<T, V>> {
if (sizeof...(Us) == 0) { static meta_element create() {
// this pattern only has wildcards and thus always matches return {&typeid(T),
return true; match_integral_constant_element<std::integral_constant<T, V>>};
}
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 <>
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 detail
} // namespace caf } // namespace caf
......
...@@ -481,6 +481,16 @@ struct is_optional<optional<T>> : std::true_type { ...@@ -481,6 +481,16 @@ struct is_optional<optional<T>> : std::true_type {
// no members // 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 detail
} // namespace caf } // namespace caf
......
...@@ -138,7 +138,9 @@ struct type_checker<type_pair<Opt1, Opt2>, F1, F2> { ...@@ -138,7 +138,9 @@ struct type_checker<type_pair<Opt1, Opt2>, F1, F2> {
template <class A, class B, template <class, class> class Predicate> template <class A, class B, template <class, class> class Predicate>
struct static_asserter { 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> template <class T>
...@@ -163,15 +165,21 @@ struct deduce_lifted_output_type<type_list<typed_continue_helper<R>>> { ...@@ -163,15 +165,21 @@ struct deduce_lifted_output_type<type_list<typed_continue_helper<R>>> {
template <class Signatures, typename InputTypes> template <class Signatures, typename InputTypes>
struct deduce_output_type { struct deduce_output_type {
static constexpr int input_pos = tl_find_if< static_assert(tl_find<
Signatures, InputTypes,
input_is<InputTypes>::template eval atom_value
>::value; >::value == -1,
static_assert(input_pos >= 0, "typed actor does not support given input"); "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 signature = typename tl_at<Signatures, input_pos>::type;
using type = using type = detail::type_pair<typename signature::output_opt1_types,
detail::type_pair<typename signature::output_opt1_types, typename signature::output_opt2_types>;
typename signature::output_opt2_types>;
}; };
template <class... Ts> template <class... Ts>
......
...@@ -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/matcher.hpp" #include "caf/detail/try_match.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"
...@@ -227,10 +227,9 @@ Result unroll_expr(PPFPs& fs, uint64_t bitmask, long_constant<N>, Msg& msg) { ...@@ -227,10 +227,9 @@ Result unroll_expr(PPFPs& fs, uint64_t bitmask, long_constant<N>, Msg& msg) {
} }
auto& f = get<N>(fs); auto& f = get<N>(fs);
using ft = typename std::decay<decltype(f)>::type; 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; typename ft::intermediate_tuple targs;
if (match(msg, &targs)) { if (try_match(msg, ms.arr.data(), ms.arr.size(), targs.data)) {
//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(msg, targs)); auto res = detail::apply_args(f, is, deduce_const(msg, targs));
if (unroll_expr_result_valid(res)) { if (unroll_expr_result_valid(res)) {
...@@ -240,19 +239,21 @@ Result unroll_expr(PPFPs& fs, uint64_t bitmask, long_constant<N>, Msg& msg) { ...@@ -240,19 +239,21 @@ Result unroll_expr(PPFPs& fs, uint64_t bitmask, long_constant<N>, Msg& msg) {
return none; return none;
} }
template <class PPFPs, class Tuple> template <class PPFPs>
uint64_t calc_bitmask(PPFPs&, minus1l, const std::type_info&, const Tuple&) { uint64_t calc_bitmask(PPFPs&, minus1l, const std::type_info&, const message&) {
return 0x00; return 0x00;
} }
template <class Case, long N, class Tuple> template <class Case, long N>
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 message& msg) {
auto& f = get<N>(fs); auto& f = get<N>(fs);
using ft = typename std::decay<decltype(f)>::type; using ft = typename std::decay<decltype(f)>::type;
detail::matcher<typename ft::pattern, typename ft::filtered_pattern> match; meta_elements<typename ft::pattern> ms;
uint64_t result = match(tup, nullptr) ? (0x01 << N) : 0x00; uint64_t result = try_match(msg, ms.arr.data(), ms.arr.size(), nullptr)
return result | calc_bitmask(fs, long_constant<N - 1l>(), tinf, tup); ? (0x01 << N)
: 0x00;
return result | calc_bitmask(fs, long_constant<N - 1l>(), tinf, msg);
} }
template <bool IsManipulator, typename T0, typename T1> template <bool IsManipulator, typename T0, typename T1>
......
...@@ -181,16 +181,12 @@ class message { ...@@ -181,16 +181,12 @@ class message {
/** /**
* Returns an iterator to the beginning. * Returns an iterator to the beginning.
*/ */
inline const_iterator begin() const { const_iterator begin() const;
return m_vals->begin();
}
/** /**
* Returns an iterator to the end. * Returns an iterator to the end.
*/ */
inline const_iterator end() const { const_iterator end() const;
return m_vals->end();
}
/** /**
* Returns a copy-on-write pointer to the internal data. * Returns a copy-on-write pointer to the internal data.
...@@ -285,6 +281,16 @@ inline bool operator!=(const message& lhs, const message& rhs) { ...@@ -285,6 +281,16 @@ inline bool operator!=(const message& lhs, const message& rhs) {
return !(lhs == 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...`. * Creates a new `message` containing the elements `args...`.
* @relates message * @relates message
...@@ -296,10 +302,14 @@ typename std::enable_if< ...@@ -296,10 +302,14 @@ typename std::enable_if<
message message
>::type >::type
make_message(T&& arg, Ts&&... args) { make_message(T&& arg, Ts&&... args) {
using namespace detail; using storage
using data = tuple_vals<typename strip_and_convert<T>::type, = detail::tuple_vals<typename lift_message_element<
typename strip_and_convert<Ts>::type...>; typename detail::strip_and_convert<T>::type
auto ptr = new data(std::forward<T>(arg), std::forward<Ts>(args)...); >::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}}; return message{detail::message_data::ptr{ptr}};
} }
......
...@@ -74,10 +74,12 @@ struct typed_mpi<detail::type_list<Is...>, ...@@ -74,10 +74,12 @@ struct typed_mpi<detail::type_list<Is...>,
static std::string static_type_name() { static std::string static_type_name() {
std::string input[] = {type_name_access<Is>::get()...}; std::string input[] = {type_name_access<Is>::get()...};
std::string output_opt1[] = {type_name_access<Ls>::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, return replies_to_type_name(sizeof...(Is), input,
sizeof...(Ls), output_opt1, sizeof...(Ls), output_opt1,
sizeof...(Rs), output_opt2); sizeof...(Rs), output_opt2 + 1);
} }
}; };
......
...@@ -233,7 +233,7 @@ class typed_behavior { ...@@ -233,7 +233,7 @@ class typed_behavior {
detail::type_list<typename detail::deduce_mpi<Cs>::type...>, detail::type_list<typename detail::deduce_mpi<Cs>::type...>,
detail::is_hidden_msg_handler detail::is_hidden_msg_handler
>::type; >::type;
detail::static_asserter<signatures, mpi, detail::ctm> asserter; detail::static_asserter<signatures, mpi, detail::ctm>::verify_match();
// final (type-erasure) step // final (type-erasure) step
m_bhvr = std::move(expr); m_bhvr = std::move(expr);
} }
......
...@@ -133,8 +133,11 @@ auto moving_tuple_cast(message& tup) ...@@ -133,8 +133,11 @@ auto moving_tuple_cast(message& tup)
} }
} }
// same for nil, leading, and trailing // same for nil, leading, and trailing
if (std::equal(sub.begin(), sub.end(), auto eq = [](const detail::message_iterator& lhs,
arr_pos, detail::types_only_eq)) { 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 result_type::from(sub);
} }
return none; return none;
......
...@@ -103,4 +103,15 @@ bool message::dynamically_typed() const { ...@@ -103,4 +103,15 @@ bool message::dynamically_typed() const {
return m_vals ? m_vals->dynamically_typed() : false; 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 } // namespace caf
...@@ -27,9 +27,13 @@ message_data::message_data(bool is_dynamic) : m_is_dynamic(is_dynamic) { ...@@ -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 { 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 return this == &other
|| (size() == other.size() || (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) 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 { ...@@ -650,10 +650,7 @@ class default_meta_message : public uniform_type_info {
return false; return false;
} }
bool equals(const void* instance1, const void* instance2) const override { bool equals(const void* instance1, const void* instance2) const override {
auto& lhs = *cast(instance1); return *cast(instance1) == *cast(instance2);
auto& rhs = *cast(instance2);
full_eq_type cmp;
return std::equal(lhs.begin(), lhs.end(), rhs.begin(), cmp);
} }
private: private:
......
...@@ -21,6 +21,29 @@ using namespace caf; ...@@ -21,6 +21,29 @@ using namespace caf;
namespace { namespace {
constexpr auto s_foo = atom("FooBar"); 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> template <atom_value AtomValue, class... Types>
...@@ -36,7 +59,6 @@ struct send_to_self { ...@@ -36,7 +59,6 @@ struct send_to_self {
m_self->send(m_self, std::forward<Ts>(args)...); m_self->send(m_self, std::forward<Ts>(args)...);
} }
blocking_actor* m_self; blocking_actor* m_self;
}; };
int main() { int main() {
...@@ -53,22 +75,19 @@ int main() { ...@@ -53,22 +75,19 @@ int main() {
send_to_self f{self.get()}; send_to_self f{self.get()};
f(atom("foo"), static_cast<uint32_t>(42)); f(atom("foo"), static_cast<uint32_t>(42));
f(atom(":Attach"), atom(":Baz"), "cstring"); f(atom(":Attach"), atom(":Baz"), "cstring");
// m(atom("b"), atom("a"), atom("c"), 23.f, 1.f, 1.f);
f(1.f); f(1.f);
f(atom("a"), atom("b"), atom("c"), 23.f); f(atom("a"), atom("b"), atom("c"), 23.f);
bool matched_pattern[3] = {false, false, false}; bool matched_pattern[3] = {false, false, false};
int i = 0; int i = 0;
CAF_CHECKPOINT();
for (i = 0; i < 3; ++i) { for (i = 0; i < 3; ++i) {
self->receive( self->receive(
//}
// self->receive_for(i, 3) (
on(atom("foo"), arg_match) >> [&](uint32_t value) { on(atom("foo"), arg_match) >> [&](uint32_t value) {
CAF_CHECKPOINT(); CAF_CHECKPOINT();
matched_pattern[0] = true; matched_pattern[0] = true;
CAF_CHECK_EQUAL(value, 42); CAF_CHECK_EQUAL(value, 42);
}, },
on(atom(":Attach"), atom(":Baz"), arg_match) >> on(atom(":Attach"), atom(":Baz"), arg_match) >> [&](const string& str) {
[&](const string& str) {
CAF_CHECKPOINT(); CAF_CHECKPOINT();
matched_pattern[1] = true; matched_pattern[1] = true;
CAF_CHECK_EQUAL(str, "cstring"); CAF_CHECK_EQUAL(str, "cstring");
...@@ -83,6 +102,17 @@ int main() { ...@@ -83,6 +102,17 @@ int main() {
self->receive( self->receive(
// "erase" message { atom("b"), atom("a"), atom("c"), 23.f } // "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()); 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(); return CAF_TEST_RESULT();
} }
...@@ -630,6 +630,141 @@ void counting_actor(event_based_actor* self) { ...@@ -630,6 +630,141 @@ void counting_actor(event_based_actor* self) {
CAF_CHECK_EQUAL(self->mailbox().count(), 200); 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> } // namespace <anonymous>
int main() { int main() {
...@@ -641,6 +776,10 @@ int main() { ...@@ -641,6 +776,10 @@ int main() {
CAF_CHECKPOINT(); CAF_CHECKPOINT();
await_all_actors_done(); await_all_actors_done();
CAF_CHECKPOINT(); CAF_CHECKPOINT();
test_typed_testee();
CAF_CHECKPOINT();
await_all_actors_done();
CAF_CHECKPOINT();
// test setting exit reasons for scoped actors // test setting exit reasons for scoped actors
{ // lifetime scope of self { // lifetime scope of self
scoped_actor 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