Commit 4d53aa0d authored by Dominik Charousset's avatar Dominik Charousset

Define actor interfaces using function signatures

The implementation of `replies_to<...>::with<...>` introduced
unnecessary complexity and metaprogramming utilities. Using plain old
function signatures instead makes working with actor interfaces a bit
easier internally and confronts users with less `detail` types should
they face compiler errors related to `typed_actor`.
parent d5c0956f
...@@ -97,7 +97,13 @@ is based on [Keep a Changelog](https://keepachangelog.com). ...@@ -97,7 +97,13 @@ is based on [Keep a Changelog](https://keepachangelog.com).
typed behavior always must provide all message handlers (typed behavior assume typed behavior always must provide all message handlers (typed behavior assume
a complete implementation of the interface). This use case received direct a complete implementation of the interface). This use case received direct
support: constructing a typed behavior with `partial_behavior_init` as first support: constructing a typed behavior with `partial_behavior_init` as first
argument suppresses the check for completeness . argument suppresses the check for completeness.
- In order to reduce complexity of typed actors, CAF defines interfaces as a set
of function signatures rather than using custom metaprogramming facilities.
Function signatures *must* always wrap the return type in a `result<T>`. For
example: `typed_actor<result<double>(double)>`. We have reimplemented the
metaprogramming facilities `racts_to<...>` and `replies_to<...>::with<...>`
as an alternative way of writing the function signature.
### Removed ### Removed
......
...@@ -34,8 +34,8 @@ namespace { ...@@ -34,8 +34,8 @@ namespace {
// atoms for chopstick and philosopher interfaces // atoms for chopstick and philosopher interfaces
// a chopstick // a chopstick
using chopstick = typed_actor<replies_to<take_atom>::with<taken_atom, bool>, using chopstick
reacts_to<put_atom>>; = typed_actor<result<taken_atom, bool>(take_atom), result<void>(put_atom)>;
chopstick::behavior_type taken_chopstick(chopstick::pointer, chopstick::behavior_type taken_chopstick(chopstick::pointer,
const strong_actor_ptr&); const strong_actor_ptr&);
......
...@@ -14,8 +14,9 @@ using namespace caf; ...@@ -14,8 +14,9 @@ using namespace caf;
namespace { namespace {
using calculator_actor = typed_actor<replies_to<add_atom, int, int>::with<int>, using calculator_actor
replies_to<sub_atom, int, int>::with<int>>; = typed_actor<result<int32_t>(add_atom, int32_t, int32_t),
result<int32_t>(sub_atom, int32_t, int32_t)>;
// prototypes and forward declarations // prototypes and forward declarations
behavior calculator_fun(event_based_actor* self); behavior calculator_fun(event_based_actor* self);
...@@ -28,8 +29,8 @@ class typed_calculator; ...@@ -28,8 +29,8 @@ class typed_calculator;
// function-based, dynamically typed, event-based API // function-based, dynamically typed, event-based API
behavior calculator_fun(event_based_actor*) { behavior calculator_fun(event_based_actor*) {
return { return {
[](add_atom, int a, int b) { return a + b; }, [](add_atom, int32_t a, int32_t b) { return a + b; },
[](sub_atom, int a, int b) { return a - b; }, [](sub_atom, int32_t a, int32_t b) { return a - b; },
}; };
} }
...@@ -37,8 +38,8 @@ behavior calculator_fun(event_based_actor*) { ...@@ -37,8 +38,8 @@ behavior calculator_fun(event_based_actor*) {
void blocking_calculator_fun(blocking_actor* self) { void blocking_calculator_fun(blocking_actor* self) {
bool running = true; bool running = true;
self->receive_while(running)( // self->receive_while(running)( //
[](add_atom, int a, int b) { return a + b; }, [](add_atom, int32_t a, int32_t b) { return a + b; },
[](sub_atom, int a, int b) { return a - b; }, [](sub_atom, int32_t a, int32_t b) { return a - b; },
[&](exit_msg& em) { [&](exit_msg& em) {
if (em.reason) { if (em.reason) {
self->fail_state(std::move(em.reason)); self->fail_state(std::move(em.reason));
...@@ -50,8 +51,8 @@ void blocking_calculator_fun(blocking_actor* self) { ...@@ -50,8 +51,8 @@ void blocking_calculator_fun(blocking_actor* self) {
// function-based, statically typed, event-based API // function-based, statically typed, event-based API
calculator_actor::behavior_type typed_calculator_fun() { calculator_actor::behavior_type typed_calculator_fun() {
return { return {
[](add_atom, int a, int b) { return a + b; }, [](add_atom, int32_t a, int32_t b) { return a + b; },
[](sub_atom, int a, int b) { return a - b; }, [](sub_atom, int32_t a, int32_t b) { return a - b; },
}; };
} }
...@@ -97,7 +98,8 @@ void tester(scoped_actor&) { ...@@ -97,7 +98,8 @@ void tester(scoped_actor&) {
// tests a calculator instance // tests a calculator instance
template <class Handle, class... Ts> template <class Handle, class... Ts>
void tester(scoped_actor& self, const Handle& hdl, int x, int y, Ts&&... xs) { void tester(scoped_actor& self, const Handle& hdl, int32_t x, int32_t y,
Ts&&... xs) {
auto handle_err = [&](const error& err) { auto handle_err = [&](const error& err) {
aout(self) << "AUT (actor under test) failed: " aout(self) << "AUT (actor under test) failed: "
<< self->system().render(err) << endl; << self->system().render(err) << endl;
...@@ -105,12 +107,12 @@ void tester(scoped_actor& self, const Handle& hdl, int x, int y, Ts&&... xs) { ...@@ -105,12 +107,12 @@ void tester(scoped_actor& self, const Handle& hdl, int x, int y, Ts&&... xs) {
// first test: x + y = z // first test: x + y = z
self->request(hdl, infinite, add_atom_v, x, y) self->request(hdl, infinite, add_atom_v, x, y)
.receive( .receive(
[&](int res1) { [&](int32_t res1) {
aout(self) << x << " + " << y << " = " << res1 << endl; aout(self) << x << " + " << y << " = " << res1 << endl;
// second test: x - y = z // second test: x - y = z
self->request(hdl, infinite, sub_atom_v, x, y) self->request(hdl, infinite, sub_atom_v, x, y)
.receive( .receive(
[&](int res2) { [&](int32_t res2) {
aout(self) << x << " - " << y << " = " << res2 << endl; aout(self) << x << " - " << y << " = " << res2 << endl;
}, },
handle_err); handle_err);
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
// without updating the references in the *.tex files! // without updating the references in the *.tex files!
// Manual references: lines 18-44, and 49-50 (Actor.tex) // Manual references: lines 18-44, and 49-50 (Actor.tex)
#include <cstdint>
#include <iostream> #include <iostream>
#include "caf/all.hpp" #include "caf/all.hpp"
...@@ -15,33 +16,21 @@ using std::cout; ...@@ -15,33 +16,21 @@ using std::cout;
using std::endl; using std::endl;
using namespace caf; using namespace caf;
using cell = typed_actor<reacts_to<put_atom, int>, using cell
replies_to<get_atom>::with<int>>; = typed_actor<result<void>(put_atom, int32_t), result<int32_t>(get_atom)>;
struct cell_state { struct cell_state {
int value = 0; int32_t value = 0;
}; };
cell::behavior_type type_checked_cell(cell::stateful_pointer<cell_state> self) { cell::behavior_type type_checked_cell(cell::stateful_pointer<cell_state> self) {
return { return {[=](put_atom, int32_t val) { self->state.value = val; },
[=](put_atom, int val) { [=](get_atom) { return self->state.value; }};
self->state.value = val;
},
[=](get_atom) {
return self->state.value;
}
};
} }
behavior unchecked_cell(stateful_actor<cell_state>* self) { behavior unchecked_cell(stateful_actor<cell_state>* self) {
return { return {[=](put_atom, int32_t val) { self->state.value = val; },
[=](put_atom, int val) { [=](get_atom) { return self->state.value; }};
self->state.value = val;
},
[=](get_atom) {
return self->state.value;
}
};
} }
void caf_main(actor_system& system) { void caf_main(actor_system& system) {
......
...@@ -10,16 +10,18 @@ using namespace caf; ...@@ -10,16 +10,18 @@ using namespace caf;
// using add_atom = atom_constant<atom("add")>; (defined in atom.hpp) // using add_atom = atom_constant<atom("add")>; (defined in atom.hpp)
using calc = typed_actor<replies_to<add_atom, int, int>::with<int>>; using calc = typed_actor<result<int32_t>(add_atom, int32_t, int32_t)>;
void actor_a(event_based_actor* self, const calc& worker) { void actor_a(event_based_actor* self, const calc& worker) {
self->request(worker, std::chrono::seconds(10), add_atom_v, 1, 2) self->request(worker, std::chrono::seconds(10), add_atom_v, 1, 2)
.then([=](int result) { aout(self) << "1 + 2 = " << result << std::endl; }); .then([=](int32_t result) { //
aout(self) << "1 + 2 = " << result << std::endl;
});
} }
calc::behavior_type actor_b(calc::pointer self, const calc& worker) { calc::behavior_type actor_b(calc::pointer self, const calc& worker) {
return { return {
[=](add_atom add, int x, int y) { [=](add_atom add, int32_t x, int32_t y) {
return self->delegate(worker, add, x, y); return self->delegate(worker, add, x, y);
}, },
}; };
...@@ -27,7 +29,7 @@ calc::behavior_type actor_b(calc::pointer self, const calc& worker) { ...@@ -27,7 +29,7 @@ calc::behavior_type actor_b(calc::pointer self, const calc& worker) {
calc::behavior_type actor_c() { calc::behavior_type actor_c() {
return { return {
[](add_atom, int x, int y) { return x + y; }, [](add_atom, int32_t x, int32_t y) { return x + y; },
}; };
} }
......
...@@ -46,7 +46,7 @@ public: ...@@ -46,7 +46,7 @@ public:
} }
}; };
using divider = typed_actor<replies_to<div_atom, double, double>::with<double>>; using divider = typed_actor<result<double>(div_atom, double, double)>;
divider::behavior_type divider_impl() { divider::behavior_type divider_impl() {
return { return {
......
...@@ -36,20 +36,20 @@ using namespace caf; ...@@ -36,20 +36,20 @@ using namespace caf;
/// A simple actor for storing an integer value. /// A simple actor for storing an integer value.
using cell = typed_actor< using cell = typed_actor<
// Writes a new value. // Writes a new value.
reacts_to<put_atom, int>, result<void>(put_atom, int),
// Reads the value. // Reads the value.
replies_to<get_atom>::with<int>>; result<int>(get_atom)>;
/// An for storing a 2-dimensional matrix of integers. /// An for storing a 2-dimensional matrix of integers.
using matrix = typed_actor< using matrix = typed_actor<
// Writes a new value to given cell (x-coordinate, y-coordinate, new-value). // Writes a new value to given cell (x-coordinate, y-coordinate, new-value).
reacts_to<put_atom, int, int, int>, result<void>(put_atom, int, int, int),
// Reads from given cell. // Reads from given cell.
replies_to<get_atom, int, int>::with<int>, result<int>(get_atom, int, int),
// Computes the average for given row. // Computes the average for given row.
replies_to<get_atom, average_atom, row_atom, int>::with<double>, result<double>(get_atom, average_atom, row_atom, int),
// Computes the average for given column. // Computes the average for given column.
replies_to<get_atom, average_atom, column_atom, int>::with<double>>; result<double>(get_atom, average_atom, column_atom, int)>;
struct cell_state { struct cell_state {
int value = 0; int value = 0;
......
...@@ -15,16 +15,12 @@ using std::endl; ...@@ -15,16 +15,12 @@ using std::endl;
using namespace caf; using namespace caf;
// using add_atom = atom_constant<atom("add")>; (defined in atom.hpp) using adder = typed_actor<result<int32_t>(add_atom, int32_t, int32_t)>;
using adder = typed_actor<replies_to<add_atom, int, int>::with<int>>;
// function-based, statically typed, event-based API // function-based, statically typed, event-based API
adder::behavior_type worker() { adder::behavior_type worker() {
return { return {
[](add_atom, int a, int b) { [](add_atom, int32_t a, int32_t b) { return a + b; },
return a + b;
}
}; };
} }
...@@ -32,13 +28,13 @@ adder::behavior_type worker() { ...@@ -32,13 +28,13 @@ adder::behavior_type worker() {
adder::behavior_type calculator_master(adder::pointer self) { adder::behavior_type calculator_master(adder::pointer self) {
auto w = self->spawn(worker); auto w = self->spawn(worker);
return { return {
[=](add_atom x, int y, int z) -> result<int> { [=](add_atom x, int32_t y, int32_t z) -> result<int32_t> {
auto rp = self->make_response_promise<int>(); auto rp = self->make_response_promise<int32_t>();
self->request(w, infinite, x, y, z).then([=](int result) mutable { self->request(w, infinite, x, y, z).then([=](int32_t result) mutable {
rp.deliver(result); rp.deliver(result);
}); });
return rp; return rp;
} },
}; };
} }
......
...@@ -6,9 +6,10 @@ ...@@ -6,9 +6,10 @@
// without updating the references in the *.tex files! // without updating the references in the *.tex files!
// Manual references: lines 20-37, 39-51, 53-64, 67-69 (MessagePassing.tex) // Manual references: lines 20-37, 39-51, 53-64, 67-69 (MessagePassing.tex)
#include <vector>
#include <chrono> #include <chrono>
#include <cstdint>
#include <iostream> #include <iostream>
#include <vector>
#include "caf/all.hpp" #include "caf/all.hpp"
...@@ -17,35 +18,32 @@ using std::vector; ...@@ -17,35 +18,32 @@ using std::vector;
using std::chrono::seconds; using std::chrono::seconds;
using namespace caf; using namespace caf;
using cell = typed_actor<reacts_to<put_atom, int>, using cell
replies_to<get_atom>::with<int>>; = typed_actor<result<void>(put_atom, int32_t), result<int32_t>(get_atom)>;
struct cell_state { struct cell_state {
int value = 0; int32_t value = 0;
}; };
cell::behavior_type cell_impl(cell::stateful_pointer<cell_state> self, int x0) { cell::behavior_type cell_impl(cell::stateful_pointer<cell_state> self,
int32_t x0) {
self->state.value = x0; self->state.value = x0;
return { return {
[=](put_atom, int val) { [=](put_atom, int32_t val) { self->state.value = val; },
self->state.value = val; [=](get_atom) { return self->state.value; },
},
[=](get_atom) {
return self->state.value;
}
}; };
} }
void waiting_testee(event_based_actor* self, vector<cell> cells) { void waiting_testee(event_based_actor* self, vector<cell> cells) {
for (auto& x : cells) for (auto& x : cells)
self->request(x, seconds(1), get_atom_v).await([=](int y) { self->request(x, seconds(1), get_atom_v).await([=](int32_t y) {
aout(self) << "cell #" << x.id() << " -> " << y << endl; aout(self) << "cell #" << x.id() << " -> " << y << endl;
}); });
} }
void multiplexed_testee(event_based_actor* self, vector<cell> cells) { void multiplexed_testee(event_based_actor* self, vector<cell> cells) {
for (auto& x : cells) for (auto& x : cells)
self->request(x, seconds(1), get_atom_v).then([=](int y) { self->request(x, seconds(1), get_atom_v).then([=](int32_t y) {
aout(self) << "cell #" << x.id() << " -> " << y << endl; aout(self) << "cell #" << x.id() << " -> " << y << endl;
}); });
} }
...@@ -54,7 +52,9 @@ void blocking_testee(blocking_actor* self, vector<cell> cells) { ...@@ -54,7 +52,9 @@ void blocking_testee(blocking_actor* self, vector<cell> cells) {
for (auto& x : cells) for (auto& x : cells)
self->request(x, seconds(1), get_atom_v) self->request(x, seconds(1), get_atom_v)
.receive( .receive(
[&](int y) { aout(self) << "cell #" << x.id() << " -> " << y << endl; }, [&](int32_t y) {
aout(self) << "cell #" << x.id() << " -> " << y << endl;
},
[&](error& err) { [&](error& err) {
aout(self) << "cell #" << x.id() << " -> " aout(self) << "cell #" << x.id() << " -> "
<< self->system().render(err) << endl; << self->system().render(err) << endl;
......
...@@ -12,13 +12,14 @@ using namespace caf; ...@@ -12,13 +12,14 @@ using namespace caf;
namespace { namespace {
using calculator_type = typed_actor<replies_to<add_atom, int, int>::with<int>, using calculator_type
replies_to<sub_atom, int, int>::with<int>>; = typed_actor<result<int32_t>(add_atom, int32_t, int32_t),
result<int32_t>(sub_atom, int32_t, int32_t)>;
calculator_type::behavior_type typed_calculator_fun(calculator_type::pointer) { calculator_type::behavior_type typed_calculator_fun(calculator_type::pointer) {
return { return {
[](add_atom, int x, int y) { return x + y; }, [](add_atom, int32_t x, int32_t y) -> int32_t { return x + y; },
[](sub_atom, int x, int y) { return x - y; }, [](sub_atom, int32_t x, int32_t y) -> int32_t { return x - y; },
}; };
} }
...@@ -31,8 +32,8 @@ public: ...@@ -31,8 +32,8 @@ public:
protected: protected:
behavior_type make_behavior() override { behavior_type make_behavior() override {
return { return {
[](add_atom, int x, int y) { return x + y; }, [](add_atom, int32_t x, int32_t y) -> int32_t { return x + y; },
[](sub_atom, int x, int y) { return x - y; }, [](sub_atom, int32_t x, int32_t y) -> int32_t { return x - y; },
}; };
} }
}; };
...@@ -42,9 +43,9 @@ void tester(event_based_actor* self, const calculator_type& testee) { ...@@ -42,9 +43,9 @@ void tester(event_based_actor* self, const calculator_type& testee) {
// first test: 2 + 1 = 3 // first test: 2 + 1 = 3
self->request(testee, infinite, add_atom_v, 2, 1) self->request(testee, infinite, add_atom_v, 2, 1)
.then( .then(
[=](int r1) { [=](int32_t r1) {
// second test: 2 - 1 = 1 // second test: 2 - 1 = 1
self->request(testee, infinite, sub_atom_v, 2, 1).then([=](int r2) { self->request(testee, infinite, sub_atom_v, 2, 1).then([=](int32_t r2) {
// both tests succeeded // both tests succeeded
if (r1 == 3 && r2 == 1) { if (r1 == 3 && r2 == 1) {
aout(self) << "AUT (actor under test) seems to be ok" << endl; aout(self) << "AUT (actor under test) seems to be ok" << endl;
......
...@@ -20,8 +20,8 @@ ...@@ -20,8 +20,8 @@
// --(rst-calculator-begin)-- // --(rst-calculator-begin)--
using calculator using calculator
= caf::typed_actor<caf::replies_to<caf::add_atom, int, int>::with<int>, = caf::typed_actor<caf::result<int32_t>(caf::add_atom, int32_t, int32_t),
caf::replies_to<caf::sub_atom, int, int>::with<int>>; caf::result<int32_t>(caf::sub_atom, int32_t, int32_t)>;
// --(rst-calculator-end)-- // --(rst-calculator-end)--
CAF_BEGIN_TYPE_ID_BLOCK(remote_spawn, first_custom_type_id) CAF_BEGIN_TYPE_ID_BLOCK(remote_spawn, first_custom_type_id)
...@@ -39,11 +39,11 @@ using namespace caf; ...@@ -39,11 +39,11 @@ using namespace caf;
calculator::behavior_type calculator_fun(calculator::pointer self) { calculator::behavior_type calculator_fun(calculator::pointer self) {
return { return {
[=](add_atom, int a, int b) -> int { [=](add_atom, int32_t a, int32_t b) {
aout(self) << "received task from a remote node" << endl; aout(self) << "received task from a remote node" << endl;
return a + b; return a + b;
}, },
[=](sub_atom, int a, int b) -> int { [=](sub_atom, int32_t a, int32_t b) {
aout(self) << "received task from a remote node" << endl; aout(self) << "received task from a remote node" << endl;
return a - b; return a - b;
}, },
...@@ -65,8 +65,8 @@ void client_repl(function_view<calculator> f) { ...@@ -65,8 +65,8 @@ void client_repl(function_view<calculator> f) {
auto usage = [] { auto usage = [] {
cout << "Usage:" << endl cout << "Usage:" << endl
<< " quit : terminate program" << endl << " quit : terminate program" << endl
<< " <x> + <y> : adds two integers" << endl << " <x> + <y> : adds two int32_tegers" << endl
<< " <x> - <y> : subtracts two integers" << endl << " <x> - <y> : subtracts two int32_tegers" << endl
<< endl; << endl;
}; };
usage(); usage();
...@@ -81,15 +81,15 @@ void client_repl(function_view<calculator> f) { ...@@ -81,15 +81,15 @@ void client_repl(function_view<calculator> f) {
usage(); usage();
continue; continue;
} }
auto to_int = [](const string& str) -> optional<int> { auto to_int32_t = [](const string& str) -> optional<int32_t> {
char* end = nullptr; char* end = nullptr;
auto res = strtol(str.c_str(), &end, 10); auto res = strtol(str.c_str(), &end, 10);
if (end == str.c_str() + str.size()) if (end == str.c_str() + str.size())
return static_cast<int>(res); return static_cast<int32_t>(res);
return none; return none;
}; };
auto x = to_int(words[0]); auto x = to_int32_t(words[0]);
auto y = to_int(words[2]); auto y = to_int32_t(words[2]);
if (!x || !y || (words[1] != "+" && words[1] != "-")) if (!x || !y || (words[1] != "+" && words[1] != "-"))
usage(); usage();
else else
......
...@@ -56,18 +56,31 @@ namespace caf::detail { ...@@ -56,18 +56,31 @@ namespace caf::detail {
template <class> template <class>
struct typed_mpi_access; struct typed_mpi_access;
template <class... In, class... Out> template <class Out, class... In>
struct typed_mpi_access<typed_mpi<type_list<In...>, output_tuple<Out...>>> { struct typed_mpi_access<Out(In...)> {
std::string operator()() const {
static_assert(sizeof...(In) > 0, "typed MPI without inputs");
std::vector<std::string> inputs{type_name_v<In>...};
std::string result = "(";
result += join(inputs, ",");
result += ") -> ";
result += type_name_v<Out>;
return result;
}
};
template <class... Out, class... In>
struct typed_mpi_access<result<Out...>(In...)> {
std::string operator()() const { std::string operator()() const {
static_assert(sizeof...(In) > 0, "typed MPI without inputs"); static_assert(sizeof...(In) > 0, "typed MPI without inputs");
static_assert(sizeof...(Out) > 0, "typed MPI without outputs"); static_assert(sizeof...(Out) > 0, "typed MPI without outputs");
std::vector<std::string> inputs{type_name_v<In>...}; std::vector<std::string> inputs{type_name_v<In>...};
std::vector<std::string> outputs1{type_name_v<Out>...}; std::vector<std::string> outputs1{type_name_v<Out>...};
std::string result = "caf::replies_to<"; std::string result = "(";
result += join(inputs, ","); result += join(inputs, ",");
result += ">::with<"; result += ") -> (";
result += join(outputs1, ","); result += join(outputs1, ",");
result += ">"; result += ")";
return result; return result;
} }
}; };
......
...@@ -25,16 +25,6 @@ ...@@ -25,16 +25,6 @@
namespace caf { namespace caf {
template <class T>
struct output_types_of {
// nop
};
template <class In, class Out>
struct output_types_of<typed_mpi<In, Out>> {
using type = Out;
};
template <class T> template <class T>
struct signatures_of { struct signatures_of {
using type = typename std::remove_pointer<T>::type::signatures; using type = typename std::remove_pointer<T>::type::signatures;
...@@ -52,6 +42,9 @@ constexpr bool statically_typed() { ...@@ -52,6 +42,9 @@ constexpr bool statically_typed() {
template <class T> template <class T>
struct is_void_response : std::false_type {}; struct is_void_response : std::false_type {};
template <>
struct is_void_response<void> : std::true_type {};
template <> template <>
struct is_void_response<detail::type_list<void>> : std::true_type {}; struct is_void_response<detail::type_list<void>> : std::true_type {};
......
...@@ -56,33 +56,35 @@ struct composed_type<detail::type_list<X, Xs...>, Ys, detail::type_list<>, Rs> ...@@ -56,33 +56,35 @@ struct composed_type<detail::type_list<X, Xs...>, Ys, detail::type_list<>, Rs>
: composed_type<detail::type_list<Xs...>, Ys, Ys, Rs> {}; : composed_type<detail::type_list<Xs...>, Ys, Ys, Rs> {};
// Output type matches the input type of the next actor. // Output type matches the input type of the next actor.
template <class... In, class... Out, class... Xs, class Ys, class... MapsTo, template <class Out, class... In, class... Xs, class Ys, class MapsTo,
class... Zs, class... Rs> class... Zs, class... Rs>
struct composed_type< struct composed_type<detail::type_list<Out(In...), Xs...>, Ys,
detail::type_list<typed_mpi<detail::type_list<In...>, output_tuple<Out...>>, detail::type_list<MapsTo(Out), Zs...>,
Xs...>, detail::type_list<Rs...>>
Ys, : composed_type<detail::type_list<Xs...>, Ys, Ys,
detail::type_list< detail::type_list<Rs..., MapsTo(In...)>> {};
typed_mpi<detail::type_list<Out...>, output_tuple<MapsTo...>>, Zs...>,
detail::type_list<Rs...>> // Output type matches the input type of the next actor.
: composed_type< template <class... Out, class... In, class... Xs, class Ys, class MapsTo,
detail::type_list<Xs...>, Ys, Ys, class... Zs, class... Rs>
detail::type_list< struct composed_type<detail::type_list<result<Out...>(In...), Xs...>, Ys,
Rs..., typed_mpi<detail::type_list<In...>, output_tuple<MapsTo...>>>> { detail::type_list<MapsTo(Out...), Zs...>,
}; detail::type_list<Rs...>>
: composed_type<detail::type_list<Xs...>, Ys, Ys,
detail::type_list<Rs..., MapsTo(In...)>> {};
// No match, recurse over Zs. // No match, recurse over Zs.
template <class In, class Out, class... Xs, class Ys, class Unrelated, template <class Out, class... In, class... Xs, class Ys, class MapsTo,
class MapsTo, class... Zs, class Rs> class... Unrelated, class... Zs, class Rs>
struct composed_type<detail::type_list<typed_mpi<In, Out>, Xs...>, Ys, struct composed_type<detail::type_list<Out(In...), Xs...>, Ys,
detail::type_list<typed_mpi<Unrelated, MapsTo>, Zs...>, Rs> detail::type_list<MapsTo(Unrelated...), Zs...>, Rs>
: composed_type<detail::type_list<typed_mpi<In, Out>, Xs...>, Ys, : composed_type<detail::type_list<Out(In...), Xs...>, Ys,
detail::type_list<Zs...>, Rs> {}; detail::type_list<Zs...>, Rs> {};
/// Convenience type alias. /// Convenience type alias.
/// @relates composed_type /// @relates composed_type
template <class F, class G> template <class F, class G>
using composed_type_t = typename composed_type<G, F, F, using composed_type_t =
detail::type_list<>>::type; typename composed_type<G, F, F, detail::type_list<>>::type;
} // namespace caf } // namespace caf
...@@ -26,73 +26,64 @@ ...@@ -26,73 +26,64 @@
#include "caf/optional.hpp" #include "caf/optional.hpp"
#include "caf/replies_to.hpp" #include "caf/replies_to.hpp"
namespace caf { namespace caf::detail {
namespace detail {
// dmi = deduce_mpi_implementation // dmi = deduce_mpi_implementation
template <class T> template <class T>
struct dmi; struct dmi;
// case #1: function returning a single value // case #1: function returning a single value
template <class Y, class... Xs> template <class Out, class... In>
struct dmi<Y(Xs...)> { struct dmi<Out(In...)> {
using type = typed_mpi<type_list<std::decay_t<Xs>...>, using type = result<implicit_conversions_t<Out>>(std::decay_t<In>...);
output_tuple<implicit_conversions_t<Y>>>;
};
// case #2a: function returning a result<...>
template <class... Ys, class... Xs>
struct dmi<result<Ys...>(Xs...)> {
using type = typed_mpi<type_list<std::decay_t<Xs>...>,
output_tuple<implicit_conversions_t<Ys>...>>;
}; };
// case #2b: function returning a std::tuple<...> // case #2: function returning a result<...>
template <class... Ys, class... Xs> template <class... Out, class... In>
struct dmi<std::tuple<Ys...>(Xs...)> { struct dmi<result<Out...>(In...)> {
using type = typed_mpi<type_list<std::decay_t<Xs>...>, using type = result<Out...>(std::decay_t<In>...);
output_tuple<implicit_conversions_t<Ys>...>>;
}; };
// case #2c: function returning a std::tuple<...> // case #3: function returning a delegated<...>
template <class... Ys, class... Xs> template <class... Out, class... In>
struct dmi<delegated<Ys...>(Xs...)> { struct dmi<delegated<Out...>(In...)> {
using type = typed_mpi<type_list<std::decay_t<Xs>...>, using type = result<Out...>(std::decay_t<In>...);
output_tuple<implicit_conversions_t<Ys>...>>;
}; };
// case #2d: function returning a typed_response_promise<...> // case #4: function returning a typed_response_promise<...>
template <class... Ys, class... Xs> template <class... Out, class... In>
struct dmi<typed_response_promise<Ys...>(Xs...)> { struct dmi<typed_response_promise<Out...>(In...)> {
using type = typed_mpi<type_list<std::decay_t<Xs>...>, using type = result<Out...>(std::decay_t<In>...);
output_tuple<implicit_conversions_t<Ys>...>>;
}; };
// case #3: function returning an optional<>
template <class Y, class... Xs>
struct dmi<optional<Y>(Xs...)> : dmi<Y(Xs...)> {};
// case #4: function returning an expected<>
template <class Y, class... Xs>
struct dmi<expected<Y>(Xs...)> : dmi<Y(Xs...)> {};
// -- dmfou = deduce_mpi_function_object_unboxing // -- dmfou = deduce_mpi_function_object_unboxing
template <class T, bool isClass = std::is_class<T>::value> template <class T, bool isClass = std::is_class<T>::value>
struct dmfou; struct dmfou;
// case #1: const member function pointer // case #1a: const member function pointer
template <class C, class Result, class... Ts> template <class C, class Out, class... In>
struct dmfou<Result (C::*)(Ts...) const, false> : dmi<Result(Ts...)> {}; struct dmfou<Out (C::*)(In...) const, false> : dmi<Out(In...)> {};
// case #2: member function pointer // case #1b: const member function pointer with noexcept
template <class C, class Result, class... Ts> template <class C, class Out, class... In>
struct dmfou<Result (C::*)(Ts...), false> : dmi<Result(Ts...)> {}; struct dmfou<Out (C::*)(In...) const noexcept, false> : dmi<Out(In...)> {};
// case #3: good ol' function // case #2a: member function pointer
template <class Result, class... Ts> template <class C, class Out, class... In>
struct dmfou<Result(Ts...), false> : dmi<Result(Ts...)> {}; struct dmfou<Out (C::*)(In...), false> : dmi<Out(In...)> {};
// case #2b: member function pointer with noexcept
template <class C, class Out, class... In>
struct dmfou<Out (C::*)(In...) noexcept, false> : dmi<Out(In...)> {};
// case #3a: good ol' function
template <class Out, class... In>
struct dmfou<Out(In...), false> : dmi<Out(In...)> {};
// case #3a: good ol' function with noexcept
template <class Out, class... In>
struct dmfou<Out(In...) noexcept, false> : dmi<Out(In...)> {};
template <class T> template <class T>
struct dmfou<T, true> : dmfou<decltype(&T::operator()), false> {}; struct dmfou<T, true> : dmfou<decltype(&T::operator()), false> {};
...@@ -104,11 +95,12 @@ struct dmfou<timeout_definition<T>, true> { ...@@ -104,11 +95,12 @@ struct dmfou<timeout_definition<T>, true> {
using type = timeout_definition<T>; using type = timeout_definition<T>;
}; };
} // namespace detail } // namespace caf::detail
namespace caf {
/// Deduces the message passing interface from a function object. /// Deduces the message passing interface from a function object.
template <class T> template <class T>
using deduce_mpi_t = typename detail::dmfou<std::decay_t<T>>::type; using deduce_mpi_t = typename detail::dmfou<std::decay_t<T>>::type;
} // namespace caf } // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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 "caf/replies_to.hpp"
#include "caf/detail/type_list.hpp"
#include "caf/detail/typed_actor_util.hpp"
namespace caf::detail {
template <class T, class... Lists>
struct mpi_splice_by_input;
template <class T>
struct mpi_splice_by_input<T> {
using type = T;
};
template <class T, class... Lists>
struct mpi_splice_by_input<T, type_list<>, Lists...> {
// consumed an entire list without match -> fail
using type = none_t;
};
// splice two MPIs if they have the same input
template <class Input, class... Xs, class... Ys, class... Ts, class... Lists>
struct mpi_splice_by_input<typed_mpi<Input, type_list<Xs...>>,
type_list<typed_mpi<Input, type_list<Ys...>>, Ts...>,
Lists...>
: mpi_splice_by_input<typed_mpi<Input, type_list<Xs..., Ys...>>, Lists...> {};
// skip element in list until empty
template <class MPI, class MPI2, class... Ts, class... Lists>
struct mpi_splice_by_input<MPI, type_list<MPI2, Ts...>, Lists...>
: mpi_splice_by_input<MPI, type_list<Ts...>, Lists...> {};
template <class Result, class CurrentNeedle, class... Lists>
struct input_mapped;
template <class... Rs, class... Lists>
struct input_mapped<type_list<Rs...>, none_t, type_list<>, Lists...> {
using type = type_list<Rs...>;
};
template <class... Rs, class T, class... Ts, class... Lists>
struct input_mapped<type_list<Rs...>, none_t, type_list<T, Ts...>, Lists...>
: input_mapped<type_list<Rs...>, T, type_list<Ts...>, Lists...> {};
template <class... Rs, class T, class FirstList, class... Lists>
struct input_mapped<type_list<Rs...>, T, FirstList, Lists...>
: input_mapped<
type_list<Rs..., typename mpi_splice_by_input<T, Lists...>::type>, none_t,
FirstList, Lists...> {};
template <template <class...> class Target, class ListA, class ListB>
struct mpi_splice;
template <template <class...> class Target, class... Ts, class List>
struct mpi_splice<Target, type_list<Ts...>, List> {
using spliced_list =
typename input_mapped<type_list<>, none_t, type_list<Ts...>, List>::type;
using filtered_list = typename tl_filter_not_type<spliced_list, none_t>::type;
static_assert(tl_size<filtered_list>::value > 0,
"cannot splice incompatible actor handles");
using type = typename tl_apply<filtered_list, Target>::type;
};
} // namespace caf::detail
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <tuple> #include <tuple>
#include "caf/delegated.hpp" #include "caf/delegated.hpp"
#include "caf/fwd.hpp"
#include "caf/replies_to.hpp" #include "caf/replies_to.hpp"
#include "caf/response_promise.hpp" #include "caf/response_promise.hpp"
#include "caf/system_messages.hpp" #include "caf/system_messages.hpp"
...@@ -28,28 +29,8 @@ ...@@ -28,28 +29,8 @@
#include "caf/detail/type_list.hpp" #include "caf/detail/type_list.hpp"
namespace caf { // forward declarations
template <class... Ts>
class typed_actor;
} // namespace caf
namespace caf::detail { namespace caf::detail {
template <class Arguments, class Signature>
struct input_is_eval_impl : std::false_type {};
template <class Arguments, class Out>
struct input_is_eval_impl<Arguments, typed_mpi<Arguments, Out>>
: std::true_type {};
template <class Arguments>
struct input_is {
template <class Signature>
struct eval : input_is_eval_impl<Arguments, Signature> {};
};
template <class... Ts> template <class... Ts>
struct make_response_promise_helper { struct make_response_promise_helper {
using type = typed_response_promise<Ts...>; using type = typed_response_promise<Ts...>;
...@@ -111,7 +92,7 @@ struct static_error_printer<N, -1, Xs, Ys> { ...@@ -111,7 +92,7 @@ struct static_error_printer<N, -1, Xs, Ys> {
#define CAF_STATICERR(x) \ #define CAF_STATICERR(x) \
template <int N, class Xs, class Ys> \ template <int N, class Xs, class Ys> \
struct static_error_printer<N, x, Xs, Ys> { \ struct static_error_printer<N, x, Xs, Ys> { \
static_assert(N == x, "unexpected handler at index " #x); \ static_assert(N == x, "unexpected handler at index " #x " (0-based)"); \
} }
CAF_STATICERR(0); CAF_STATICERR(0);
...@@ -150,4 +131,23 @@ struct extend_with_helper<typed_actor<Xs...>, typed_actor<Ys...>, Ts...> ...@@ -150,4 +131,23 @@ struct extend_with_helper<typed_actor<Xs...>, typed_actor<Ys...>, Ts...>
// nop // nop
}; };
template <class F>
struct is_normalized_signature {
static constexpr bool value = false;
};
template <class T>
constexpr bool is_decayed = !std::is_reference<T>::value
&& !std::is_const<T>::value
&& !std::is_volatile<T>::value;
template <class... Out, class... In>
struct is_normalized_signature<result<Out...>(In...)> {
static constexpr bool value = (is_decayed<Out> && ...)
&& (is_decayed<In> && ...);
};
template <class F>
constexpr bool is_normalized_signature_v = is_normalized_signature<F>::value;
} // namespace caf::detail } // namespace caf::detail
...@@ -23,9 +23,7 @@ ...@@ -23,9 +23,7 @@
#include "caf/detail/type_list.hpp" #include "caf/detail/type_list.hpp"
namespace caf { namespace caf::detail {
namespace detail {
// imi = interface_mismatch_implementation // imi = interface_mismatch_implementation
// Precondition: Pos == 0 && len(Xs) == len(Ys) && len(Zs) == 0 // Precondition: Pos == 0 && len(Xs) == len(Ys) && len(Zs) == 0
...@@ -56,19 +54,17 @@ struct imi<Pos, type_list<timeout_definition<X>>, type_list<>, type_list<>> { ...@@ -56,19 +54,17 @@ struct imi<Pos, type_list<timeout_definition<X>>, type_list<>, type_list<>> {
// end of recursion: failure (consumed all Xs but not all Ys) // end of recursion: failure (consumed all Xs but not all Ys)
template <int Pos, class Yin, class Yout, class... Ys> template <int Pos, class Yin, class Yout, class... Ys>
struct imi<Pos, type_list<>, type_list<typed_mpi<Yin, Yout>, Ys...>, struct imi<Pos, type_list<>, type_list<Yout(Yin), Ys...>, type_list<>> {
type_list<>> {
static constexpr int value = -1; static constexpr int value = -1;
using xs = type_list<>; using xs = type_list<>;
using ys = type_list<typed_mpi<Yin, Yout>, Ys...>; using ys = type_list<Yout(Yin), Ys...>;
}; };
// end of recursion: failure (consumed all Ys but not all Xs) // end of recursion: failure (consumed all Ys but not all Xs)
template <int Pos, class Xin, class Xout, class... Xs> template <int Pos, class Xin, class Xout, class... Xs>
struct imi<Pos, type_list<typed_mpi<Xin, Xout>, Xs...>, type_list<>, struct imi<Pos, type_list<Xout(Xin), Xs...>, type_list<>, type_list<>> {
type_list<>> {
static constexpr int value = -2; static constexpr int value = -2;
using xs = type_list<typed_mpi<Xin, Xout>, Xs...>; using xs = type_list<Xout(Xin), Xs...>;
using ys = type_list<>; using ys = type_list<>;
}; };
...@@ -81,27 +77,22 @@ struct imi<Pos, type_list<timeout_definition<X>>, type_list<Y, Ys...>, ...@@ -81,27 +77,22 @@ struct imi<Pos, type_list<timeout_definition<X>>, type_list<Y, Ys...>,
using ys = type_list<Y, Ys...>; using ys = type_list<Y, Ys...>;
}; };
// case #1: exact match // case #1a: exact match
template <int Pos, class In, class Out, class... Xs, class... Ys, class... Zs> template <int Pos, class Out, class... In, class... Xs, class... Ys,
struct imi<Pos, type_list<typed_mpi<In, Out>, Xs...>, class... Zs>
type_list<typed_mpi<In, Out>, Ys...>, type_list<Zs...>> struct imi<Pos, type_list<Out(In...), Xs...>, type_list<Out(In...), Ys...>,
type_list<Zs...>>
: imi<Pos + 1, type_list<Xs...>, type_list<Zs..., Ys...>, type_list<>> {}; : imi<Pos + 1, type_list<Xs...>, type_list<Zs..., Ys...>, type_list<>> {};
// case #2: match with skip_t // case #2: no match at position
template <int Pos, class In, class... Xs, class Out, class... Ys, class... Zs> template <int Pos, class Xout, class... Xin, class... Xs, class Yout,
struct imi<Pos, type_list<typed_mpi<In, output_tuple<skip_t>>, Xs...>, class... Yin, class... Ys, class... Zs>
type_list<typed_mpi<In, Out>, Ys...>, type_list<Zs...>> struct imi<Pos, type_list<Xout(Xin...), Xs...>, type_list<Yout(Yin...), Ys...>,
: imi<Pos + 1, type_list<Xs...>, type_list<Zs..., Ys...>, type_list<>> {}; type_list<Zs...>>
: imi<Pos, type_list<Xout(Xin...), Xs...>, type_list<Ys...>,
// case #3: no match at position type_list<Zs..., Yout(Yin...)>> {};
template <int Pos, class Xin, class Xout, class... Xs, class Yin, class Yout,
class... Ys, class... Zs>
struct imi<Pos, type_list<typed_mpi<Xin, Xout>, Xs...>,
type_list<typed_mpi<Yin, Yout>, Ys...>, type_list<Zs...>>
: imi<Pos, type_list<typed_mpi<Xin, Xout>, Xs...>, type_list<Ys...>,
type_list<Zs..., typed_mpi<Yin, Yout>>> {};
// case #4: no match (error) // case #3: no match (error)
template <int Pos, class X, class... Xs, class... Zs> template <int Pos, class X, class... Xs, class... Zs>
struct imi<Pos, type_list<X, Xs...>, type_list<>, type_list<Zs...>> { struct imi<Pos, type_list<X, Xs...>, type_list<>, type_list<Zs...>> {
static constexpr int value = Pos; static constexpr int value = Pos;
...@@ -109,7 +100,9 @@ struct imi<Pos, type_list<X, Xs...>, type_list<>, type_list<Zs...>> { ...@@ -109,7 +100,9 @@ struct imi<Pos, type_list<X, Xs...>, type_list<>, type_list<Zs...>> {
using ys = type_list<Zs...>; using ys = type_list<Zs...>;
}; };
} // namespace detail } // namespace caf::detail
namespace caf {
/// Scans two typed MPI lists for compatibility, returning the index of the /// Scans two typed MPI lists for compatibility, returning the index of the
/// first mismatch. Returns the number of elements on a match. /// first mismatch. Returns the number of elements on a match.
......
...@@ -21,29 +21,22 @@ ...@@ -21,29 +21,22 @@
#include <string> #include <string>
#include "caf/detail/core_export.hpp" #include "caf/detail/core_export.hpp"
#include "caf/detail/type_list.hpp" #include "caf/fwd.hpp"
namespace caf { namespace caf {
/// @cond PRIVATE /// @private
CAF_CORE_EXPORT std::string CAF_CORE_EXPORT std::string
replies_to_type_name(size_t input_size, const std::string* input, replies_to_type_name(size_t input_size, const std::string* input,
size_t output_size, const std::string* output); size_t output_size, const std::string* output);
/// @endcond
template <class...>
struct output_tuple {};
template <class Input, class Output>
struct typed_mpi {};
template <class... Is> template <class... Is>
struct replies_to { struct replies_to {
template <class... Os> template <class... Os>
using with = typed_mpi<detail::type_list<Is...>, output_tuple<Os...>>; using with = result<Os...>(Is...);
}; };
template <class... Is> template <class... Is>
using reacts_to = typed_mpi<detail::type_list<Is...>, output_tuple<void>>; using reacts_to = result<void>(Is...);
} // namespace caf } // namespace caf
...@@ -117,8 +117,8 @@ public: ...@@ -117,8 +117,8 @@ public:
template <class T = traits, class F = none_t, class OnError = none_t, template <class T = traits, class F = none_t, class OnError = none_t,
class = detail::enable_if_t<T::is_blocking>> class = detail::enable_if_t<T::is_blocking>>
detail::is_handler_for_ef<OnError, error> receive(F f, OnError g) { detail::is_handler_for_ef<OnError, error> receive(F f, OnError g) {
static_assert(detail::is_callable<F>::value, "F must provide a single, " static_assert(detail::is_callable<F>::value,
"non-template operator()"); "F must provide a single, non-template operator()");
static_assert(detail::is_callable_with<OnError, error&>::value, static_assert(detail::is_callable_with<OnError, error&>::value,
"OnError must provide an operator() that takes a caf::error"); "OnError must provide an operator() that takes a caf::error");
using result_type = typename detail::get_callable_trait<F>::result_type; using result_type = typename detail::get_callable_trait<F>::result_type;
......
...@@ -50,26 +50,32 @@ struct response_type<detail::type_list<>, Xs...> { ...@@ -50,26 +50,32 @@ struct response_type<detail::type_list<>, Xs...> {
}; };
// case #1: no match // case #1: no match
template <class In, class Out, class... Ts, class... Xs> template <class Out, class... In, class... Fs, class... Xs>
struct response_type<detail::type_list<typed_mpi<In, Out>, Ts...>, Xs...> struct response_type<detail::type_list<Out(In...), Fs...>, Xs...>
: response_type<detail::type_list<Ts...>, Xs...> {}; : response_type<detail::type_list<Fs...>, Xs...> {};
// case #2: match // case #2.a: match `result<Out...>(In...)`
template <class... Out, class... Ts, class... Xs> template <class... Out, class... In, class... Fs>
struct response_type< struct response_type<detail::type_list<result<Out...>(In...), Fs...>, In...> {
detail::type_list<typed_mpi<detail::type_list<Xs...>, output_tuple<Out...>>,
Ts...>,
Xs...> {
static constexpr bool valid = true; static constexpr bool valid = true;
using type = detail::type_list<Out...>; using type = detail::type_list<Out...>;
using tuple_type = std::tuple<Out...>; using tuple_type = std::tuple<Out...>;
using delegated_type = delegated<Out...>; using delegated_type = delegated<Out...>;
}; };
/// Computes the response message for input `Xs...` from the list // case #2.b: match `Out(In...)`
/// of message passing interfaces `Ts`. template <class Out, class... In, class... Fs>
template <class Ts, class... Xs> struct response_type<detail::type_list<Out(In...), Fs...>, In...> {
using response_type_t = typename response_type<Ts, Xs...>::type; static constexpr bool valid = true;
using type = detail::type_list<Out>;
using tuple_type = std::tuple<Out>;
using delegated_type = delegated<Out>;
};
/// Computes the response message for input `In...` from the list of message
/// passing interfaces `Fs`.
template <class Fs, class... In>
using response_type_t = typename response_type<Fs, In...>::type;
/// Unboxes `Xs` and calls `response_type`. /// Unboxes `Xs` and calls `response_type`.
template <class Ts, class Xs> template <class Ts, class Xs>
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
#include "caf/actor_system.hpp" #include "caf/actor_system.hpp"
#include "caf/composed_type.hpp" #include "caf/composed_type.hpp"
#include "caf/decorator/sequencer.hpp" #include "caf/decorator/sequencer.hpp"
#include "caf/detail/mpi_splice.hpp" #include "caf/fwd.hpp"
#include "caf/intrusive_ptr.hpp" #include "caf/intrusive_ptr.hpp"
#include "caf/make_actor.hpp" #include "caf/make_actor.hpp"
#include "caf/replies_to.hpp" #include "caf/replies_to.hpp"
...@@ -37,19 +37,8 @@ ...@@ -37,19 +37,8 @@
namespace caf { namespace caf {
template <class... Sigs>
class typed_event_based_actor;
namespace io {
template <class... Sigs>
class typed_broker;
} // namespace io
/// Identifies a statically typed actor. /// Identifies a statically typed actor.
/// @tparam Sigs Signature of this actor as `replies_to<...>::with<...>` /// @tparam Sigs Function signatures for all accepted messages.
/// parameter pack.
template <class... Sigs> template <class... Sigs>
class typed_actor : detail::comparable<typed_actor<Sigs...>>, class typed_actor : detail::comparable<typed_actor<Sigs...>>,
detail::comparable<typed_actor<Sigs...>, actor>, detail::comparable<typed_actor<Sigs...>, actor>,
...@@ -58,6 +47,10 @@ class typed_actor : detail::comparable<typed_actor<Sigs...>>, ...@@ -58,6 +47,10 @@ class typed_actor : detail::comparable<typed_actor<Sigs...>>,
public: public:
static_assert(sizeof...(Sigs) > 0, "Empty typed actor handle"); static_assert(sizeof...(Sigs) > 0, "Empty typed actor handle");
static_assert((detail::is_normalized_signature_v<Sigs> && ...),
"Function signatures must be normalized to the format "
"'result<Out...>(In...)', no qualifiers or references allowed");
// -- friend types that need access to private ctors // -- friend types that need access to private ctors
friend class local_actor; friend class local_actor;
......
...@@ -29,14 +29,22 @@ ...@@ -29,14 +29,22 @@
namespace caf ::detail { namespace caf ::detail {
// converts a list of replies_to<...>::with<...> elements to a list of template <class Signature>
// lists containing the replies_to<...> half only struct input_args;
template <class R, class... Ts>
struct input_args<R(Ts...)> {
using type = type_list<Ts...>;
};
// Converts a list of function signatures to lists of inputs (dropping the
// return type).
template <class List> template <class List>
struct input_only; struct input_only;
template <class... Ts> template <class... Ts>
struct input_only<detail::type_list<Ts...>> { struct input_only<type_list<Ts...>> {
using type = detail::type_list<typename Ts::input_types...>; using type = type_list<typename input_args<Ts>::type...>;
}; };
using skip_list = detail::type_list<skip_t>; using skip_list = detail::type_list<skip_t>;
......
...@@ -284,7 +284,7 @@ CAF_TEST(string_representation) { ...@@ -284,7 +284,7 @@ CAF_TEST(string_representation) {
CAF_TEST(mpi_string_representation) { CAF_TEST(mpi_string_representation) {
CAF_CHECK(sys.message_types(a0.dt).empty()); CAF_CHECK(sys.message_types(a0.dt).empty());
std::set<std::string> st_expected{"caf::replies_to<int32_t>::with<int32_t>"}; std::set<std::string> st_expected{"(int32_t) -> (int32_t)"};
CAF_CHECK_EQUAL(st_expected, sys.message_types(a0.st)); CAF_CHECK_EQUAL(st_expected, sys.message_types(a0.st));
CAF_CHECK_EQUAL(st_expected, sys.message_types<testee_actor>()); CAF_CHECK_EQUAL(st_expected, sys.message_types<testee_actor>());
} }
......
...@@ -38,9 +38,13 @@ using namespace std::string_literals; ...@@ -38,9 +38,13 @@ using namespace std::string_literals;
namespace { namespace {
static_assert(std::is_same<reacts_to<int, int>, result<void>(int, int)>::value);
static_assert(std::is_same<replies_to<double>::with<double>,
result<double>(double)>::value);
// check invariants of type system // check invariants of type system
using dummy1 using dummy1 = typed_actor<result<void>(int, int), result<double>(double)>;
= typed_actor<reacts_to<int, int>, replies_to<double>::with<double>>;
using dummy2 = dummy1::extend<reacts_to<ok_atom>>; using dummy2 = dummy1::extend<reacts_to<ok_atom>>;
...@@ -296,10 +300,9 @@ CAF_TEST(event_testee_series) { ...@@ -296,10 +300,9 @@ CAF_TEST(event_testee_series) {
auto et = self->spawn<event_testee>(); auto et = self->spawn<event_testee>();
CAF_MESSAGE("et->message_types() returns an interface description"); CAF_MESSAGE("et->message_types() returns an interface description");
typed_actor<replies_to<get_state_atom>::with<string>> sub_et = et; typed_actor<replies_to<get_state_atom>::with<string>> sub_et = et;
std::set<string> iface{"caf::replies_to<get_state_atom>::with<std::string>", std::set<string> iface{"(get_state_atom) -> (std::string)",
"caf::replies_to<std::string>::with<void>", "(std::string) -> (void)", "(float) -> (void)",
"caf::replies_to<float>::with<void>", "(int32_t) -> (int32_t)"};
"caf::replies_to<int32_t>::with<int32_t>"};
CAF_CHECK_EQUAL(join(sub_et->message_types(), ","), join(iface, ",")); CAF_CHECK_EQUAL(join(sub_et->message_types(), ","), join(iface, ","));
CAF_MESSAGE("the testee skips messages to drive its internal state machine"); CAF_MESSAGE("the testee skips messages to drive its internal state machine");
self->send(et, 1); self->send(et, 1);
...@@ -323,7 +326,7 @@ CAF_TEST(string_delegator_chain) { ...@@ -323,7 +326,7 @@ CAF_TEST(string_delegator_chain) {
// run test series with string reverter // run test series with string reverter
auto aut = self->spawn<monitored>(string_delegator, auto aut = self->spawn<monitored>(string_delegator,
sys.spawn(string_reverter), true); sys.spawn(string_reverter), true);
std::set<string> iface{"caf::replies_to<std::string>::with<std::string>"}; std::set<string> iface{"(std::string) -> (std::string)"};
CAF_CHECK_EQUAL(aut->message_types(), iface); CAF_CHECK_EQUAL(aut->message_types(), iface);
inject((string), from(self).to(aut).with("Hello World!"s)); inject((string), from(self).to(aut).with("Hello World!"s));
run(); run();
......
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