Commit d171858e authored by Dominik Charousset's avatar Dominik Charousset

Allow typed actors to create handles of themselves

parent dae1de64
......@@ -24,6 +24,7 @@
#include "caf/message_id.hpp"
#include "caf/local_actor.hpp"
#include "caf/actor_marker.hpp"
#include "caf/typed_behavior.hpp"
#include "caf/behavior_policy.hpp"
#include "caf/response_handle.hpp"
......@@ -37,7 +38,8 @@ namespace caf {
/// @tparam HasSyncSend Configures whether this base extends `sync_sender`.
/// @tparam Base Either `local_actor` (default) or a subtype thereof.
template <class BehaviorType, bool HasSyncSend, class Base = local_actor>
class abstract_event_based_actor : public Base {
class abstract_event_based_actor : public Base,
public actor_marker<BehaviorType>::type {
public:
using behavior_type = BehaviorType;
......
......@@ -28,6 +28,7 @@
#include "caf/intrusive_ptr.hpp"
#include "caf/fwd.hpp"
#include "caf/actor_marker.hpp"
#include "caf/abstract_actor.hpp"
#include "caf/detail/comparable.hpp"
......@@ -50,9 +51,11 @@ constexpr invalid_actor_t invalid_actor = invalid_actor_t{};
template <class T>
struct is_convertible_to_actor {
using type = typename std::remove_pointer<T>::type;
static constexpr bool value = std::is_base_of<actor_proxy, type>::value
static constexpr bool value =
! std::is_base_of<statically_typed_actor_base, type>::value
&& (std::is_base_of<actor_proxy, type>::value
|| std::is_base_of<local_actor, type>::value
|| std::is_same<scoped_actor, type>::value;
|| std::is_same<scoped_actor, type>::value);
};
/// Identifies an untyped actor. Can be used with derived types
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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_ACTOR_MARKER_HPP
#define CAF_ACTOR_MARKER_HPP
#include "caf/fwd.hpp"
namespace caf {
class statically_typed_actor_base {
// used as marker only
};
class dynamically_typed_actor_base {
// used as marker only
};
template <class T>
struct actor_marker {
using type = statically_typed_actor_base;
};
template <>
struct actor_marker<behavior> {
using type = dynamically_typed_actor_base;
};
} // namespace caf
#endif // CAF_ACTOR_MARKER_HPP
......@@ -380,6 +380,7 @@ make_message(V&& x, Ts&&... xs) {
typename unbox_message_element<
typename detail::strip_and_convert<Ts>::type
>::type...>;
auto ptr = make_counted<storage>(std::forward<V>(x), std::forward<Ts>(xs)...);
return message{detail::message_data::cow_ptr{std::move(ptr)}};
}
......
......@@ -129,6 +129,17 @@ class typed_actor : detail::comparable<typed_actor<Sigs...>>,
// nop
}
// allow `handle_type{this}` for typed actors
template <class TypedActor,
class Enable =
typename std::enable_if<
detail::tlf_is_subset(signatures(),
typename TypedActor::signatures())
>::type>
typed_actor(TypedActor* ptr) : ptr_(ptr) {
// nop
}
template <class TypedActor,
class Enable =
typename std::enable_if<
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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 announce_actor_type
#include "caf/test/unit_test.hpp"
#include "caf/all.hpp"
#include "caf/experimental/announce_actor_type.hpp"
#include "caf/detail/actor_registry.hpp"
using namespace caf;
using namespace caf::experimental;
using std::endl;
namespace {
struct fixture {
actor aut;
actor spawner;
fixture() {
auto registry = detail::singletons::get_actor_registry();
spawner = registry->get_named(atom("spawner"));
}
void set_aut(message args, bool expect_fail = false) {
CAF_MESSAGE("set aut");
scoped_actor self;
self->on_sync_failure([&] {
CAF_TEST_ERROR("received unexpeced sync. response: "
<< to_string(self->current_message()));
});
if (expect_fail) {
self->sync_send(spawner, get_atom::value, "test_actor", std::move(args)).await(
[&](error_atom, const std::string&) {
CAF_TEST_VERBOSE("received error_atom (expected)");
}
);
} else {
self->sync_send(spawner, get_atom::value, "test_actor", std::move(args)).await(
[&](ok_atom, actor_addr res, const std::set<std::string>& ifs) {
CAF_REQUIRE(res != invalid_actor_addr);
aut = actor_cast<actor>(res);
CAF_CHECK(ifs.empty());
}
);
}
}
~fixture() {
if (aut != invalid_actor) {
scoped_actor self;
self->monitor(aut);
self->receive(
[](const down_msg& dm) {
CAF_CHECK(dm.reason == exit_reason::normal);
}
);
}
await_all_actors_done();
shutdown();
}
};
using detail::is_serializable;
// not serializable
class testee1 {
};
// serializable via member function
class testee2 {
public:
template <class Archive>
void serialize(Archive&, const unsigned int) {
// nop
}
};
// serializable via free function
class testee3 {
// nop
};
template<class Archive>
void serialize(Archive&, testee3&, const unsigned int) {
// nop
}
template <class Archive, class T>
void serialize(Archive& ar, T& x, const unsigned int version,
decltype(x.serialize(ar, version))* = nullptr) {
x.serialize(ar, version);
}
struct migratable_state {
int value = 0;
};
template <class Archive>
void serialize(Archive& ar, migratable_state& x, const unsigned int) {
ar & x.value;
}
struct migratable_actor : stateful_actor<migratable_state> {
behavior make_behavior() override {
return {
[=](get_atom) {
return state.value;
},
[=](put_atom, int value) {
state.value = value;
},
[=](sys_atom, migrate_atom, actor_addr destination) {
std::vector<char> buf;
binary_serializer bs{std::back_inserter(buf)};
save(bs, 0);
actor dest = actor_cast<actor>(destination);
link_to(dest);
sync_send(dest, sys_atom::value, migrate_atom::value, std::move(buf)).then(
[=](ok_atom) {
// "decay" into a proxy for `dest`
become(
[=](sys_atom, migrate_atom, std::vector<char> buf) {
unlink_from(dest);
become(make_behavior());
binary_deserializer bd{buf.data(), buf.size()};
deserialize(bd, 0);
return make_message(ok_atom::value);
},
others >> [=] {
forward_to(dest);
}
);
},
others >> [=] {
// do nothing, i.e., process further messages as usual
}
);
},
[=](sys_atom, migrate_atom, std::vector<char> buf) {
binary_deserializer bd{buf.data(), buf.size()};
deserialize(bd, 0);
return make_message(ok_atom::value);
}
};
}
};
} // namespace <anonymous>
CAF_TEST(serializable_stuff) {
CAF_CHECK(is_serializable<testee1>::value == false);
CAF_CHECK(is_serializable<testee2>::value == true);
CAF_CHECK(is_serializable<testee3>::value == true);
}
CAF_TEST(migratable_stuff) {
auto a = spawn<migratable_actor>();
auto b = spawn<migratable_actor>();
scoped_actor self;
self->send(a, put_atom::value, 42);
self->send(a, sys_atom::value, migrate_atom::value, b.address());
self->sync_send(a, get_atom::value).await(
[&](int result) {
CAF_CHECK(result == 42);
CAF_CHECK(self->current_sender() == b.address());
}
);
self->send_exit(b, exit_reason::kill);
self->await_all_other_actors_done();
}
CAF_TEST_FIXTURE_SCOPE(announce_actor_type_tests, fixture)
CAF_TEST(fun_no_args) {
auto test_actor = [] {
CAF_MESSAGE("inside test_actor");
};
announce_actor_type("test_actor", test_actor);
set_aut(make_message());
}
CAF_TEST(fun_no_args_selfptr) {
auto test_actor = [](event_based_actor*) {
CAF_MESSAGE("inside test_actor");
};
announce_actor_type("test_actor", test_actor);
set_aut(make_message());
}
CAF_TEST(fun_one_arg) {
auto test_actor = [](int i) {
CAF_CHECK_EQUAL(i, 42);
};
announce_actor_type("test_actor", test_actor);
set_aut(make_message(42));
}
CAF_TEST(fun_one_arg_selfptr) {
auto test_actor = [](event_based_actor*, int i) {
CAF_CHECK_EQUAL(i, 42);
};
announce_actor_type("test_actor", test_actor);
set_aut(make_message(42));
}
CAF_TEST(class_no_arg) {
struct test_actor : event_based_actor {
behavior make_behavior() override {
return {};
}
};
announce_actor_type<test_actor>("test_actor");
set_aut(make_message(42), true);
set_aut(make_message());
}
CAF_TEST(class_one_arg) {
struct test_actor : event_based_actor {
test_actor(int value) {
CAF_CHECK_EQUAL(value, 42);
}
behavior make_behavior() override {
return {};
}
};
announce_actor_type<test_actor, const int&>("test_actor");
set_aut(make_message(), true);
set_aut(make_message(42));
}
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