Commit 86ab5ae8 authored by Dominik Charousset's avatar Dominik Charousset

Add new function view for typed actors

parent 70355b45
...@@ -53,6 +53,7 @@ ...@@ -53,6 +53,7 @@
#include "caf/typed_actor.hpp" #include "caf/typed_actor.hpp"
#include "caf/actor_system.hpp" #include "caf/actor_system.hpp"
#include "caf/deserializer.hpp" #include "caf/deserializer.hpp"
#include "caf/function_view.hpp"
#include "caf/scoped_actor.hpp" #include "caf/scoped_actor.hpp"
#include "caf/skip_message.hpp" #include "caf/skip_message.hpp"
#include "caf/actor_ostream.hpp" #include "caf/actor_ostream.hpp"
......
...@@ -188,6 +188,8 @@ struct deduce_output_type { ...@@ -188,6 +188,8 @@ struct deduce_output_type {
using type = typename signature::output_types; using type = typename signature::output_types;
// generates the appropriate `delegated<...>` type from given signatures // generates the appropriate `delegated<...>` type from given signatures
using delegated_type = typename detail::tl_apply<type, delegated>::type; using delegated_type = typename detail::tl_apply<type, delegated>::type;
// generates the appropriate `std::tuple<...>` type from given signature
using tuple_type = typename detail::tl_apply<type, std::tuple>::type;
}; };
template <class... Ts> template <class... Ts>
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* 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. *
******************************************************************************/
#ifndef CAF_FUNCTION_VIEW_HPP
#define CAF_FUNCTION_VIEW_HPP
#include <new>
#include <functional>
#include "caf/typed_actor.hpp"
#include "caf/scoped_actor.hpp"
namespace caf {
template <class T>
class function_view_storage;
template <class... Ts>
class function_view_storage<std::tuple<Ts...>> {
public:
function_view_storage(std::tuple<Ts...>& storage) : storage_(&storage) {
// nop
}
void operator()(Ts&... xs) {
*storage_ = std::forward_as_tuple(std::move(xs)...);
}
private:
std::tuple<Ts...>* storage_;
};
template <class T>
struct function_view_flattened_result {
using type = T;
};
template <class T>
struct function_view_flattened_result<std::tuple<T>> {
using type = T;
};
/// A function view for an actor hides any messaging from the caller.
/// Internally, a function view uses a `scoped_actor` and uses
/// blocking send and receive operations.
template <class Actor>
class function_view {
public:
using type = Actor;
function_view() {
// nop
}
function_view(const type& impl) : impl_(impl) {
new_self(impl_);
}
~function_view() {
if (impl_)
self_.~scoped_actor();
}
function_view(function_view&& x) : impl_(std::move(x.impl_)) {
new_self(impl_);
}
function_view& operator=(function_view&& x) {
assign(x.impl_);
x.assign(invalid_actor);
return *this;
}
/// Sends a request message to the assigned actor and returns the result.
/// @throws std::bad_function_call if no actor is assigned to view
/// @throws actor_exited if the requests resulted in an error
template <class... Ts,
class R =
typename detail::deduce_output_type<
typename type::signatures,
detail::type_list<
typename detail::implicit_conversions<
typename std::decay<Ts>::type
>::type...>
>::tuple_type>
typename function_view_flattened_result<R>::type operator()(Ts&&... xs) {
if (! impl_)
throw std::bad_function_call();
R result;
function_view_storage<R> h{result};
try {
self_->request(impl_, std::forward<Ts>(xs)...).receive(h);
}
catch (std::exception&) {
assign(invalid_actor);
throw;
}
return flatten(result);
}
void assign(type x) {
if (! impl_ && x)
new_self(x);
if (impl_ && ! x)
self_.~scoped_actor();
impl_.swap(x);
}
/// Checks whether this function view has an actor assigned to it.
explicit operator bool() const {
return static_cast<bool>(impl_);
}
private:
template <class T>
T&& flatten(T& x) {
return std::move(x);
}
template <class T>
T&& flatten(std::tuple<T>& x) {
return std::move(get<0>(x));
}
void new_self(const Actor& x) {
if (x)
new (&self_) scoped_actor(x->home_system());
}
union { scoped_actor self_; };
type impl_;
};
/// @relates function_view
template <class T>
bool operator==(const function_view<T>& x, std::nullptr_t) {
return ! x;
}
/// @relates function_view
template <class T>
bool operator==(std::nullptr_t x, const function_view<T>& y) {
return y == x;
}
/// @relates function_view
template <class T>
bool operator!=(const function_view<T>& x, std::nullptr_t y) {
return ! (x == y);
}
/// @relates function_view
template <class T>
bool operator!=(std::nullptr_t x, const function_view<T>& y) {
return ! (y == x);
}
/// Creates a new function view for `x`.
/// @relates function_view
template <class T>
function_view<T> make_function_view(const T& x) {
return {x};
}
} // namespace caf
#endif // CAF_FUNCTION_VIEW_HPP
...@@ -36,6 +36,9 @@ public: ...@@ -36,6 +36,9 @@ public:
scoped_actor(actor_system& sys, bool hide_actor = false); scoped_actor(actor_system& sys, bool hide_actor = false);
scoped_actor(scoped_actor&&) = default;
scoped_actor& operator=(scoped_actor&&) = default;
~scoped_actor(); ~scoped_actor();
inline blocking_actor* operator->() const { inline blocking_actor* operator->() const {
......
...@@ -205,7 +205,7 @@ class typed_actor : detail::comparable<typed_actor<Sigs...>>, ...@@ -205,7 +205,7 @@ class typed_actor : detail::comparable<typed_actor<Sigs...>>,
} }
/// Exchange content of `*this` and `other`. /// Exchange content of `*this` and `other`.
void swap(actor& other) noexcept { void swap(typed_actor& other) noexcept {
ptr_.swap(other.ptr_); ptr_.swap(other.ptr_);
} }
......
...@@ -51,6 +51,8 @@ scoped_actor::scoped_actor(actor_system& as, bool hide_actor) : context_{&as} { ...@@ -51,6 +51,8 @@ scoped_actor::scoped_actor(actor_system& as, bool hide_actor) : context_{&as} {
scoped_actor::~scoped_actor() { scoped_actor::~scoped_actor() {
CAF_LOG_TRACE(""); CAF_LOG_TRACE("");
if (! self_)
return;
if (self_->is_registered()) { if (self_->is_registered()) {
CAF_SET_AID(prev_); CAF_SET_AID(prev_);
} }
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2015 *
* 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/config.hpp"
#define CAF_SUITE function_view
#include "caf/test/unit_test.hpp"
#include <string>
#include <vector>
#include "caf/all.hpp"
using namespace caf;
namespace {
using calculator = typed_actor<replies_to<int, int>::with<int>>;
calculator::behavior_type adder() {
return {
[](int x, int y) {
return x + y;
}
};
}
calculator::behavior_type multiplier() {
return {
[](int x, int y) {
return x * y;
}
};
}
calculator::behavior_type divider() {
return {
[](int x, int y) -> maybe<int> {
if (y == 0)
return none;
return x / y;
}
};
}
using doubler = typed_actor<replies_to<int>::with<int, int>>;
doubler::behavior_type simple_doubler() {
return {
[](int x) {
return std::make_tuple(x, x);
}
};
}
struct fixture {
actor_system system;
std::vector<actor_addr> testees;
template <class F>
typename infer_handle_from_fun<F>::type spawn(F fun) {
auto result = system.spawn(fun);
testees.emplace_back(result.address());
return result;
}
~fixture() {
for (auto& testee : testees)
anon_send_exit(testee, exit_reason::kill);
}
};
} // namespace <anonymous>
CAF_TEST_FIXTURE_SCOPE(function_view_tests, fixture)
CAF_TEST(empty_function_fiew) {
function_view<calculator> f;
try {
f(10, 20);
CAF_ERROR("line must be unreachable");
}
catch (std::bad_function_call&) {
CAF_CHECK(true);
}
}
CAF_TEST(single_res_function_view) {
auto f = make_function_view(spawn(adder));
CAF_CHECK(f(3, 4) == 7);
CAF_CHECK(f != nullptr);
CAF_CHECK(nullptr != f);
function_view<calculator> g;
g = std::move(f);
CAF_CHECK(f == nullptr);
CAF_CHECK(nullptr == f);
CAF_CHECK(g != nullptr);
CAF_CHECK(nullptr != g);
CAF_CHECK(g(10, 20) == 30);
g.assign(spawn(multiplier));
CAF_CHECK(g(10, 20) == 200);
g.assign(spawn(divider));
try {
g(1, 0);
CAF_ERROR("expected exception");
}
catch (actor_exited&) {
CAF_CHECK(true);
}
CAF_CHECK(g == nullptr);
g.assign(spawn(divider));
CAF_CHECK(g(4, 2) == 2);
}
CAF_TEST(tuple_res_function_view) {
auto f = make_function_view(spawn(simple_doubler));
CAF_CHECK(f(10) == std::make_tuple(10, 10));
}
CAF_TEST_FIXTURE_SCOPE_END()
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