Commit ba12dd6c authored by Dominik Charousset's avatar Dominik Charousset

Merge branch 'issue/805'

Close #805.
parents ec6a8927 8b8f785a
......@@ -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()};
}
};
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* 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. *
******************************************************************************/
#pragma once
#include <type_traits>
#include <utility>
namespace caf {
namespace detail {
/// A move-only replacement for `std::function`.
template <class Signature>
class unique_function;
template <class R, class... Ts>
class unique_function<R (Ts...)> {
public:
// -- member types -----------------------------------------------------------
/// Function object that dispatches application with a virtual member
/// function.
class wrapper {
public:
virtual ~wrapper() {
// nop
}
virtual R operator()(Ts...) = 0;
};
/// Native function pointer.
using raw_pointer = R (*)(Ts...);
/// Pointer to a function wrapper.
using wrapper_pointer = wrapper*;
// -- factory functions ------------------------------------------------------
/// Creates a new function object wrapper.
template <class F>
static wrapper_pointer make_wrapper(F&& f) {
class impl final : public wrapper {
public:
impl(F&& fun) : fun_(std::move(fun)) {
// nop
}
R operator()(Ts... xs) override {
return fun_(xs...);
}
private:
F fun_;
};
return new impl(std::forward<F>(f));
}
// -- constructors, destructors, and assignment operators --------------------
unique_function() : holds_wrapper_(false), fptr_(nullptr) {
// nop
}
unique_function(unique_function&& other)
: holds_wrapper_(other.holds_wrapper_) {
fptr_ = other.fptr_;
if (other.holds_wrapper_)
other.holds_wrapper_ = false;
other.fptr_ = nullptr;
}
unique_function(const unique_function&) = delete;
explicit unique_function(raw_pointer fun)
: holds_wrapper_(false), fptr_(fun) {
// nop
}
explicit unique_function(wrapper_pointer ptr)
: holds_wrapper_(true), wptr_(ptr) {
// nop
}
template <
class T,
class = typename std::enable_if<
!std::is_convertible<T, raw_pointer>::value
&& std::is_same<decltype((std::declval<T&>())(std::declval<Ts>()...)),
R>::value>::type>
explicit unique_function(T f) : unique_function(make_wrapper(std::move(f))) {
// nop
}
~unique_function() {
destroy();
}
// -- assignment -------------------------------------------------------------
unique_function& operator=(unique_function&& other) {
destroy();
if (other.holds_wrapper_) {
holds_wrapper_ = true;
wptr_ = other.wptr_;
other.holds_wrapper_ = false;
other.fptr_ = nullptr;
} else {
holds_wrapper_ = false;
fptr_ = other.fptr_;
}
return *this;
}
unique_function& operator=(raw_pointer f) {
return *this = unique_function{f};
}
unique_function& operator=(const unique_function&) = delete;
void assign(raw_pointer f) {
*this = unique_function{f};
}
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.
return fptr_ == nullptr;
}
bool holds_wrapper() const noexcept {
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.
return !is_nullptr();
}
bool operator!() const noexcept {
return is_nullptr();
}
private:
// -- destruction ------------------------------------------------------------
/// Destroys the managed wrapper.
void destroy() {
if (holds_wrapper_)
delete wptr_;
}
// -- member variables -------------------------------------------------------
bool holds_wrapper_;
union {
raw_pointer fptr_;
wrapper_pointer wptr_;
};
};
template <class T>
bool operator==(const unique_function<T>& x, std::nullptr_t) noexcept {
return x.is_nullptr();
}
template <class T>
bool operator==(std::nullptr_t, const unique_function<T>& x) noexcept {
return x.is_nullptr();
}
template <class T>
bool operator!=(const unique_function<T>& x, std::nullptr_t) noexcept {
return !x.is_nullptr();
}
template <class T>
bool operator!=(std::nullptr_t, const unique_function<T>& x) noexcept {
return !x.is_nullptr();
}
} // namespace detail
} // namespace caf
......@@ -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()
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* 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. *
******************************************************************************/
#define CAF_SUITE unique_function
#include "caf/detail/unique_function.hpp"
#include "caf/test/dsl.hpp"
namespace {
using int_fun = caf::detail::unique_function<int()>;
int fourty_two() {
return 42;
}
class instance_counting_wrapper final: public int_fun::wrapper {
public:
instance_counting_wrapper(size_t* instance_counter)
: instance_counter_(instance_counter) {
*instance_counter_ += 1;
}
~instance_counting_wrapper() {
*instance_counter_ -= 1;
}
int operator()() final {
return 42;
}
private:
size_t* instance_counter_;
};
} // namespace <anonymous>
#define CHECK_VALID(f) \
CAF_CHECK(!f.is_nullptr()); \
CAF_CHECK(f); \
CAF_CHECK(f != nullptr); \
CAF_CHECK(nullptr != f); \
CAF_CHECK(!(f == nullptr)); \
CAF_CHECK(!(nullptr == f)); \
CAF_CHECK(f() == 42)
#define CHECK_INVALID(f) \
CAF_CHECK(f.is_nullptr()); \
CAF_CHECK(!f); \
CAF_CHECK(f == nullptr); \
CAF_CHECK(nullptr == f); \
CAF_CHECK(!(f != nullptr)); \
CAF_CHECK(!(nullptr != f)); \
CAF_CHECK(!f.holds_wrapper())
CAF_TEST(default construction) {
int_fun f;
CHECK_INVALID(f);
}
CAF_TEST(raw function pointer construction) {
int_fun f{fourty_two};
CHECK_VALID(f);
}
CAF_TEST(stateless lambda construction) {
int_fun f{[] { return 42; }};
CHECK_VALID(f);
CAF_CHECK(!f.holds_wrapper());
}
CAF_TEST(stateful lambda construction) {
int i = 42;
int_fun f{[=] { return i; }};
CHECK_VALID(f);
CAF_CHECK(f.holds_wrapper());
}
CAF_TEST(custom wrapper construction) {
size_t instances = 0;
{ // lifetime scope of our counting wrapper
int_fun f{new instance_counting_wrapper(&instances)};
CHECK_VALID(f);
CAF_CHECK(f.holds_wrapper());
CAF_CHECK(instances == 1);
}
CAF_CHECK(instances == 0);
}
CAF_TEST(function move construction) {
int_fun f{fourty_two};
int_fun g{std::move(f)};
CHECK_INVALID(f);
CHECK_VALID(g);
CAF_CHECK(!g.holds_wrapper());
}
CAF_TEST(stateful lambda move construction) {
int i = 42;
int_fun f{[=] { return i; }};
int_fun g{std::move(f)};
CHECK_INVALID(f);
CHECK_VALID(g);
CAF_CHECK(g.holds_wrapper());
}
CAF_TEST(custom wrapper move construction) {
size_t instances = 0;
{ // lifetime scope of our counting wrapper
int_fun f{new instance_counting_wrapper(&instances)};
int_fun g{std::move(f)};
CHECK_INVALID(f);
CHECK_VALID(g);
CAF_CHECK(g.holds_wrapper());
CAF_CHECK(instances == 1);
}
CAF_CHECK(instances == 0);
}
CAF_TEST(function assign) {
size_t instances = 0;
int_fun f;
int_fun g{fourty_two};
int_fun h{new instance_counting_wrapper(&instances)};
CAF_CHECK(instances == 1);
CHECK_INVALID(f);
CHECK_VALID(g);
CHECK_VALID(h);
f = fourty_two;
g = fourty_two;
h = fourty_two;
CAF_CHECK(instances == 0);
CHECK_VALID(f);
CHECK_VALID(g);
CHECK_VALID(h);
}
CAF_TEST(move assign) {
size_t instances = 0;
int_fun f;
int_fun g{fourty_two};
int_fun h{new instance_counting_wrapper(&instances)};
CAF_CHECK(instances == 1);
CHECK_INVALID(f);
CHECK_VALID(g);
CHECK_VALID(h);
g = std::move(h);
CAF_CHECK(instances == 1);
CHECK_INVALID(f);
CHECK_VALID(g);
CHECK_INVALID(h);
f = std::move(g);
CAF_CHECK(instances == 1);
CHECK_VALID(f);
CHECK_INVALID(g);
CHECK_INVALID(h);
f = int_fun{};
CAF_CHECK(instances == 0);
CHECK_INVALID(f);
CHECK_INVALID(g);
CHECK_INVALID(h);
}
......@@ -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