Commit b4db95d5 authored by Dominik Charousset's avatar Dominik Charousset

Use new param-API for composable behaviors

parent 0d219a24
......@@ -38,28 +38,6 @@ public:
virtual ~abstract_composable_behavior();
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
......
......@@ -57,13 +57,11 @@
#include "caf/skip_message.hpp"
#include "caf/actor_ostream.hpp"
#include "caf/function_view.hpp"
#include "caf/handler_input.hpp"
#include "caf/index_mapping.hpp"
#include "caf/spawn_options.hpp"
#include "caf/abstract_actor.hpp"
#include "caf/abstract_group.hpp"
#include "caf/blocking_actor.hpp"
#include "caf/composed_behavior.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/execution_unit.hpp"
#include "caf/memory_managed.hpp"
......@@ -78,16 +76,17 @@
#include "caf/response_handle.hpp"
#include "caf/system_messages.hpp"
#include "caf/abstract_channel.hpp"
#include "caf/composable_behavior.hpp"
#include "caf/may_have_timeout.hpp"
#include "caf/message_priority.hpp"
#include "caf/typed_actor_view.hpp"
#include "caf/binary_serializer.hpp"
#include "caf/composed_behavior.hpp"
#include "caf/event_based_actor.hpp"
#include "caf/primitive_variant.hpp"
#include "caf/timeout_definition.hpp"
#include "caf/actor_system_config.hpp"
#include "caf/binary_deserializer.hpp"
#include "caf/composable_behavior.hpp"
#include "caf/typed_actor_pointer.hpp"
#include "caf/scoped_execution_unit.hpp"
#include "caf/typed_continue_helper.hpp"
......
......@@ -62,6 +62,16 @@ struct atom_constant {
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>
std::string to_string(const atom_constant<V>&) {
return to_string(V);
......
......@@ -20,6 +20,7 @@
#ifndef CAF_COMPOSABLE_STATE_HPP
#define CAF_COMPOSABLE_STATE_HPP
#include "caf/param.hpp"
#include "caf/behavior.hpp"
#include "caf/replies_to.hpp"
#include "caf/typed_actor.hpp"
......@@ -42,7 +43,7 @@ public:
// 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
......@@ -74,8 +75,8 @@ void init_behavior_impl(T* thisptr,
detail::type_list<Ys...>>,
Ts...>,
behavior& storage, Fs... fs) {
auto f = [=](handler_input_t<Xs>... xs) {
return (*thisptr)(xs...);
auto f = [=](param_t<Xs>... xs) {
return (*thisptr)(std::move(xs)...);
};
detail::type_list<Ts...> token;
init_behavior_impl(thisptr, token, storage, fs..., f);
......
......@@ -20,7 +20,7 @@
#ifndef 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/typed_actor_pointer.hpp"
......@@ -71,7 +71,7 @@ public:
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>
......
......@@ -43,6 +43,8 @@ public:
~message_data();
bool shared() const override;
// nested types
class cow_ptr {
......
......@@ -22,12 +22,15 @@
#include <cstddef>
#include "caf/config.hpp"
#include "caf/param.hpp"
#include "caf/detail/type_traits.hpp"
namespace caf {
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>
struct pseudo_tuple {
using pointer = void*;
......@@ -35,29 +38,84 @@ struct pseudo_tuple {
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>
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) {
static_assert(N < sizeof...(Ts), "N >= tv.size()");
auto vp = tv.at(N);
CAF_ASSERT(vp != nullptr);
return *reinterpret_cast<const typename detail::type_at<N, Ts...>::type*>(vp);
pseudo_tuple_access<const typename detail::type_at<N, Ts...>::type> f;
return f.get(tv, N);
}
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()");
auto vp = tv.get_mutable(N);
CAF_ASSERT(vp != nullptr);
return *reinterpret_cast<typename detail::type_at<N, Ts...>::type*>(vp);
pseudo_tuple_access<typename detail::type_at<N, Ts...>::type> f;
return f.get(tv, N);
}
} // namespace detail
......
......@@ -20,11 +20,13 @@
#ifndef CAF_MATCH_CASE_HPP
#define CAF_MATCH_CASE_HPP
#include <type_traits>
#include "caf/none.hpp"
#include "caf/param.hpp"
#include "caf/optional.hpp"
#include "caf/skip_message.hpp"
#include "caf/match_case.hpp"
#include "caf/skip_message.hpp"
#include "caf/detail/int_list.hpp"
#include "caf/detail/try_match.hpp"
......@@ -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>
T& unopt(T& v) {
return v;
......@@ -159,12 +167,15 @@ public:
using pattern =
typename detail::tl_map<
arg_types,
std::decay
param_decay
>::type;
using intermediate_tuple =
typename detail::tl_apply<
pattern,
typename detail::tl_map<
arg_types,
std::decay
>::type,
detail::pseudo_tuple
>::type;
......@@ -178,15 +189,15 @@ public:
}
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;
// 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;
}
// detach msg before invoking fun_ if needed
if (is_manipulator) {
if (is_manipulator && it.shared_access) {
msg.force_unshare();
it.shared_access = false;
// update pointers in our intermediate tuple
for (size_t i = 0; i < msg.size(); ++i) {
// msg is guaranteed to be detached, hence we don't need to
......
......@@ -298,6 +298,10 @@ public:
vals_.unshare();
}
inline bool shared() const {
return vals_ ? vals_->shared() : false;
}
void reset(raw_ptr new_ptr = nullptr, bool add_ref = true);
void swap(message& other);
......
......@@ -17,35 +17,97 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#ifndef CAF_HANDLER_INPUT_HPP
#define CAF_HANDLER_INPUT_HPP
#ifndef CAF_PARAM_HPP
#define CAF_PARAM_HPP
#include <type_traits>
#include "caf/atom.hpp"
#include "caf/type_erased_tuple.hpp"
namespace caf {
/// Defines `type` as `const T&` unless `T` is an arithmetic type. In the
/// latter case, `type` is an alias for `T`.
template <class T, bool IsArithmetic = std::is_arithmetic<T>::value>
struct handler_input {
using type = const T&;
/// Represents a message handler parameter of type `T` and
/// guarantees copy-on-write semantics.
template <class T>
class param {
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>
struct handler_input<T, true> {
struct remove_param {
using type = T;
};
template <atom_value X>
struct handler_input<atom_constant<X>, false> {
using type = atom_constant<X>;
template <class T>
struct remove_param<param<T>> {
using type = T;
};
/// Convenience struct for `remove_param<std::decay<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
#endif // CAF_HANDLER_INPUT_HPP
#endif // CAF_PARAM_HPP
......@@ -61,6 +61,9 @@ public:
/// Returns the size of this tuple.
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.
virtual uint32_t type_token() const = 0;
......@@ -158,6 +161,10 @@ public:
return sizeof...(Ts);
}
bool shared() const override {
return false;
}
uint32_t type_token() const override {
return detail::make_type_token<Ts...>();
}
......
......@@ -28,6 +28,10 @@ message_data::~message_data() {
// nop
}
bool message_data::shared() const {
return ! unique();
}
message_data* message_data::cow_ptr::get_unshared() {
auto p = ptr_.get();
if (! p->unique()) {
......
......@@ -92,20 +92,16 @@ public:
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);
if (i == values_.end())
return "";
return i->second;
}
result<void> operator()(put_atom put, const std::string& key,
const std::string& value) override {
return invoke_mutable(this, put, key, value);
}
result<void> operator()(put_atom, std::string& key, std::string& value) {
values_.emplace(std::move(key), std::move(value));
result<void> operator()(put_atom, param<std::string> key,
param<std::string> value) override {
values_.emplace(key.move(), value.move());
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