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

Add support for move-only behavior functions

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