Commit 8b8f785a authored by Dominik Charousset's avatar Dominik Charousset

Add support for move-only behavior functions

parent 63fc9aa4
......@@ -19,26 +19,36 @@
#pragma once
#include <string>
#include <functional>
#include "caf/fwd.hpp"
#include "caf/abstract_channel.hpp"
#include "caf/behavior.hpp"
#include "caf/detail/unique_function.hpp"
#include "caf/fwd.hpp"
#include "caf/input_range.hpp"
#include "caf/abstract_channel.hpp"
namespace caf {
/// Stores spawn-time flags and groups.
class actor_config {
public:
// -- member types -----------------------------------------------------------
using init_fun_type = detail::unique_function<behavior(local_actor*)>;
// -- constructors, destructors, and assignment operators --------------------
explicit actor_config(execution_unit* ptr = nullptr);
// -- member variables -------------------------------------------------------
execution_unit* host;
int flags;
input_range<const group>* groups;
std::function<behavior (local_actor*)> init_fun;
detail::unique_function<behavior(local_actor*)> init_fun;
explicit actor_config(execution_unit* ptr = nullptr);
// -- properties -------------------------------------------------------------
inline actor_config& add_flag(int x) {
actor_config& add_flag(int x) {
flags |= x;
return *this;
}
......@@ -48,4 +58,3 @@ public:
std::string to_string(const actor_config& x);
} // namespace caf
......@@ -147,21 +147,19 @@ actor_factory make_actor_factory(F fun) {
message_verifier<typename trait::arg_types> mv;
if (!mv(msg, tk))
return {};
cfg.init_fun = [=](local_actor* x) -> behavior {
CAF_ASSERT(cfg.host);
cfg.init_fun = actor_config::init_fun_type{[=](local_actor* x) -> behavior {
using ctrait = typename detail::get_callable_trait<F>::type;
using fd = fun_decorator<F, impl, behavior_t, trait::mode,
typename ctrait::result_type,
typename ctrait::arg_types>;
fd f{fun, static_cast<impl*>(x)};
empty_type_erased_tuple dummy_;
auto& ct = msg.empty() ? dummy_
: const_cast<message&>(msg).content();
auto& ct = msg.empty() ? dummy_ : const_cast<message&>(msg).content();
auto opt = ct.apply(f);
if (!opt)
return {};
return std::move(*opt);
};
}};
handle hdl = cfg.host->system().spawn_class<impl, no_spawn_options>(cfg);
return {actor_cast<strong_actor_ptr>(std::move(hdl)),
cfg.host->system().message_types<handle>()};
......
......@@ -19,36 +19,66 @@
#pragma once
#include <tuple>
#include <functional>
#include "caf/fwd.hpp"
#include "caf/detail/apply_args.hpp"
#include "caf/detail/spawn_fwd.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/detail/unique_function.hpp"
namespace caf {
namespace detail {
template <class Base, class F, class ArgsPtr,
bool ReturnsBehavior, bool HasSelfPtr>
class init_fun_factory_helper {
class init_fun_factory_helper_base
: public unique_function<behavior(local_actor*)>::wrapper {
public:
init_fun_factory_helper(init_fun_factory_helper&&) = default;
init_fun_factory_helper(const init_fun_factory_helper&) = default;
// -- member types -----------------------------------------------------------
using super = unique_function<behavior(local_actor*)>::wrapper;
using hook_fun_type = unique_function<void(local_actor*)>;
// -- constructors, destructors, and assignment operators --------------------
using super::super;
// -- properties -------------------------------------------------------------
template <class T>
void hook(T&& x) {
hook_ = hook_fun_type{std::forward<T>(x)};
}
protected:
// -- member variables -------------------------------------------------------
hook_fun_type hook_;
};
/// Wraps a user-defined function and gives it a uniform signature.
template <class Base, class F, class ArgsPtr, bool ReturnsBehavior,
bool HasSelfPtr>
class init_fun_factory_helper final : public init_fun_factory_helper_base {
public:
init_fun_factory_helper(F fun, ArgsPtr args)
: fun_(std::move(fun)),
args_(std::move(args)) {
// nop
}
behavior operator()(local_actor* self) {
init_fun_factory_helper(init_fun_factory_helper&&) = default;
init_fun_factory_helper& operator=(init_fun_factory_helper&&) = default;
behavior operator()(local_actor* self) final {
if (hook_ != nullptr)
hook_(self);
bool_token<ReturnsBehavior> returns_behavior_token;
bool_token<HasSelfPtr> captures_self_token;
return apply(returns_behavior_token, captures_self_token, self);
}
private:
// behavior (pointer)
behavior apply(std::true_type, std::true_type, local_actor* ptr) {
......@@ -83,10 +113,12 @@ private:
template <class Base, class F>
class init_fun_factory {
public:
using fun = std::function<behavior (local_actor*)>;
using ptr_type = std::unique_ptr<init_fun_factory_helper_base>;
using fun = unique_function<behavior(local_actor*)>;
template <class... Ts>
fun operator()(F f, Ts&&... xs) {
ptr_type make(F f, Ts&&... xs) {
static_assert(std::is_base_of<local_actor, Base>::value,
"Given Base does not extend local_actor");
using trait = typename detail::get_callable_trait<F>::type;
......@@ -98,10 +130,15 @@ public:
using tuple_type = decltype(std::make_tuple(detail::spawn_fwd<Ts>(xs)...));
using tuple_ptr = std::shared_ptr<tuple_type>;
using helper = init_fun_factory_helper<Base, F, tuple_ptr, rets, selfptr>;
return helper{std::move(f),
sizeof...(Ts) > 0
? std::make_shared<tuple_type>(detail::spawn_fwd<Ts>(xs)...)
: nullptr};
return ptr_type{new helper{std::move(f), sizeof...(Ts) > 0
? std::make_shared<tuple_type>(
detail::spawn_fwd<Ts>(xs)...)
: nullptr}};
}
template <class... Ts>
fun operator()(F f, Ts&&... xs) {
return fun{make(std::move(f), std::forward<Ts>(xs)...).release()};
}
};
......
......@@ -19,6 +19,7 @@
#pragma once
#include <type_traits>
#include <utility>
namespace caf {
namespace detail {
......@@ -70,6 +71,8 @@ public:
return new impl(std::forward<F>(f));
}
// -- constructors, destructors, and assignment operators --------------------
unique_function() : holds_wrapper_(false), fptr_(nullptr) {
// nop
}
......@@ -104,6 +107,12 @@ public:
// nop
}
~unique_function() {
destroy();
}
// -- assignment -------------------------------------------------------------
unique_function& operator=(unique_function&& other) {
destroy();
if (other.holds_wrapper_) {
......@@ -124,16 +133,16 @@ public:
unique_function& operator=(const unique_function&) = delete;
~unique_function() {
destroy();
void assign(raw_pointer f) {
*this = unique_function{f};
}
R operator()(Ts... xs) {
if (holds_wrapper_)
return (*wptr_)(std::move(xs)...);
return (*fptr_)(std::move(xs)...);
void assign(wrapper_pointer ptr) {
*this = unique_function{ptr};
}
// -- properties -------------------------------------------------------------
bool is_nullptr() const noexcept {
// No type dispatching needed, because the union puts both pointers into
// the same memory location.
......@@ -144,6 +153,14 @@ public:
return holds_wrapper_;
}
// -- operators --------------------------------------------------------------
R operator()(Ts... xs) {
if (holds_wrapper_)
return (*wptr_)(std::move(xs)...);
return (*fptr_)(std::move(xs)...);
}
explicit operator bool() const noexcept {
// No type dispatching needed, because the union puts both pointers into
// the same memory location.
......@@ -155,6 +172,8 @@ public:
}
private:
// -- destruction ------------------------------------------------------------
/// Destroys the managed wrapper.
void destroy() {
if (holds_wrapper_)
......
......@@ -36,6 +36,7 @@
#include "caf/delegated.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/detail/typed_actor_util.hpp"
#include "caf/detail/unique_function.hpp"
#include "caf/duration.hpp"
#include "caf/error.hpp"
#include "caf/fwd.hpp"
......@@ -426,7 +427,7 @@ protected:
message_id last_request_id_;
/// Factory function for returning initial behavior in function-based actors.
std::function<behavior (local_actor*)> initial_behavior_fac_;
detail::unique_function<behavior(local_actor*)> initial_behavior_fac_;
};
} // namespace caf
......
......@@ -101,14 +101,14 @@ public:
// quits after 5 timeouts
actor spawn_event_testee2(scoped_actor& parent) {
struct impl : event_based_actor {
struct wrapper : event_based_actor {
actor parent;
impl(actor_config& cfg, actor parent_actor)
wrapper(actor_config& cfg, actor parent_actor)
: event_based_actor(cfg),
parent(std::move(parent_actor)) {
inc_actor_instances();
}
~impl() override {
~wrapper() override {
dec_actor_instances();
}
behavior wait4timeout(int remaining) {
......@@ -127,7 +127,7 @@ actor spawn_event_testee2(scoped_actor& parent) {
return wait4timeout(5);
}
};
return parent->spawn<impl>(parent);
return parent->spawn<wrapper>(parent);
}
class testee_actor : public blocking_actor {
......@@ -555,7 +555,7 @@ CAF_TEST(kill_the_immortal) {
CAF_TEST(move_only_argument) {
using unique_int = std::unique_ptr<int>;
unique_int uptr{new int(42)};
auto impl = [](event_based_actor* self, unique_int ptr) -> behavior {
auto wrapper = [](event_based_actor* self, unique_int ptr) -> behavior {
auto i = *ptr;
return {
[=](float) {
......@@ -564,8 +564,24 @@ CAF_TEST(move_only_argument) {
}
};
};
auto f = make_function_view(system.spawn(impl, std::move(uptr)));
auto f = make_function_view(system.spawn(wrapper, std::move(uptr)));
CAF_CHECK_EQUAL(to_string(f(1.f)), "(42)");
}
CAF_TEST(move-only function object) {
struct move_only_fun {
move_only_fun() = default;
move_only_fun(const move_only_fun&) = delete;
move_only_fun(move_only_fun&&) = default;
behavior operator()(event_based_actor*) {
return {};
}
};
actor_system_config cfg;
actor_system sys{cfg};
move_only_fun f;
sys.spawn(std::move(f));
}
CAF_TEST_FIXTURE_SCOPE_END()
......@@ -23,12 +23,13 @@
#include <memory>
#include <thread>
#include "caf/actor_system.hpp"
#include "caf/detail/unique_function.hpp"
#include "caf/expected.hpp"
#include "caf/fwd.hpp"
#include "caf/send.hpp"
#include "caf/node_id.hpp"
#include "caf/expected.hpp"
#include "caf/actor_system.hpp"
#include "caf/proxy_registry.hpp"
#include "caf/send.hpp"
#include "caf/io/hook.hpp"
#include "caf/io/broker.hpp"
......@@ -336,11 +337,11 @@ private:
CAF_ASSERT(ptr != nullptr);
detail::init_fun_factory<Impl, F> fac;
actor_config cfg{&backend()};
auto init_fun = fac(std::move(fun), ptr->hdl(), std::forward<Ts>(xs)...);
cfg.init_fun = [ptr, init_fun](local_actor* self) mutable -> behavior {
auto fptr = fac.make(std::move(fun), ptr->hdl(), std::forward<Ts>(xs)...);
fptr->hook([=](local_actor* self) mutable {
static_cast<abstract_broker*>(self)->add_scribe(std::move(ptr));
return init_fun(self);
};
});
cfg.init_fun.assign(fptr.release());
return system().spawn_class<Impl, Os>(cfg);
}
......@@ -352,13 +353,13 @@ private:
return eptr.error();
auto ptr = std::move(*eptr);
detail::init_fun_factory<Impl, F> fac;
auto init_fun = fac(std::move(fun), std::forward<Ts>(xs)...);
port = ptr->port();
actor_config cfg{&backend()};
cfg.init_fun = [ptr, init_fun](local_actor* self) mutable -> behavior {
auto fptr = fac.make(std::move(fun), std::forward<Ts>(xs)...);
fptr->hook([=](local_actor* self) mutable {
static_cast<abstract_broker*>(self)->add_doorman(std::move(ptr));
return init_fun(self);
};
});
actor_config cfg{&backend()};
cfg.init_fun.assign(fptr.release());
return system().spawn_class<Impl, Os>(cfg);
}
......
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