Commit b4db95d5 authored by Dominik Charousset's avatar Dominik Charousset

Use new param-API for composable behaviors

parent 0d219a24
...@@ -38,28 +38,6 @@ public: ...@@ -38,28 +38,6 @@ public:
virtual ~abstract_composable_behavior(); virtual ~abstract_composable_behavior();
virtual void init_behavior(behavior& x) = 0; virtual void init_behavior(behavior& x) = 0;
template <class Derived, class... Ts>
auto invoke_mutable(Derived* thisptr, const Ts&...)
-> decltype(std::declval<Derived>()(std::declval<Ts>()...)) {
// re-dispatch on current message via lambda expression
auto f = [thisptr](Ts&... xs) {
return (*thisptr)(xs...);
};
// reference to the current message
auto& msg = thisptr->self->current_message();
if (! msg.template match_elements<Ts...>())
return sec::invalid_invoke_mutable;
// make sure no other actor accesses the same data
msg.force_unshare();
// fill a pseudo tuple with values and invoke f
detail::pseudo_tuple<Ts...> buf;
// msg is guaranteed to be detached, hence we don't need to
// check this condition over and over again via get_mutable
for (size_t i = 0; i < msg.size(); ++i)
buf[i] = const_cast<void*>(msg.at(i));
return detail::apply_args(f, detail::get_indices(buf), buf);
}
}; };
} // namespace caf } // namespace caf
......
...@@ -57,13 +57,11 @@ ...@@ -57,13 +57,11 @@
#include "caf/skip_message.hpp" #include "caf/skip_message.hpp"
#include "caf/actor_ostream.hpp" #include "caf/actor_ostream.hpp"
#include "caf/function_view.hpp" #include "caf/function_view.hpp"
#include "caf/handler_input.hpp"
#include "caf/index_mapping.hpp" #include "caf/index_mapping.hpp"
#include "caf/spawn_options.hpp" #include "caf/spawn_options.hpp"
#include "caf/abstract_actor.hpp" #include "caf/abstract_actor.hpp"
#include "caf/abstract_group.hpp" #include "caf/abstract_group.hpp"
#include "caf/blocking_actor.hpp" #include "caf/blocking_actor.hpp"
#include "caf/composed_behavior.hpp"
#include "caf/deep_to_string.hpp" #include "caf/deep_to_string.hpp"
#include "caf/execution_unit.hpp" #include "caf/execution_unit.hpp"
#include "caf/memory_managed.hpp" #include "caf/memory_managed.hpp"
...@@ -78,16 +76,17 @@ ...@@ -78,16 +76,17 @@
#include "caf/response_handle.hpp" #include "caf/response_handle.hpp"
#include "caf/system_messages.hpp" #include "caf/system_messages.hpp"
#include "caf/abstract_channel.hpp" #include "caf/abstract_channel.hpp"
#include "caf/composable_behavior.hpp"
#include "caf/may_have_timeout.hpp" #include "caf/may_have_timeout.hpp"
#include "caf/message_priority.hpp" #include "caf/message_priority.hpp"
#include "caf/typed_actor_view.hpp" #include "caf/typed_actor_view.hpp"
#include "caf/binary_serializer.hpp" #include "caf/binary_serializer.hpp"
#include "caf/composed_behavior.hpp"
#include "caf/event_based_actor.hpp" #include "caf/event_based_actor.hpp"
#include "caf/primitive_variant.hpp" #include "caf/primitive_variant.hpp"
#include "caf/timeout_definition.hpp" #include "caf/timeout_definition.hpp"
#include "caf/actor_system_config.hpp" #include "caf/actor_system_config.hpp"
#include "caf/binary_deserializer.hpp" #include "caf/binary_deserializer.hpp"
#include "caf/composable_behavior.hpp"
#include "caf/typed_actor_pointer.hpp" #include "caf/typed_actor_pointer.hpp"
#include "caf/scoped_execution_unit.hpp" #include "caf/scoped_execution_unit.hpp"
#include "caf/typed_continue_helper.hpp" #include "caf/typed_continue_helper.hpp"
......
...@@ -62,6 +62,16 @@ struct atom_constant { ...@@ -62,6 +62,16 @@ struct atom_constant {
static const atom_constant value; static const atom_constant value;
}; };
template <class T>
struct is_atom_constant {
static constexpr bool value = false;
};
template <atom_value X>
struct is_atom_constant<atom_constant<X>> {
static constexpr bool value = true;
};
template <atom_value V> template <atom_value V>
std::string to_string(const atom_constant<V>&) { std::string to_string(const atom_constant<V>&) {
return to_string(V); return to_string(V);
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#ifndef CAF_COMPOSABLE_STATE_HPP #ifndef CAF_COMPOSABLE_STATE_HPP
#define CAF_COMPOSABLE_STATE_HPP #define CAF_COMPOSABLE_STATE_HPP
#include "caf/param.hpp"
#include "caf/behavior.hpp" #include "caf/behavior.hpp"
#include "caf/replies_to.hpp" #include "caf/replies_to.hpp"
#include "caf/typed_actor.hpp" #include "caf/typed_actor.hpp"
...@@ -42,7 +43,7 @@ public: ...@@ -42,7 +43,7 @@ public:
// nop // nop
} }
virtual result<Ys...> operator()(handler_input_t<Xs>...) = 0; virtual result<Ys...> operator()(param_t<Xs>...) = 0;
}; };
// this class works around compiler issues on GCC // this class works around compiler issues on GCC
...@@ -74,8 +75,8 @@ void init_behavior_impl(T* thisptr, ...@@ -74,8 +75,8 @@ void init_behavior_impl(T* thisptr,
detail::type_list<Ys...>>, detail::type_list<Ys...>>,
Ts...>, Ts...>,
behavior& storage, Fs... fs) { behavior& storage, Fs... fs) {
auto f = [=](handler_input_t<Xs>... xs) { auto f = [=](param_t<Xs>... xs) {
return (*thisptr)(xs...); return (*thisptr)(std::move(xs)...);
}; };
detail::type_list<Ts...> token; detail::type_list<Ts...> token;
init_behavior_impl(thisptr, token, storage, fs..., f); init_behavior_impl(thisptr, token, storage, fs..., f);
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
#ifndef CAF_COMPOSED_STATE_HPP #ifndef CAF_COMPOSED_STATE_HPP
#define CAF_COMPOSED_STATE_HPP #define CAF_COMPOSED_STATE_HPP
#include "caf/handler_input.hpp" #include "caf/param.hpp"
#include "caf/composable_behavior.hpp" #include "caf/composable_behavior.hpp"
#include "caf/typed_actor_pointer.hpp" #include "caf/typed_actor_pointer.hpp"
...@@ -71,7 +71,7 @@ public: ...@@ -71,7 +71,7 @@ public:
using super::operator(); using super::operator();
virtual result<Ys...> operator()(handler_input_t<Xs>...) override = 0; virtual result<Ys...> operator()(param_t<Xs>...) override = 0;
}; };
template <class... Ts> template <class... Ts>
......
...@@ -43,6 +43,8 @@ public: ...@@ -43,6 +43,8 @@ public:
~message_data(); ~message_data();
bool shared() const override;
// nested types // nested types
class cow_ptr { class cow_ptr {
......
...@@ -22,12 +22,15 @@ ...@@ -22,12 +22,15 @@
#include <cstddef> #include <cstddef>
#include "caf/config.hpp"
#include "caf/param.hpp"
#include "caf/detail/type_traits.hpp" #include "caf/detail/type_traits.hpp"
namespace caf { namespace caf {
namespace detail { namespace detail {
// tuple-like access to an array of void pointers // tuple-like access to an array of void pointers that is
// also aware of the semantics of param<T>
template <class... T> template <class... T>
struct pseudo_tuple { struct pseudo_tuple {
using pointer = void*; using pointer = void*;
...@@ -35,29 +38,84 @@ struct pseudo_tuple { ...@@ -35,29 +38,84 @@ struct pseudo_tuple {
pointer data[sizeof...(T) > 0 ? sizeof...(T) : 1]; pointer data[sizeof...(T) > 0 ? sizeof...(T) : 1];
inline const_pointer at(size_t p) const { return data[p]; } bool shared_access;
inline pointer get_mutable(size_t p) { return data[p]; } pseudo_tuple(bool is_shared) : shared_access(is_shared) {
// nop
}
inline pointer& operator[](size_t p) { return data[p]; } inline const_pointer at(size_t p) const {
return data[p];
}
inline pointer get_mutable(size_t p) {
return data[p];
}
inline pointer& operator[](size_t p) {
return data[p];
}
};
template <class T>
struct pseudo_tuple_access {
using result_type = T&;
template <class Tuple>
static T& get(Tuple& xs, size_t pos) {
auto vp = xs.get_mutable(pos);
CAF_ASSERT(vp != nullptr);
return *reinterpret_cast<T*>(vp);
}
};
template <class T>
struct pseudo_tuple_access<const T> {
using result_type = const T&;
template <class Tuple>
static const T& get(const Tuple& xs, size_t pos) {
auto vp = xs.at(pos);
CAF_ASSERT(vp != nullptr);
return *reinterpret_cast<const T*>(vp);
}
};
template <class T>
struct pseudo_tuple_access<param<T>> {
using result_type = param<T>;
template <class Tuple>
static result_type get(const Tuple& xs, size_t pos) {
auto vp = xs.at(pos);
CAF_ASSERT(vp != nullptr);
return {vp, xs.shared_access};
}
};
template <class T>
struct pseudo_tuple_access<const param<T>> : pseudo_tuple_access<param<T>> {
// nop
}; };
template <size_t N, class... Ts> template <size_t N, class... Ts>
const typename detail::type_at<N, Ts...>::type& typename pseudo_tuple_access<
const typename detail::type_at<N, Ts...>::type
>::result_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()");
auto vp = tv.at(N); pseudo_tuple_access<const typename detail::type_at<N, Ts...>::type> f;
CAF_ASSERT(vp != nullptr); return f.get(tv, N);
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 pseudo_tuple_access<
typename detail::type_at<N, Ts...>::type
>::result_type
get(detail::pseudo_tuple<Ts...>& tv) {
static_assert(N < sizeof...(Ts), "N >= tv.size()"); static_assert(N < sizeof...(Ts), "N >= tv.size()");
auto vp = tv.get_mutable(N); pseudo_tuple_access<typename detail::type_at<N, Ts...>::type> f;
CAF_ASSERT(vp != nullptr); return f.get(tv, N);
return *reinterpret_cast<typename detail::type_at<N, Ts...>::type*>(vp);
} }
} // namespace detail } // namespace detail
......
...@@ -20,11 +20,13 @@ ...@@ -20,11 +20,13 @@
#ifndef CAF_MATCH_CASE_HPP #ifndef CAF_MATCH_CASE_HPP
#define CAF_MATCH_CASE_HPP #define CAF_MATCH_CASE_HPP
#include <type_traits>
#include "caf/none.hpp" #include "caf/none.hpp"
#include "caf/param.hpp"
#include "caf/optional.hpp" #include "caf/optional.hpp"
#include "caf/skip_message.hpp"
#include "caf/match_case.hpp" #include "caf/match_case.hpp"
#include "caf/skip_message.hpp"
#include "caf/detail/int_list.hpp" #include "caf/detail/int_list.hpp"
#include "caf/detail/try_match.hpp" #include "caf/detail/try_match.hpp"
...@@ -75,6 +77,12 @@ struct match_case_zipper { ...@@ -75,6 +77,12 @@ struct match_case_zipper {
} }
}; };
template <class T>
T&& unopt(T&& v,
typename std::enable_if<std::is_rvalue_reference<T&&>::value>::type* = 0) {
return std::move(v);
}
template <class T> template <class T>
T& unopt(T& v) { T& unopt(T& v) {
return v; return v;
...@@ -159,12 +167,15 @@ public: ...@@ -159,12 +167,15 @@ public:
using pattern = using pattern =
typename detail::tl_map< typename detail::tl_map<
arg_types, arg_types,
std::decay param_decay
>::type; >::type;
using intermediate_tuple = using intermediate_tuple =
typename detail::tl_apply< typename detail::tl_apply<
pattern, typename detail::tl_map<
arg_types,
std::decay
>::type,
detail::pseudo_tuple detail::pseudo_tuple
>::type; >::type;
...@@ -178,15 +189,15 @@ public: ...@@ -178,15 +189,15 @@ public:
} }
match_case::result invoke(detail::invoke_result_visitor& f, message& msg) override { match_case::result invoke(detail::invoke_result_visitor& f, message& msg) override {
intermediate_tuple it; intermediate_tuple it{msg.shared()};
detail::meta_elements<pattern> ms; detail::meta_elements<pattern> ms;
// check if try_match() reports success // check if try_match() reports success
if (! detail::try_match(msg, ms.arr.data(), ms.arr.size(), it.data)) { if (! detail::try_match(msg, ms.arr.data(), ms.arr.size(), it.data))
return match_case::no_match; return match_case::no_match;
}
// detach msg before invoking fun_ if needed // detach msg before invoking fun_ if needed
if (is_manipulator) { if (is_manipulator && it.shared_access) {
msg.force_unshare(); msg.force_unshare();
it.shared_access = false;
// update pointers in our intermediate tuple // update pointers in our intermediate tuple
for (size_t i = 0; i < msg.size(); ++i) { for (size_t i = 0; i < msg.size(); ++i) {
// msg is guaranteed to be detached, hence we don't need to // msg is guaranteed to be detached, hence we don't need to
......
...@@ -298,6 +298,10 @@ public: ...@@ -298,6 +298,10 @@ public:
vals_.unshare(); vals_.unshare();
} }
inline bool shared() const {
return vals_ ? vals_->shared() : false;
}
void reset(raw_ptr new_ptr = nullptr, bool add_ref = true); void reset(raw_ptr new_ptr = nullptr, bool add_ref = true);
void swap(message& other); void swap(message& other);
......
...@@ -17,35 +17,97 @@ ...@@ -17,35 +17,97 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
#ifndef CAF_HANDLER_INPUT_HPP #ifndef CAF_PARAM_HPP
#define CAF_HANDLER_INPUT_HPP #define CAF_PARAM_HPP
#include <type_traits> #include <type_traits>
#include "caf/atom.hpp" #include "caf/atom.hpp"
#include "caf/type_erased_tuple.hpp"
namespace caf { namespace caf {
/// Defines `type` as `const T&` unless `T` is an arithmetic type. In the /// Represents a message handler parameter of type `T` and
/// latter case, `type` is an alias for `T`. /// guarantees copy-on-write semantics.
template <class T, bool IsArithmetic = std::is_arithmetic<T>::value> template <class T>
struct handler_input { class param {
using type = const T&; public:
enum flag {
shared_access, // x_ lives in a shared type_erased_tuple
exclusive_access, // x_ lives in an unshared type_erased_tuple
private_access // x_ is a copy of the original value
};
param(const void* ptr, bool is_shared)
: x_(reinterpret_cast<T*>(const_cast<void*>(ptr))) {
flag_ = is_shared ? shared_access : exclusive_access;
}
param(const param& other) = delete;
param& operator=(const param& other) = delete;
param(param&& other) : x_(other.x_), flag_(other.flag_) {
other.x_ = nullptr;
}
~param() {
if (flag_ == private_access)
delete x_;
}
const T& get() const {
return *x_;
}
operator const T&() const {
return *x_;
}
T& get_mutable() {
if (flag_ == shared_access) {
auto cpy = new T(get());
x_ = cpy;
flag_ = private_access;
}
return *x_;
}
T&& move() {
return std::move(get_mutable());
}
private:
T* x_;
flag flag_;
}; };
/// Convenience alias that wraps `T` into `param<T>`
/// unless `T` is arithmetic or an atom constant.
template <class T>
using param_t =
typename std::conditional<
std::is_arithmetic<T>::value || is_atom_constant<T>::value,
T,
param<T>
>::type;
/// Unpacks `param<T>` to `T`.
template <class T> template <class T>
struct handler_input<T, true> { struct remove_param {
using type = T; using type = T;
}; };
template <atom_value X> template <class T>
struct handler_input<atom_constant<X>, false> { struct remove_param<param<T>> {
using type = atom_constant<X>; using type = T;
}; };
/// Convenience struct for `remove_param<std::decay<T>>`.
template <class T> template <class T>
using handler_input_t = typename handler_input<T>::type; struct param_decay {
using type = typename remove_param<typename std::decay<T>::type>::type;
};
} // namespace caf } // namespace caf
#endif // CAF_HANDLER_INPUT_HPP #endif // CAF_PARAM_HPP
...@@ -61,6 +61,9 @@ public: ...@@ -61,6 +61,9 @@ public:
/// Returns the size of this tuple. /// Returns the size of this tuple.
virtual size_t size() const = 0; virtual size_t size() const = 0;
/// Returns whether multiple references to this tuple exist.
virtual bool shared() const = 0;
/// Returns a type hint for the element types. /// Returns a type hint for the element types.
virtual uint32_t type_token() const = 0; virtual uint32_t type_token() const = 0;
...@@ -158,6 +161,10 @@ public: ...@@ -158,6 +161,10 @@ public:
return sizeof...(Ts); return sizeof...(Ts);
} }
bool shared() const override {
return false;
}
uint32_t type_token() const override { uint32_t type_token() const override {
return detail::make_type_token<Ts...>(); return detail::make_type_token<Ts...>();
} }
......
...@@ -28,6 +28,10 @@ message_data::~message_data() { ...@@ -28,6 +28,10 @@ message_data::~message_data() {
// nop // nop
} }
bool message_data::shared() const {
return ! unique();
}
message_data* message_data::cow_ptr::get_unshared() { message_data* message_data::cow_ptr::get_unshared() {
auto p = ptr_.get(); auto p = ptr_.get();
if (! p->unique()) { if (! p->unique()) {
......
...@@ -92,20 +92,16 @@ public: ...@@ -92,20 +92,16 @@ public:
return "dictionary"; return "dictionary";
} }
result<std::string> operator()(get_atom, const std::string& key) override { result<std::string> operator()(get_atom, param<std::string> key) override {
auto i = values_.find(key); auto i = values_.find(key);
if (i == values_.end()) if (i == values_.end())
return ""; return "";
return i->second; return i->second;
} }
result<void> operator()(put_atom put, const std::string& key, result<void> operator()(put_atom, param<std::string> key,
const std::string& value) override { param<std::string> value) override {
return invoke_mutable(this, put, key, value); values_.emplace(key.move(), value.move());
}
result<void> operator()(put_atom, std::string& key, std::string& value) {
values_.emplace(std::move(key), std::move(value));
return unit; return unit;
} }
......
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