Commit f102a1b1 authored by Dominik Charousset's avatar Dominik Charousset

Backport type_id API and _v atom syntax

Porting some of the additions back to the CAF 0.17 series allows users
to write code that is forward compatible to CAF 0.18.
parent 2ee9e994
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
#include <iostream> #include <iostream>
#include "caf/all.hpp" #include "caf/all.hpp"
#include "caf/io/all.hpp"
using namespace caf; using namespace caf;
using std::endl; using std::endl;
......
...@@ -30,8 +30,7 @@ using kickoff_atom = atom_constant<atom("kickoff")>; ...@@ -30,8 +30,7 @@ using kickoff_atom = atom_constant<atom("kickoff")>;
// utility function to print an exit message with custom name // utility function to print an exit message with custom name
void print_on_exit(scheduled_actor* self, const std::string& name) { void print_on_exit(scheduled_actor* self, const std::string& name) {
self->attach_functor([=](const error& reason) { self->attach_functor([=](const error& reason) {
aout(self) << name << " exited: " << self->home_system().render(reason) aout(self) << name << " exited: " << to_string(reason) << endl;
<< endl;
}); });
} }
...@@ -174,8 +173,8 @@ void run_server(actor_system& system, const config& cfg) { ...@@ -174,8 +173,8 @@ void run_server(actor_system& system, const config& cfg) {
auto server_actor = system.middleman().spawn_server(server, cfg.port, auto server_actor = system.middleman().spawn_server(server, cfg.port,
pong_actor); pong_actor);
if (!server_actor) if (!server_actor)
cerr << "unable to spawn server: " cerr << "unable to spawn server: " << to_string(server_actor.error())
<< system.render(server_actor.error()) << endl; << endl;
} }
void run_client(actor_system& system, const config& cfg) { void run_client(actor_system& system, const config& cfg) {
...@@ -184,8 +183,8 @@ void run_client(actor_system& system, const config& cfg) { ...@@ -184,8 +183,8 @@ void run_client(actor_system& system, const config& cfg) {
auto io_actor = system.middleman().spawn_client(protobuf_io, cfg.host, auto io_actor = system.middleman().spawn_client(protobuf_io, cfg.host,
cfg.port, ping_actor); cfg.port, ping_actor);
if (!io_actor) { if (!io_actor) {
cout << "cannot connect to " << cfg.host << " at port " << cfg.port cout << "cannot connect to " << cfg.host << " at port " << cfg.port << ": "
<< ": " << system.render(io_actor.error()) << endl; << to_string(io_actor.error()) << endl;
return; return;
} }
send_as(*io_actor, ping_actor, kickoff_atom::value, *io_actor); send_as(*io_actor, ping_actor, kickoff_atom::value, *io_actor);
......
This diff is collapsed.
...@@ -13,8 +13,6 @@ using namespace caf::io; ...@@ -13,8 +13,6 @@ using namespace caf::io;
namespace { namespace {
using tick_atom = atom_constant<atom("tick")>;
constexpr const char http_ok[] = R"__(HTTP/1.1 200 OK constexpr const char http_ok[] = R"__(HTTP/1.1 200 OK
Content-Type: text/plain Content-Type: text/plain
Connection: keep-alive Connection: keep-alive
...@@ -51,7 +49,7 @@ behavior server(broker* self) { ...@@ -51,7 +49,7 @@ behavior server(broker* self) {
self->set_down_handler([=](down_msg&) { self->set_down_handler([=](down_msg&) {
++*counter; ++*counter;
}); });
self->delayed_send(self, std::chrono::seconds(1), tick_atom::value); self->delayed_send(self, std::chrono::seconds(1), tick_atom_v);
return { return {
[=](const new_connection_msg& ncm) { [=](const new_connection_msg& ncm) {
auto worker = self->fork(connection_worker, ncm.handle); auto worker = self->fork(connection_worker, ncm.handle);
...@@ -61,7 +59,7 @@ behavior server(broker* self) { ...@@ -61,7 +59,7 @@ behavior server(broker* self) {
[=](tick_atom) { [=](tick_atom) {
aout(self) << "Finished " << *counter << " requests per second." << endl; aout(self) << "Finished " << *counter << " requests per second." << endl;
*counter = 0; *counter = 0;
self->delayed_send(self, std::chrono::seconds(1), tick_atom::value); self->delayed_send(self, std::chrono::seconds(1), tick_atom_v);
} }
}; };
} }
...@@ -79,8 +77,8 @@ public: ...@@ -79,8 +77,8 @@ public:
void caf_main(actor_system& system, const config& cfg) { void caf_main(actor_system& system, const config& cfg) {
auto server_actor = system.middleman().spawn_server(server, cfg.port); auto server_actor = system.middleman().spawn_server(server, cfg.port);
if (!server_actor) { if (!server_actor) {
cerr << "*** cannot spawn server: " cerr << "*** cannot spawn server: " << to_string(server_actor.error())
<< system.render(server_actor.error()) << endl; << endl;
return; return;
} }
cout << "*** listening on port " << cfg.port << endl; cout << "*** listening on port " << cfg.port << endl;
......
...@@ -38,19 +38,12 @@ relaxed-sleep-duration=10ms ...@@ -38,19 +38,12 @@ relaxed-sleep-duration=10ms
; configures whether MMs try to span a full mesh ; configures whether MMs try to span a full mesh
enable-automatic-connections=false enable-automatic-connections=false
; application identifier of this node, prevents connection to other CAF ; application identifier of this node, prevents connection to other CAF
; instances with incompatible identifiers (at least one entry must match) ; instances with different identifier
app-identifiers=["generic-caf-app"] app-identifier=""
; maximum number of consecutive I/O reads per broker ; maximum number of consecutive I/O reads per broker
max-consecutive-reads=50 max-consecutive-reads=50
; heartbeat message interval for periodic traffic ; heartbeat message interval in ms (0 disables heartbeating)
; (0 disables heartbeating) heartbeat-interval=0ms
heartbeat-interval=0s
; force disconnects of CAF nodes after receiving no traffic for this amount of
; time (0 disables this feature, but we highly recommend using heartbeats and
; connection timeouts), be careful when deploying CAF applications with
; different heartbeat intervals and connection timeouts: this timeout should
; line up with the *longest* heartbeat interval (ideally it's a multiple)
connection-timeout=0s
; configures whether the MM attaches its internal utility actors to the ; configures whether the MM attaches its internal utility actors to the
; scheduler instead of dedicating individual threads (needed only for ; scheduler instead of dedicating individual threads (needed only for
; deterministic testing) ; deterministic testing)
...@@ -59,6 +52,10 @@ attach-utility-actors=false ...@@ -59,6 +52,10 @@ attach-utility-actors=false
; setting this to true allows fully deterministic execution in unit test and ; setting this to true allows fully deterministic execution in unit test and
; requires the user to trigger I/O manually ; requires the user to trigger I/O manually
manual-multiplexing=false manual-multiplexing=false
; disables communication via TCP
disable-tcp=false
; enable communication via UDP
enable-udp=false
; configures how many background workers are spawned for deserialization, ; configures how many background workers are spawned for deserialization,
; by default CAF uses 1-4 workers depending on the number of cores ; by default CAF uses 1-4 workers depending on the number of cores
workers=<min(3, number of cores / 4) + 1> workers=<min(3, number of cores / 4) + 1>
......
This diff is collapsed.
...@@ -3,34 +3,45 @@ ...@@ -3,34 +3,45 @@
// Manual refs: 24-27, 30-34, 75-78, 81-84 (ConfiguringActorApplications) // Manual refs: 24-27, 30-34, 75-78, 81-84 (ConfiguringActorApplications)
// 23-33 (TypeInspection) // 23-33 (TypeInspection)
#include <string>
#include <vector>
#include <cassert> #include <cassert>
#include <utility>
#include <iostream> #include <iostream>
#include <string>
#include <utility>
#include <vector>
#include "caf/all.hpp" #include "caf/all.hpp"
using std::cout; // --(rst-type-id-block-begin)--
struct foo;
struct foo2;
CAF_BEGIN_TYPE_ID_BLOCK(custom_types_1, first_custom_type_id)
CAF_ADD_TYPE_ID(custom_types_1, (foo))
CAF_ADD_TYPE_ID(custom_types_1, (foo2))
CAF_ADD_TYPE_ID(custom_types_1, (std::pair<int32_t, int32_t>) )
CAF_END_TYPE_ID_BLOCK(custom_types_1)
// --(rst-type-id-block-end)--
using std::cerr; using std::cerr;
using std::cout;
using std::endl; using std::endl;
using std::vector; using std::vector;
using namespace caf; using namespace caf;
namespace { // --(rst-foo-begin)--
// POD struct foo
struct foo { struct foo {
std::vector<int> a; std::vector<int> a;
int b; int b;
}; };
// foo needs to be serializable
template <class Inspector> template <class Inspector>
typename Inspector::result_type inspect(Inspector& f, foo& x) { typename Inspector::result_type inspect(Inspector& f, foo& x) {
return f(meta::type_name("foo"), x.a, x.b); return f(meta::type_name("foo"), x.a, x.b);
} }
// --(rst-foo-end)--
// a pair of two ints // a pair of two ints
using foo_pair = std::pair<int, int>; using foo_pair = std::pair<int, int>;
...@@ -58,7 +69,7 @@ void testee(event_based_actor* self, size_t remaining) { ...@@ -58,7 +69,7 @@ void testee(event_based_actor* self, size_t remaining) {
else else
self->quit(); self->quit();
}; };
self->become ( self->become(
// note: we sent a foo_pair2, but match on foo_pair // note: we sent a foo_pair2, but match on foo_pair
// that works because both are aliases for std::pair<int, int> // that works because both are aliases for std::pair<int, int>
[=](const foo_pair& val) { [=](const foo_pair& val) {
...@@ -68,20 +79,10 @@ void testee(event_based_actor* self, size_t remaining) { ...@@ -68,20 +79,10 @@ void testee(event_based_actor* self, size_t remaining) {
[=](const foo& val) { [=](const foo& val) {
aout(self) << to_string(val) << endl; aout(self) << to_string(val) << endl;
set_next_behavior(); set_next_behavior();
} });
);
} }
class config : public actor_system_config { void caf_main(actor_system& sys) {
public:
config() {
add_message_type<foo>("foo");
add_message_type<foo2>("foo2");
add_message_type<foo_pair>("foo_pair");
}
};
void caf_main(actor_system& system, const config&) {
// two variables for testing serialization // two variables for testing serialization
foo2 f1; foo2 f1;
foo2 f2; foo2 f2;
...@@ -90,34 +91,30 @@ void caf_main(actor_system& system, const config&) { ...@@ -90,34 +91,30 @@ void caf_main(actor_system& system, const config&) {
f1.b.resize(1); f1.b.resize(1);
f1.b.back().push_back(42); f1.b.back().push_back(42);
// I/O buffer // I/O buffer
vector<char> buf; binary_serializer::container_type buf;
// write f1 to buffer // write f1 to buffer
binary_serializer bs{system, buf}; binary_serializer bs{sys, buf};
auto e = bs(f1); auto e = bs(f1);
if (e) { if (e) {
std::cerr << "*** unable to serialize foo2: " std::cerr << "*** unable to serialize foo2: " << to_string(e) << '\n';
<< system.render(e) << std::endl;
return; return;
} }
// read f2 back from buffer // read f2 back from buffer
binary_deserializer bd{system, buf}; binary_deserializer bd{sys, buf};
e = bd(f2); e = bd(f2);
if (e) { if (e) {
std::cerr << "*** unable to serialize foo2: " std::cerr << "*** unable to serialize foo2: " << to_string(e) << '\n';
<< system.render(e) << std::endl;
return; return;
} }
// must be equal // must be equal
assert(to_string(f1) == to_string(f2)); assert(to_string(f1) == to_string(f2));
// spawn a testee that receives two messages of user-defined type // spawn a testee that receives two messages of user-defined type
auto t = system.spawn(testee, 2u); auto t = sys.spawn(testee, 2u);
scoped_actor self{system}; scoped_actor self{sys};
// send t a foo // send t a foo
self->send(t, foo{std::vector<int>{1, 2, 3, 4}, 5}); self->send(t, foo{std::vector<int>{1, 2, 3, 4}, 5});
// send t a foo_pair2 // send t a foo_pair2
self->send(t, foo_pair2{3, 4}); self->send(t, foo_pair2{3, 4});
} }
} // namespace CAF_MAIN(id_block::custom_types_1)
CAF_MAIN()
...@@ -6,14 +6,20 @@ ...@@ -6,14 +6,20 @@
#include "caf/all.hpp" #include "caf/all.hpp"
class foo;
CAF_BEGIN_TYPE_ID_BLOCK(custom_types_2, first_custom_type_id)
CAF_ADD_TYPE_ID(custom_types_2, (foo))
CAF_END_TYPE_ID_BLOCK(custom_types_2)
using std::cout; using std::cout;
using std::endl; using std::endl;
using std::make_pair; using std::make_pair;
using namespace caf; using namespace caf;
namespace {
// a simple class using getter and setter member functions // a simple class using getter and setter member functions
class foo { class foo {
public: public:
...@@ -58,17 +64,8 @@ behavior testee(event_based_actor* self) { ...@@ -58,17 +64,8 @@ behavior testee(event_based_actor* self) {
}; };
} }
class config : public actor_system_config { void caf_main(actor_system& sys) {
public: anon_send(sys.spawn(testee), foo{1, 2});
config() {
add_message_type<foo>("foo");
}
};
void caf_main(actor_system& system, const config&) {
anon_send(system.spawn(testee), foo{1, 2});
} }
} // namespace CAF_MAIN(id_block::custom_types_2)
CAF_MAIN()
// Showcases custom message types that cannot provide // Showcases custom message types that cannot provide
// friend access to the inspect() function. // friend access to the inspect() function.
// Manual refs: 20-49, 76-103 (TypeInspection)
#include <utility>
#include <iostream> #include <iostream>
#include <utility>
#include "caf/all.hpp" #include "caf/all.hpp"
class foo;
CAF_BEGIN_TYPE_ID_BLOCK(custom_types_3, first_custom_type_id)
CAF_ADD_TYPE_ID(custom_types_3, (foo))
CAF_END_TYPE_ID_BLOCK(custom_types_3)
using std::cout; using std::cout;
using std::endl; using std::endl;
using std::make_pair; using std::make_pair;
using namespace caf; using namespace caf;
namespace { // Identical to our second custom type example, but no friend access for
// `inspect`.
// identical to our second custom type example, but // --(rst-foo-begin)--
// no friend access for `inspect`
class foo { class foo {
public: public:
foo(int a0 = 0, int b0 = 0) : a_(a0), b_(b0) { foo(int a0 = 0, int b0 = 0) : a_(a0), b_(b0) {
...@@ -47,72 +52,31 @@ private: ...@@ -47,72 +52,31 @@ private:
int a_; int a_;
int b_; int b_;
}; };
// --(rst-foo-end)--
// A lightweight scope guard implementation. // --(rst-inspect-begin)--
template <class Fun>
class scope_guard {
public:
scope_guard(Fun f) : fun_(std::move(f)), enabled_(true) { }
scope_guard(scope_guard&& x) : fun_(std::move(x.fun_)), enabled_(x.enabled_) {
x.enabled_ = false;
}
~scope_guard() {
if (enabled_) fun_();
}
private:
Fun fun_;
bool enabled_;
};
// Creates a guard that executes `f` as soon as it goes out of scope.
template <class Fun>
scope_guard<Fun> make_scope_guard(Fun f) {
return {std::move(f)};
}
template <class Inspector> template <class Inspector>
typename std::enable_if<Inspector::reads_state, typename Inspector::result_type inspect(Inspector& f, foo& x) {
typename Inspector::result_type>::type auto a = x.a();
inspect(Inspector& f, foo& x) { auto b = x.b();
return f(meta::type_name("foo"), x.a(), x.b()); auto load = meta::load_callback([&]() -> error {
} // Write back to x when loading values from the inspector.
template <class Inspector>
typename std::enable_if<Inspector::writes_state,
typename Inspector::result_type>::type
inspect(Inspector& f, foo& x) {
int a;
int b;
// write back to x at scope exit
auto g = make_scope_guard([&] {
x.set_a(a); x.set_a(a);
x.set_b(b); x.set_b(b);
return none;
}); });
return f(meta::type_name("foo"), a, b); return f(meta::type_name("foo"), a, b, load);
} }
// --(rst-inspect-end)--
behavior testee(event_based_actor* self) { behavior testee(event_based_actor* self) {
return { return {
[=](const foo& x) { [=](const foo& x) { aout(self) << to_string(x) << endl; },
aout(self) << to_string(x) << endl;
}
}; };
} }
class config : public actor_system_config { void caf_main(actor_system& system) {
public:
config() {
add_message_type<foo>("foo");
}
};
void caf_main(actor_system& system, const config&) {
anon_send(system.spawn(testee), foo{1, 2}); anon_send(system.spawn(testee), foo{1, 2});
} }
} // namespace CAF_MAIN(id_block::custom_types_3)
CAF_MAIN()
...@@ -3,33 +3,36 @@ ...@@ -3,33 +3,36 @@
* exercise using only libcaf's event-based actor implementation. * * exercise using only libcaf's event-based actor implementation. *
\******************************************************************************/ \******************************************************************************/
#include <chrono>
#include <iostream>
#include <map> #include <map>
#include <sstream>
#include <thread> #include <thread>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <chrono>
#include <sstream>
#include <iostream>
#include "caf/all.hpp" #include "caf/all.hpp"
using std::cout; CAF_BEGIN_TYPE_ID_BLOCK(dining_philosophers, first_custom_type_id)
CAF_ADD_ATOM(dining_philosophers, custom, take_atom, "take")
CAF_ADD_ATOM(dining_philosophers, custom, taken_atom, "taken")
CAF_ADD_ATOM(dining_philosophers, custom, eat_atom, "eat")
CAF_ADD_ATOM(dining_philosophers, custom, think_atom, "think")
CAF_END_TYPE_ID_BLOCK(dining_philosophers)
using std::cerr; using std::cerr;
using std::cout;
using std::endl; using std::endl;
using std::chrono::seconds; using std::chrono::seconds;
using namespace caf; using namespace caf;
using namespace custom;
namespace { namespace {
// atoms for chopstick interface // atoms for chopstick and philosopher interfaces
using put_atom = atom_constant<atom("put")>;
using take_atom = atom_constant<atom("take")>;
using taken_atom = atom_constant<atom("taken")>;
// atoms for philosopher interface
using eat_atom = atom_constant<atom("eat")>;
using think_atom = atom_constant<atom("think")>;
// a chopstick // a chopstick
using chopstick = typed_actor<replies_to<take_atom>::with<taken_atom, bool>, using chopstick = typed_actor<replies_to<take_atom>::with<taken_atom, bool>,
...@@ -41,26 +44,24 @@ chopstick::behavior_type taken_chopstick(chopstick::pointer, ...@@ -41,26 +44,24 @@ chopstick::behavior_type taken_chopstick(chopstick::pointer,
// either taken by a philosopher or available // either taken by a philosopher or available
chopstick::behavior_type available_chopstick(chopstick::pointer self) { chopstick::behavior_type available_chopstick(chopstick::pointer self) {
return { return {
[=](take_atom) -> std::tuple<taken_atom, bool> { [=](take_atom) -> result<taken_atom, bool> {
self->become(taken_chopstick(self, self->current_sender())); self->become(taken_chopstick(self, self->current_sender()));
return std::make_tuple(taken_atom::value, true); return {taken_atom_v, true};
}, },
[](put_atom) { [](put_atom) { cerr << "chopstick received unexpected 'put'" << endl; },
cerr << "chopstick received unexpected 'put'" << endl;
}
}; };
} }
chopstick::behavior_type taken_chopstick(chopstick::pointer self, chopstick::behavior_type taken_chopstick(chopstick::pointer self,
const strong_actor_ptr& user) { const strong_actor_ptr& user) {
return { return {
[](take_atom) -> std::tuple<taken_atom, bool> { [](take_atom) -> result<taken_atom, bool> {
return std::make_tuple(taken_atom::value, false); return {taken_atom_v, false};
}, },
[=](put_atom) { [=](put_atom) {
if (self->current_sender() == user) if (self->current_sender() == user)
self->become(available_chopstick(self)); self->become(available_chopstick(self));
} },
}; };
} }
...@@ -94,71 +95,56 @@ chopstick::behavior_type taken_chopstick(chopstick::pointer self, ...@@ -94,71 +95,56 @@ chopstick::behavior_type taken_chopstick(chopstick::pointer self,
class philosopher : public event_based_actor { class philosopher : public event_based_actor {
public: public:
philosopher(actor_config& cfg, philosopher(actor_config& cfg, std::string n, chopstick l, chopstick r)
std::string n, : event_based_actor(cfg),
chopstick l, name_(std::move(n)),
chopstick r) left_(std::move(l)),
: event_based_actor(cfg), right_(std::move(r)) {
name_(std::move(n)),
left_(std::move(l)),
right_(std::move(r)) {
// we only accept one message per state and skip others in the meantime // we only accept one message per state and skip others in the meantime
set_default_handler(skip); set_default_handler(skip);
// a philosopher that receives {eat} stops thinking and becomes hungry // a philosopher that receives {eat} stops thinking and becomes hungry
thinking_.assign( thinking_.assign([=](eat_atom) {
[=](eat_atom) { become(hungry_);
become(hungry_); send(left_, take_atom_v);
send(left_, take_atom::value); send(right_, take_atom_v);
send(right_, take_atom::value); });
}
);
// wait for the first answer of a chopstick // wait for the first answer of a chopstick
hungry_.assign( hungry_.assign([=](taken_atom, bool result) {
[=](taken_atom, bool result) { if (result)
if (result) become(granted_);
become(granted_); else
else become(denied_);
become(denied_); });
}
);
// philosopher was able to obtain the first chopstick // philosopher was able to obtain the first chopstick
granted_.assign( granted_.assign([=](taken_atom, bool result) {
[=](taken_atom, bool result) { if (result) {
if (result) { aout(this) << name_ << " has picked up chopsticks with IDs "
aout(this) << name_ << left_->id() << " and " << right_->id()
<< " has picked up chopsticks with IDs " << " and starts to eat\n";
<< left_->id() << " and " << right_->id() // eat some time
<< " and starts to eat\n"; delayed_send(this, seconds(5), think_atom_v);
// eat some time become(eating_);
delayed_send(this, seconds(5), think_atom::value); } else {
become(eating_); send(current_sender() == left_ ? right_ : left_, put_atom_v);
} else { send(this, eat_atom_v);
send(current_sender() == left_ ? right_ : left_, put_atom::value);
send(this, eat_atom::value);
become(thinking_);
}
}
);
// philosopher was *not* able to obtain the first chopstick
denied_.assign(
[=](taken_atom, bool result) {
if (result)
send(current_sender() == left_ ? left_ : right_, put_atom::value);
send(this, eat_atom::value);
become(thinking_); become(thinking_);
} }
); });
// philosopher was *not* able to obtain the first chopstick
denied_.assign([=](taken_atom, bool result) {
if (result)
send(current_sender() == left_ ? left_ : right_, put_atom_v);
send(this, eat_atom_v);
become(thinking_);
});
// philosopher obtained both chopstick and eats (for five seconds) // philosopher obtained both chopstick and eats (for five seconds)
eating_.assign( eating_.assign([=](think_atom) {
[=](think_atom) { send(left_, put_atom_v);
send(left_, put_atom::value); send(right_, put_atom_v);
send(right_, put_atom::value); delayed_send(this, seconds(5), eat_atom_v);
delayed_send(this, seconds(5), eat_atom::value); aout(this) << name_ << " puts down his chopsticks and starts to think\n";
aout(this) << name_ become(thinking_);
<< " puts down his chopsticks and starts to think\n"; });
become(thinking_);
}
);
} }
const char* name() const override { const char* name() const override {
...@@ -168,26 +154,26 @@ public: ...@@ -168,26 +154,26 @@ public:
protected: protected:
behavior make_behavior() override { behavior make_behavior() override {
// start thinking // start thinking
send(this, think_atom::value); send(this, think_atom_v);
// philosophers start to think after receiving {think} // philosophers start to think after receiving {think}
return ( return {
[=](think_atom) { [=](think_atom) {
aout(this) << name_ << " starts to think\n"; aout(this) << name_ << " starts to think\n";
delayed_send(this, seconds(5), eat_atom::value); delayed_send(this, seconds(5), eat_atom_v);
become(thinking_); become(thinking_);
} },
); };
} }
private: private:
std::string name_; // the name of this philosopher std::string name_; // the name of this philosopher
chopstick left_; // left chopstick chopstick left_; // left chopstick
chopstick right_; // right chopstick chopstick right_; // right chopstick
behavior thinking_; // initial behavior behavior thinking_; // initial behavior
behavior hungry_; // tries to take chopsticks behavior hungry_; // tries to take chopsticks
behavior granted_; // has one chopstick and waits for the second one behavior granted_; // has one chopstick and waits for the second one
behavior denied_; // could not get first chopsticks behavior denied_; // could not get first chopsticks
behavior eating_; // wait for some time, then go thinking again behavior eating_; // wait for some time, then go thinking again
}; };
} // namespace } // namespace
...@@ -203,10 +189,10 @@ void caf_main(actor_system& system) { ...@@ -203,10 +189,10 @@ void caf_main(actor_system& system) {
} }
aout(self) << endl; aout(self) << endl;
// spawn five philosophers // spawn five philosophers
std::vector<std::string> names {"Plato", "Hume", "Kant", std::vector<std::string> names{"Plato", "Hume", "Kant", "Nietzsche",
"Nietzsche", "Descartes"}; "Descartes"};
for (size_t i = 0; i < 5; ++i) for (size_t i = 0; i < 5; ++i)
self->spawn<philosopher>(names[i], chopsticks[i], chopsticks[(i + 1) % 5]); self->spawn<philosopher>(names[i], chopsticks[i], chopsticks[(i + 1) % 5]);
} }
CAF_MAIN() CAF_MAIN(id_block::dining_philosophers)
#include "caf/all.hpp" #include "caf/all.hpp"
using namespace caf;
using idle_atom = atom_constant<atom("idle")>; using namespace caf;
using request_atom = atom_constant<atom("request")>;
using response_atom = atom_constant<atom("response")>;
behavior server(event_based_actor* self) { behavior server(event_based_actor* self) {
self->set_default_handler(skip);
return { return {
[=](idle_atom, const actor& worker) { [=](idle_atom, const actor& worker) {
self->become ( self->become(keep_behavior, [=](ping_atom atm) {
keep_behavior, self->delegate(worker, atm);
[=](request_atom atm) { self->unbecome();
self->delegate(worker, atm); });
self->unbecome();
},
[=](idle_atom) {
return skip();
}
);
}, },
[=](request_atom) {
return skip();
}
}; };
} }
behavior client(event_based_actor* self, const actor& serv) { behavior client(event_based_actor* self, const actor& serv) {
self->link_to(serv); self->link_to(serv);
self->send(serv, idle_atom::value, self); self->send(serv, idle_atom_v, self);
return { return {
[=](request_atom) { [=](ping_atom) {
self->send(serv, idle_atom::value, self); self->send(serv, idle_atom_v, self);
return response_atom::value; return pong_atom_v;
} },
}; };
} }
...@@ -40,20 +29,18 @@ void caf_main(actor_system& system) { ...@@ -40,20 +29,18 @@ void caf_main(actor_system& system) {
auto serv = system.spawn(server); auto serv = system.spawn(server);
auto worker = system.spawn(client, serv); auto worker = system.spawn(client, serv);
scoped_actor self{system}; scoped_actor self{system};
self->request(serv, std::chrono::seconds(10), request_atom::value).receive( self->request(serv, std::chrono::seconds(10), ping_atom_v)
[&](response_atom) { .receive(
aout(self) << "received response from " [&](pong_atom) {
<< (self->current_sender() == worker ? "worker\n" aout(self) << "received response from "
: "server\n"); << (self->current_sender() == worker ? "worker\n"
}, : "server\n");
[&](error& err) { },
aout(self) << "received error " [&](error& err) {
<< system.render(err) aout(self) << "received error " << to_string(err) << " from "
<< " from " << (self->current_sender() == worker ? "worker\n"
<< (self->current_sender() == worker ? "worker\n" : "server\n");
: "server\n"); });
}
);
self->send_exit(serv, exit_reason::user_shutdown); self->send_exit(serv, exit_reason::user_shutdown);
} }
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* for both the blocking and the event-based API. * * for both the blocking and the event-based API. *
\******************************************************************************/ \******************************************************************************/
// Manual refs: lines 19-21, 31-72, 74-108, 140-145 (Actor) // Manual refs: lines 17-18, 21-26, 28-56, 58-92, 123-128 (Actor)
#include <iostream> #include <iostream>
...@@ -14,11 +14,9 @@ using namespace caf; ...@@ -14,11 +14,9 @@ using namespace caf;
namespace { namespace {
using add_atom = atom_constant<atom("add")>; using calculator_actor
using sub_atom = atom_constant<atom("sub")>; = typed_actor<replies_to<add_atom, int32_t, int32_t>::with<int32_t>,
replies_to<sub_atom, int32_t, int32_t>::with<int32_t>>;
using calculator_actor = typed_actor<replies_to<add_atom, int, int>::with<int>,
replies_to<sub_atom, int, int>::with<int>>;
// prototypes and forward declarations // prototypes and forward declarations
behavior calculator_fun(event_based_actor* self); behavior calculator_fun(event_based_actor* self);
...@@ -31,43 +29,30 @@ class typed_calculator; ...@@ -31,43 +29,30 @@ 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) { [](add_atom, int32_t a, int32_t b) { return a + b; },
return a + b; [](sub_atom, int32_t a, int32_t b) { return a - b; },
},
[](sub_atom, int a, int b) {
return a - b;
}
}; };
} }
// function-based, dynamically typed, blocking API // function-based, dynamically typed, blocking API
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) { [](add_atom, int32_t a, int32_t b) { return a + b; },
return a + b; [](sub_atom, int32_t a, int32_t b) { return a - b; },
},
[](sub_atom, int a, int 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));
running = false; running = false;
} }
} });
);
} }
// 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) { [](add_atom, int32_t a, int32_t b) { return a + b; },
return a + b; [](sub_atom, int32_t a, int32_t b) { return a - b; },
},
[](sub_atom, int a, int b) {
return a - b;
}
}; };
} }
...@@ -113,25 +98,25 @@ void tester(scoped_actor&) { ...@@ -113,25 +98,25 @@ 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: " << to_string(err) << endl;
<< self->system().render(err) << endl;
}; };
// first test: x + y = z // first test: x + y = z
self->request(hdl, infinite, add_atom::value, x, y).receive( self->request(hdl, infinite, add_atom_v, x, y)
[&](int res1) { .receive(
aout(self) << x << " + " << y << " = " << res1 << endl; [&](int32_t res1) {
// second test: x - y = z aout(self) << x << " + " << y << " = " << res1 << endl;
self->request(hdl, infinite, sub_atom::value, x, y).receive( // second test: x - y = z
[&](int res2) { self->request(hdl, infinite, sub_atom_v, x, y)
aout(self) << x << " - " << y << " = " << res2 << endl; .receive(
}, [&](int32_t res2) {
handle_err aout(self) << x << " - " << y << " = " << res2 << endl;
); },
}, handle_err);
handle_err },
); handle_err);
tester(self, std::forward<Ts>(xs)...); tester(self, std::forward<Ts>(xs)...);
} }
......
...@@ -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 = typed_actor<reacts_to<put_atom, int32_t>,
replies_to<get_atom>::with<int>>; replies_to<get_atom>::with<int32_t>>;
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) {
...@@ -49,9 +38,9 @@ void caf_main(actor_system& system) { ...@@ -49,9 +38,9 @@ void caf_main(actor_system& system) {
auto cell1 = system.spawn(type_checked_cell); auto cell1 = system.spawn(type_checked_cell);
auto cell2 = system.spawn(unchecked_cell); auto cell2 = system.spawn(unchecked_cell);
auto f = make_function_view(cell1); auto f = make_function_view(cell1);
cout << "cell value: " << f(get_atom::value) << endl; cout << "cell value: " << f(get_atom_v) << endl;
f(put_atom::value, 20); f(put_atom_v, 20);
cout << "cell value (after setting to 20): " << f(get_atom::value) << endl; cout << "cell value (after setting to 20): " << f(get_atom_v) << endl;
// get an unchecked cell and send it some garbage // get an unchecked cell and send it some garbage
anon_send(cell2, "hello there!"); anon_send(cell2, "hello there!");
} }
......
/******************************************************************************\ /******************************************************************************\
* This example illustrates how to do time-triggered loops in libcaf. * * This example illustrates how to do time-triggered loops in libcaf. *
\ ******************************************************************************/ \******************************************************************************/
#include <algorithm>
#include <chrono> #include <chrono>
#include <iostream> #include <iostream>
#include <algorithm>
#include "caf/all.hpp" #include "caf/all.hpp"
// This file is partially included in the manual, do not modify // This file is partially included in the manual, do not modify
// without updating the references in the *.tex files! // without updating the references in the *.tex files!
// Manual references: lines 56-75 (MessagePassing.tex) // Manual references: lines 58-75 (MessagePassing.tex)
using std::cout; using std::cout;
using std::endl; using std::endl;
...@@ -17,23 +18,24 @@ using std::pair; ...@@ -17,23 +18,24 @@ using std::pair;
using namespace caf; using namespace caf;
using step_atom = atom_constant<atom("step")>;
// ASCII art figures // ASCII art figures
constexpr const char* figures[] = { constexpr const char* figures[] = {
"<(^.^<)", "<(^.^<)",
"<(^.^)>", "<(^.^)>",
"(>^.^)>" "(>^.^)>",
}; };
struct animation_step { size_t figure_idx; size_t offset; }; struct animation_step {
size_t figure_idx;
size_t offset;
};
// array of {figure, offset} pairs // array of {figure, offset} pairs
constexpr animation_step animation_steps[] = { constexpr animation_step animation_steps[] = {
{1, 7}, {0, 7}, {0, 6}, {0, 5}, {1, 5}, {2, 5}, {2, 6}, {1, 7}, {0, 7}, {0, 6}, {0, 5}, {1, 5}, {2, 5}, {2, 6},
{2, 7}, {2, 8}, {2, 9}, {2, 10}, {1, 10}, {0, 10}, {0, 9}, {2, 7}, {2, 8}, {2, 9}, {2, 10}, {1, 10}, {0, 10}, {0, 9},
{1, 9}, {2, 10}, {2, 11}, {2, 12}, {2, 13}, {1, 13}, {0, 13}, {1, 9}, {2, 10}, {2, 11}, {2, 12}, {2, 13}, {1, 13}, {0, 13},
{0, 12}, {0, 11}, {0, 10}, {0, 9}, {0, 8}, {0, 7}, {1, 7} {0, 12}, {0, 11}, {0, 10}, {0, 9}, {0, 8}, {0, 7}, {1, 7},
}; };
constexpr size_t animation_width = 20; constexpr size_t animation_width = 20;
...@@ -56,22 +58,20 @@ void draw_kirby(const animation_step& animation) { ...@@ -56,22 +58,20 @@ void draw_kirby(const animation_step& animation) {
// uses a message-based loop to iterate over all animation steps // uses a message-based loop to iterate over all animation steps
void dancing_kirby(event_based_actor* self) { void dancing_kirby(event_based_actor* self) {
// let's get it started // let's get it started
self->send(self, step_atom::value, size_t{0}); self->send(self, update_atom_v, size_t{0});
self->become ( self->become([=](update_atom, size_t step) {
[=](step_atom, size_t step) { if (step == sizeof(animation_step)) {
if (step == sizeof(animation_step)) { // we've printed all animation steps (done)
// we've printed all animation steps (done) cout << endl;
cout << endl; self->quit();
self->quit(); return;
return;
}
// print given step
draw_kirby(animation_steps[step]);
// animate next step in 150ms
self->delayed_send(self, std::chrono::milliseconds(150),
step_atom::value, step + 1);
} }
); // print given step
draw_kirby(animation_steps[step]);
// animate next step in 150ms
self->delayed_send(self, std::chrono::milliseconds(150), update_atom_v,
step + 1);
});
} }
void caf_main(actor_system& system) { void caf_main(actor_system& system) {
......
#include <iostream> #include <iostream>
#include "caf/all.hpp" #include "caf/all.hpp"
// This file is partially included in the manual, do not modify // This file is partially included in the manual, do not modify
// without updating the references in the *.tex files! // without updating the references in the *.tex files!
// Manual references: lines 15-42 (MessagePassing.tex) // Manual references: lines 15-36 (MessagePassing.tex)
using std::endl;
using namespace caf; 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<replies_to<add_atom, int32_t, int32_t>::with<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::value, 1, 2).then( self->request(worker, std::chrono::seconds(10), add_atom_v, 1, 2)
[=](int result) { .then([=](int32_t result) { //
aout(self) << "1 + 2 = " << result << endl; 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);
} },
}; };
} }
calc::behavior_type actor_c() { calc::behavior_type actor_c() {
return { return {
[](add_atom, int x, int y) { [](add_atom, int32_t x, int32_t y) { return x + y; },
return x + y;
}
}; };
} }
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
* A very basic, interactive divider. * * A very basic, interactive divider. *
\******************************************************************************/ \******************************************************************************/
// Manual refs: 19-25, 35-48, 68-77 (MessagePassing); // Manual refs: 17-19, 49-59, 70-76 (MessagePassing);
// 19-34, 50-58 (Error) // 17-47 (Error)
#include <iostream> #include <iostream>
...@@ -14,16 +14,10 @@ using std::endl; ...@@ -14,16 +14,10 @@ using std::endl;
using std::flush; using std::flush;
using namespace caf; using namespace caf;
namespace {
enum class math_error : uint8_t { enum class math_error : uint8_t {
division_by_zero = 1 division_by_zero = 1,
}; };
error make_error(math_error x) {
return {static_cast<uint8_t>(x), atom("math")};
}
std::string to_string(math_error x) { std::string to_string(math_error x) {
switch (x) { switch (x) {
case math_error::division_by_zero: case math_error::division_by_zero:
...@@ -33,7 +27,13 @@ std::string to_string(math_error x) { ...@@ -33,7 +27,13 @@ std::string to_string(math_error x) {
} }
} }
using div_atom = atom_constant<atom("div")>; CAF_BEGIN_TYPE_ID_BLOCK(divider, first_custom_type_id)
CAF_ADD_TYPE_ID(divider, (math_error))
CAF_END_TYPE_ID_BLOCK(divider)
CAF_ERROR_CODE_ENUM(math_error)
using divider = typed_actor<replies_to<div_atom, double, double>::with<double>>; using divider = typed_actor<replies_to<div_atom, double, double>::with<double>>;
...@@ -43,21 +43,11 @@ divider::behavior_type divider_impl() { ...@@ -43,21 +43,11 @@ divider::behavior_type divider_impl() {
if (y == 0.0) if (y == 0.0)
return math_error::division_by_zero; return math_error::division_by_zero;
return x / y; return x / y;
} },
}; };
} }
class config : public actor_system_config { void caf_main(actor_system& system) {
public:
config() {
auto renderer = [](uint8_t x, atom_value, const message&) {
return "math_error" + deep_to_string_as_tuple(static_cast<math_error>(x));
};
add_error_category(atom("math"), renderer);
}
};
void caf_main(actor_system& system, const config&) {
double x; double x;
double y; double y;
cout << "x: " << flush; cout << "x: " << flush;
...@@ -66,17 +56,13 @@ void caf_main(actor_system& system, const config&) { ...@@ -66,17 +56,13 @@ void caf_main(actor_system& system, const config&) {
std::cin >> y; std::cin >> y;
auto div = system.spawn(divider_impl); auto div = system.spawn(divider_impl);
scoped_actor self{system}; scoped_actor self{system};
self->request(div, std::chrono::seconds(10), div_atom::value, x, y).receive( self->request(div, std::chrono::seconds(10), div_atom_v, x, y)
[&](double z) { .receive(
aout(self) << x << " / " << y << " = " << z << endl; [&](double z) { aout(self) << x << " / " << y << " = " << z << endl; },
}, [&](const error& err) {
[&](const error& err) { aout(self) << "*** cannot compute " << x << " / " << y << " => "
aout(self) << "*** cannot compute " << x << " / " << y << " => " << to_string(err) << endl;
<< system.render(err) << endl; });
}
);
} }
} // namespace CAF_MAIN(id_block::divider)
CAF_MAIN()
#include "caf/all.hpp"
#include <cassert> #include <cassert>
#include <cstdint> #include <cstdint>
#include <iostream> #include <iostream>
#include "caf/all.hpp"
using std::endl; enum class ec : uint8_t {
using namespace caf; push_to_full = 1,
pop_from_empty,
};
namespace { CAF_BEGIN_TYPE_ID_BLOCK(fixed_stack, first_custom_type_id)
using pop_atom = atom_constant<atom("pop")>; CAF_ADD_TYPE_ID(fixed_stack, (ec))
using push_atom = atom_constant<atom("push")>;
enum class fixed_stack_errc : uint8_t { push_to_full = 1, pop_from_empty }; CAF_ADD_ATOM(fixed_stack, custom, pop_atom, "pop")
CAF_ADD_ATOM(fixed_stack, custom, push_atom, "push")
error make_error(fixed_stack_errc x) { CAF_END_TYPE_ID_BLOCK(fixed_stack)
return error{static_cast<uint8_t>(x), atom("FixedStack")};
} CAF_ERROR_CODE_ENUM(ec)
using std::endl;
using namespace caf;
using namespace custom;
class fixed_stack : public event_based_actor { class fixed_stack : public event_based_actor {
public: public:
fixed_stack(actor_config& cfg, size_t stack_size) fixed_stack(actor_config& cfg, size_t stack_size)
: event_based_actor(cfg), : event_based_actor(cfg), size_(stack_size) {
size_(stack_size) { full_.assign( //
full_.assign( [=](push_atom, int) -> error { return ec::push_to_full; },
[=](push_atom, int) -> error {
return fixed_stack_errc::push_to_full;
},
[=](pop_atom) -> int { [=](pop_atom) -> int {
auto result = data_.back(); auto result = data_.back();
data_.pop_back(); data_.pop_back();
become(filled_); become(filled_);
return result; return result;
} });
); filled_.assign( //
filled_.assign(
[=](push_atom, int what) { [=](push_atom, int what) {
data_.push_back(what); data_.push_back(what);
if (data_.size() == size_) if (data_.size() == size_)
...@@ -45,17 +48,13 @@ public: ...@@ -45,17 +48,13 @@ public:
if (data_.empty()) if (data_.empty())
become(empty_); become(empty_);
return result; return result;
} });
); empty_.assign( //
empty_.assign(
[=](push_atom, int what) { [=](push_atom, int what) {
data_.push_back(what); data_.push_back(what);
become(filled_); become(filled_);
}, },
[=](pop_atom) -> error { [=](pop_atom) -> error { return ec::pop_from_empty; });
return fixed_stack_errc::pop_from_empty;
}
);
} }
behavior make_behavior() override { behavior make_behavior() override {
...@@ -76,24 +75,17 @@ void caf_main(actor_system& system) { ...@@ -76,24 +75,17 @@ void caf_main(actor_system& system) {
auto st = self->spawn<fixed_stack>(5u); auto st = self->spawn<fixed_stack>(5u);
// fill stack // fill stack
for (int i = 0; i < 10; ++i) for (int i = 0; i < 10; ++i)
self->send(st, push_atom::value, i); self->send(st, push_atom_v, i);
// drain stack // drain stack
aout(self) << "stack: { "; aout(self) << "stack: { ";
bool stack_empty = false; bool stack_empty = false;
while (!stack_empty) { while (!stack_empty) {
self->request(st, std::chrono::seconds(10), pop_atom::value).receive( self->request(st, std::chrono::seconds(10), pop_atom_v)
[&](int x) { .receive([&](int x) { aout(self) << x << " "; },
aout(self) << x << " "; [&](const error&) { stack_empty = true; });
},
[&](const error&) {
stack_empty = true;
}
);
} }
aout(self) << "}" << endl; aout(self) << "}" << endl;
self->send_exit(st, exit_reason::user_shutdown); self->send_exit(st, exit_reason::user_shutdown);
} }
} // namespace CAF_MAIN(id_block::fixed_stack)
CAF_MAIN()
...@@ -15,16 +15,13 @@ using std::endl; ...@@ -15,16 +15,13 @@ using std::endl;
using namespace caf; using namespace caf;
// using add_atom = atom_constant<atom("add")>; (defined in atom.hpp) using adder
= typed_actor<replies_to<add_atom, int32_t, int32_t>::with<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,19 +29,19 @@ adder::behavior_type worker() { ...@@ -32,19 +29,19 @@ 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;
} },
}; };
} }
void caf_main(actor_system& system) { void caf_main(actor_system& system) {
auto f = make_function_view(system.spawn(calculator_master)); auto f = make_function_view(system.spawn(calculator_master));
cout << "12 + 13 = " << f(add_atom::value, 12, 13) << endl; cout << "12 + 13 = " << f(add_atom_v, 12, 13) << endl;
} }
CAF_MAIN() CAF_MAIN()
...@@ -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,50 +18,46 @@ using std::vector; ...@@ -17,50 +18,46 @@ 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 = typed_actor<reacts_to<put_atom, int32_t>,
replies_to<get_atom>::with<int>>; replies_to<get_atom>::with<int32_t>>;
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::value).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::value).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;
}); });
} }
void blocking_testee(blocking_actor* self, vector<cell> cells) { void blocking_testee(blocking_actor* self, vector<cell> cells) {
for (auto& x : cells) for (auto& x : cells)
self->request(x, seconds(1), get_atom::value).receive( self->request(x, seconds(1), get_atom_v)
[&](int y) { .receive(
aout(self) << "cell #" << x.id() << " -> " << y << endl; [&](int32_t y) {
}, aout(self) << "cell #" << x.id() << " -> " << y << endl;
[&](error& err) { },
aout(self) << "cell #" << x.id() [&](error& err) {
<< " -> " << self->system().render(err) << endl; aout(self) << "cell #" << x.id() << " -> " << to_string(err) << endl;
} });
);
} }
void caf_main(actor_system& system) { void caf_main(actor_system& system) {
......
/******************************************************************************\ /******************************************************************************\
* This example is a very basic, non-interactive math service implemented * * This example is a very basic, non-interactive math service implemented *
* using typed actors. * * using typed actors. *
\ ******************************************************************************/ \******************************************************************************/
#include "caf/all.hpp"
#include <cassert> #include <cassert>
#include <iostream> #include <iostream>
#include "caf/all.hpp"
using std::endl; using std::endl;
using namespace caf; using namespace caf;
namespace { namespace {
using plus_atom = atom_constant<atom("plus")>; using calculator_type
using minus_atom = atom_constant<atom("minus")>; = typed_actor<replies_to<add_atom, int32_t, int32_t>::with<int32_t>,
using result_atom = atom_constant<atom("result")>; replies_to<sub_atom, int32_t, int32_t>::with<int32_t>>;
using calculator_type =
typed_actor<replies_to<plus_atom, int, int>::with<result_atom, int>,
replies_to<minus_atom, int, int>::with<result_atom, int>>;
calculator_type::behavior_type typed_calculator_fun(calculator_type::pointer) { calculator_type::behavior_type typed_calculator_fun(calculator_type::pointer) {
return { return {
[](plus_atom, int x, int y) { [](add_atom, int32_t x, int32_t y) { return x + y; },
return std::make_tuple(result_atom::value, x + y); [](sub_atom, int32_t x, int32_t y) { return x - y; },
},
[](minus_atom, int x, int y) {
return std::make_tuple(result_atom::value, x - y);
}
}; };
} }
...@@ -40,12 +32,8 @@ public: ...@@ -40,12 +32,8 @@ public:
protected: protected:
behavior_type make_behavior() override { behavior_type make_behavior() override {
return { return {
[](plus_atom, int x, int y) { [](add_atom, int32_t x, int32_t y) { return x + y; },
return std::make_tuple(result_atom::value, x + y); [](sub_atom, int32_t x, int32_t y) { return x - y; },
},
[](minus_atom, int x, int y) {
return std::make_tuple(result_atom::value, x - y);
}
}; };
} }
}; };
...@@ -53,26 +41,23 @@ protected: ...@@ -53,26 +41,23 @@ protected:
void tester(event_based_actor* self, const calculator_type& testee) { void tester(event_based_actor* self, const calculator_type& testee) {
self->link_to(testee); self->link_to(testee);
// first test: 2 + 1 = 3 // first test: 2 + 1 = 3
self->request(testee, infinite, plus_atom::value, 2, 1).then( self->request(testee, infinite, add_atom_v, 2, 1)
[=](result_atom, int r1) { .then(
// second test: 2 - 1 = 1 [=](int32_t r1) {
self->request(testee, infinite, minus_atom::value, 2, 1).then( // second test: 2 - 1 = 1
[=](result_atom, 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" aout(self) << "AUT (actor under test) seems to be ok" << endl;
<< endl;
} }
self->send_exit(testee, exit_reason::user_shutdown); self->send_exit(testee, exit_reason::user_shutdown);
} });
); },
}, [=](const error& err) {
[=](const error& err) { aout(self) << "AUT (actor under test) failed: " << to_string(err)
aout(self) << "AUT (actor under test) failed: " << endl;
<< self->system().render(err) << endl; self->quit(exit_reason::user_shutdown);
self->quit(exit_reason::user_shutdown); });
}
);
} }
void caf_main(actor_system& system) { void caf_main(actor_system& system) {
......
...@@ -79,7 +79,7 @@ void ChatWidget::sendChatMessage() { ...@@ -79,7 +79,7 @@ void ChatWidget::sendChatMessage() {
auto x = system().groups().get(mod, g); auto x = system().groups().get(mod, g);
if (! x) if (! x)
print("*** error: " print("*** error: "
+ QString::fromUtf8(system().render(x.error()).c_str())); + QString::fromUtf8(to_string(x.error()).c_str()));
else else
self()->send(self(), atom("join"), std::move(*x)); self()->send(self(), atom("join"), std::move(*x));
}, },
...@@ -128,7 +128,7 @@ void ChatWidget::joinGroup() { ...@@ -128,7 +128,7 @@ void ChatWidget::joinGroup() {
string gid = gname.midRef(pos+1).toUtf8().constData(); string gid = gname.midRef(pos+1).toUtf8().constData();
auto x = system().groups().get(mod, gid); auto x = system().groups().get(mod, gid);
if (! x) if (! x)
QMessageBox::critical(this, "Error", system().render(x.error()).c_str()); QMessageBox::critical(this, "Error", to_string(x.error()).c_str());
else else
self()->send(self(), join_atom::value, std::move(*x)); self()->send(self(), join_atom::value, std::move(*x));
} }
......
...@@ -59,9 +59,8 @@ int main(int argc, char** argv) { ...@@ -59,9 +59,8 @@ int main(int argc, char** argv) {
auto group_uri = cfg.group_id.substr(p + 1); auto group_uri = cfg.group_id.substr(p + 1);
auto g = system.groups().get(module, group_uri); auto g = system.groups().get(module, group_uri);
if (! g) { if (! g) {
cerr << "*** unable to get group " << group_uri cerr << "*** unable to get group " << group_uri << " from module "
<< " from module " << module << ": " << module << ": " << to_string(g.error()) << endl;
<< system.render(g.error()) << endl;
return -1; return -1;
} }
grp = std::move(*g); grp = std::move(*g);
......
...@@ -6,59 +6,63 @@ ...@@ -6,59 +6,63 @@
* - ./build/bin/group_chat -s -p 4242 * * - ./build/bin/group_chat -s -p 4242 *
* - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n alice * * - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n alice *
* - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n bob * * - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n bob *
\ ******************************************************************************/ \******************************************************************************/
#include <set>
#include <map>
#include <vector>
#include <cstdlib> #include <cstdlib>
#include <sstream>
#include <iostream> #include <iostream>
#include <map>
#include <set>
#include <sstream>
#include <vector>
#include "caf/all.hpp" #include "caf/all.hpp"
#include "caf/io/all.hpp" #include "caf/io/all.hpp"
#include "caf/string_algorithms.hpp" #include "caf/string_algorithms.hpp"
using namespace std; CAF_BEGIN_TYPE_ID_BLOCK(group_chat, first_custom_type_id)
using namespace caf;
namespace { CAF_ADD_ATOM(group_chat, custom, broadcast_atom, "broadcast")
using broadcast_atom = atom_constant<atom("broadcast")>; CAF_END_TYPE_ID_BLOCK(group_chat)
struct line { string str; }; using namespace caf;
using namespace custom;
istream& operator>>(istream& is, line& l) { struct line {
getline(is, l.str); std::string str;
};
std::istream& operator>>(std::istream& is, line& l) {
std::getline(is, l.str);
return is; return is;
} }
behavior client(event_based_actor* self, const string& name) { behavior client(event_based_actor* self, const std::string& name) {
return { return {
[=](broadcast_atom, const string& message) { [=](broadcast_atom, const std::string& message) {
for(auto& dest : self->joined_groups()) { for (auto& dest : self->joined_groups()) {
self->send(dest, name + ": " + message); self->send(dest, name + ": " + message);
} }
}, },
[=](join_atom, const group& what) { [=](join_atom, const group& what) {
for (const auto& g : self->joined_groups()) { for (const auto& g : self->joined_groups()) {
cout << "*** leave " << to_string(g) << endl; std::cout << "*** leave " << to_string(g) << std::endl;
self->send(g, name + " has left the chatroom"); self->send(g, name + " has left the chatroom");
self->leave(g); self->leave(g);
} }
cout << "*** join " << to_string(what) << endl; std::cout << "*** join " << to_string(what) << std::endl;
self->join(what); self->join(what);
self->send(what, name + " has entered the chatroom"); self->send(what, name + " has entered the chatroom");
}, },
[=](const string& txt) { [=](const std::string& txt) {
// don't print own messages // don't print own messages
if (self->current_sender() != self) if (self->current_sender() != self)
cout << txt << endl; std::cout << txt << std::endl;
}, },
[=](const group_down_msg& g) { [=](const group_down_msg& g) {
cout << "*** chatroom offline: " << to_string(g.source) << endl; std::cout << "*** chatroom offline: " << to_string(g.source) << std::endl;
} },
}; };
} }
...@@ -71,81 +75,81 @@ public: ...@@ -71,81 +75,81 @@ public:
config() { config() {
opt_group{custom_options_, "global"} opt_group{custom_options_, "global"}
.add(name, "name,n", "set name") .add(name, "name,n", "set name")
.add(group_uris, "group,g", "join group") .add(group_uris, "group,g", "join group")
.add(server_mode, "server,s", "run in server mode") .add(server_mode, "server,s", "run in server mode")
.add(port, "port,p", "set port (ignored in client mode)"); .add(port, "port,p", "set port (ignored in client mode)");
} }
}; };
void run_server(actor_system& system, const config& cfg) { void run_server(actor_system& system, const config& cfg) {
auto res = system.middleman().publish_local_groups(cfg.port); auto res = system.middleman().publish_local_groups(cfg.port);
if (! res) { if (!res) {
std::cerr << "*** publishing local groups failed: " std::cerr << "*** publishing local groups failed: "
<< system.render(res.error()) << endl; << to_string(res.error()) << std::endl;
return; return;
} }
cout << "*** listening at port " << *res << endl std::cout << "*** listening at port " << *res << std::endl
<< "*** press [enter] to quit" << endl; << "*** press [enter] to quit" << std::endl;
string dummy; std::string dummy;
std::getline(std::cin, dummy); std::getline(std::cin, dummy);
cout << "... cya" << endl; std::cout << "... cya" << std::endl;
} }
void run_client(actor_system& system, const config& cfg) { void run_client(actor_system& system, const config& cfg) {
auto name = cfg.name; auto name = cfg.name;
while (name.empty()) { while (name.empty()) {
cout << "please enter your name: " << flush; std::cout << "please enter your name: " << std::flush;
if (!getline(cin, name)) { if (!std::getline(std::cin, name)) {
cerr << "*** no name given... terminating" << endl; std::cerr << "*** no name given... terminating" << std::endl;
return; return;
} }
} }
cout << "*** starting client, type '/help' for a list of commands" << endl; std::cout << "*** starting client, type '/help' for a list of commands\n";
auto client_actor = system.spawn(client, name); auto client_actor = system.spawn(client, name);
for (auto& uri : cfg.group_uris) { for (auto& uri : cfg.group_uris) {
auto tmp = system.groups().get(uri); auto tmp = system.groups().get(uri);
if (tmp) if (tmp)
anon_send(client_actor, join_atom::value, std::move(*tmp)); anon_send(client_actor, join_atom_v, std::move(*tmp));
else else
cerr << R"(*** failed to parse ")" << uri << R"(" as group URI: )" std::cerr << R"(*** failed to parse ")" << uri << R"(" as group URI: )"
<< system.render(tmp.error()) << endl; << to_string(tmp.error()) << std::endl;
} }
istream_iterator<line> eof; std::istream_iterator<line> eof;
vector<string> words; std::vector<std::string> words;
for (istream_iterator<line> i(cin); i != eof; ++i) { for (std::istream_iterator<line> i{std::cin}; i != eof; ++i) {
auto send_input = [&] { auto send_input = [&] {
if (!i->str.empty()) if (!i->str.empty())
anon_send(client_actor, broadcast_atom::value, i->str); anon_send(client_actor, broadcast_atom_v, i->str);
}; };
words.clear(); words.clear();
split(words, i->str, is_any_of(" ")); split(words, i->str, is_any_of(" "));
auto res = message_builder(words.begin(), words.end()).apply({ message_handler f{
[&](const string& cmd, const string& mod, const string& id) { [&](const std::string& cmd, const std::string& mod,
const std::string& id) {
if (cmd == "/join") { if (cmd == "/join") {
auto grp = system.groups().get(mod, id); auto grp = system.groups().get(mod, id);
if (grp) if (grp)
anon_send(client_actor, join_atom::value, *grp); anon_send(client_actor, join_atom_v, *grp);
} } else {
else {
send_input(); send_input();
} }
}, },
[&](const string& cmd) { [&](const std::string& cmd) {
if (cmd == "/quit") { if (cmd == "/quit") {
cin.setstate(ios_base::eofbit); std::cin.setstate(std::ios_base::eofbit);
} } else if (cmd[0] == '/') {
else if (cmd[0] == '/') { std::cout << "*** available commands:\n"
cout << "*** available commands:\n" " /join <module> <group> join a new chat channel\n"
" /join <module> <group> join a new chat channel\n" " /quit quit the program\n"
" /quit quit the program\n" " /help print this text\n";
" /help print this text\n" << flush; } else {
}
else {
send_input(); send_input();
} }
} },
}); };
auto msg = message_builder(words.begin(), words.end()).move_to_message();
auto res = f(msg);
if (!res) if (!res)
send_input(); send_input();
} }
...@@ -158,6 +162,4 @@ void caf_main(actor_system& system, const config& cfg) { ...@@ -158,6 +162,4 @@ void caf_main(actor_system& system, const config& cfg) {
f(system, cfg); f(system, cfg);
} }
} // namespace CAF_MAIN(id_block::group_chat, io::middleman)
CAF_MAIN(io::middleman)
...@@ -7,45 +7,46 @@ ...@@ -7,45 +7,46 @@
// Run client at the same host: // Run client at the same host:
// - remote_spawn -H localhost -p 4242 // - remote_spawn -H localhost -p 4242
// Manual refs: 33-39, 99-101,106,110 (ConfiguringActorApplications)
// 125-143 (RemoteSpawn)
#include <array> #include <array>
#include <vector>
#include <string>
#include <sstream>
#include <cassert> #include <cassert>
#include <iostream>
#include <functional> #include <functional>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include "caf/all.hpp" #include "caf/all.hpp"
#include "caf/io/all.hpp" #include "caf/io/all.hpp"
using std::cout; // --(rst-calculator-begin)--
using calculator = caf::typed_actor<
caf::replies_to<caf::add_atom, int32_t, int32_t>::with<int32_t>,
caf::replies_to<caf::sub_atom, int32_t, int32_t>::with<int32_t>>;
// --(rst-calculator-end)--
CAF_BEGIN_TYPE_ID_BLOCK(remote_spawn, first_custom_type_id)
CAF_ADD_TYPE_ID(remote_spawn, (calculator))
CAF_END_TYPE_ID_BLOCK(remote_spawn)
using std::cerr; using std::cerr;
using std::cout;
using std::endl; using std::endl;
using std::string; using std::string;
using namespace caf; using namespace caf;
namespace {
using add_atom = atom_constant<atom("add")>;
using sub_atom = atom_constant<atom("sub")>;
using calculator = typed_actor<replies_to<add_atom, int, int>::with<int>,
replies_to<sub_atom, int, int>::with<int>>;
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;
} },
}; };
} }
...@@ -62,10 +63,11 @@ string trim(string s) { ...@@ -62,10 +63,11 @@ string trim(string s) {
// implements our main loop for reading user input // implements our main loop for reading user input
void client_repl(function_view<calculator> f) { 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 << endl; << " <x> - <y> : subtracts two int32_tegers" << endl
<< endl;
}; };
usage(); usage();
// read next line, split it, and evaluate user input // read next line, split it, and evaluate user input
...@@ -79,20 +81,21 @@ void client_repl(function_view<calculator> f) { ...@@ -79,20 +81,21 @@ 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
cout << " = " << (words[1] == "+" ? f(add_atom::value, *x, *y) cout << " = "
: f(sub_atom::value, *x, *y)) << "\n"; << (words[1] == "+" ? f(add_atom_v, *x, *y) : f(sub_atom_v, *x, *y))
<< "\n";
} }
} }
...@@ -100,9 +103,9 @@ struct config : actor_system_config { ...@@ -100,9 +103,9 @@ struct config : actor_system_config {
config() { config() {
add_actor_type("calculator", calculator_fun); add_actor_type("calculator", calculator_fun);
opt_group{custom_options_, "global"} opt_group{custom_options_, "global"}
.add(port, "port,p", "set port") .add(port, "port,p", "set port")
.add(host, "host,H", "set node (ignored in server mode)") .add(host, "host,H", "set node (ignored in server mode)")
.add(server_mode, "server-mode,s", "enable server mode"); .add(server_mode, "server-mode,s", "enable server mode");
} }
uint16_t port = 0; uint16_t port = 0;
string host = "localhost"; string host = "localhost";
...@@ -112,31 +115,28 @@ struct config : actor_system_config { ...@@ -112,31 +115,28 @@ struct config : actor_system_config {
void server(actor_system& system, const config& cfg) { void server(actor_system& system, const config& cfg) {
auto res = system.middleman().open(cfg.port); auto res = system.middleman().open(cfg.port);
if (!res) { if (!res) {
cerr << "*** cannot open port: " cerr << "*** cannot open port: " << to_string(res.error()) << endl;
<< system.render(res.error()) << endl;
return; return;
} }
cout << "*** running on port: " cout << "*** running on port: " << *res << endl
<< *res << endl
<< "*** press <enter> to shutdown server" << endl; << "*** press <enter> to shutdown server" << endl;
getchar(); getchar();
} }
// --(rst-client-begin)--
void client(actor_system& system, const config& cfg) { void client(actor_system& system, const config& cfg) {
auto node = system.middleman().connect(cfg.host, cfg.port); auto node = system.middleman().connect(cfg.host, cfg.port);
if (!node) { if (!node) {
cerr << "*** connect failed: " cerr << "*** connect failed: " << to_string(node.error()) << endl;
<< system.render(node.error()) << endl;
return; return;
} }
auto type = "calculator"; // type of the actor we wish to spawn auto type = "calculator"; // type of the actor we wish to spawn
auto args = make_message(); // arguments to construct the actor auto args = make_message(); // arguments to construct the actor
auto tout = std::chrono::seconds(30); // wait no longer than 30s auto tout = std::chrono::seconds(30); // wait no longer than 30s
auto worker = system.middleman().remote_spawn<calculator>(*node, type, auto worker = system.middleman().remote_spawn<calculator>(*node, type, args,
args, tout); tout);
if (!worker) { if (!worker) {
cerr << "*** remote spawn failed: " cerr << "*** remote spawn failed: " << to_string(worker.error()) << endl;
<< system.render(worker.error()) << endl;
return; return;
} }
// start using worker in main loop // start using worker in main loop
...@@ -144,12 +144,11 @@ void client(actor_system& system, const config& cfg) { ...@@ -144,12 +144,11 @@ void client(actor_system& system, const config& cfg) {
// be a good citizen and terminate remotely spawned actor before exiting // be a good citizen and terminate remotely spawned actor before exiting
anon_send_exit(*worker, exit_reason::kill); anon_send_exit(*worker, exit_reason::kill);
} }
// --(rst-client-end)--
void caf_main(actor_system& system, const config& cfg) { void caf_main(actor_system& system, const config& cfg) {
auto f = cfg.server_mode ? server : client; auto f = cfg.server_mode ? server : client;
f(system, cfg); f(system, cfg);
} }
} // namespace CAF_MAIN(id_block::remote_spawn, io::middleman)
CAF_MAIN(io::middleman)
...@@ -2,109 +2,127 @@ ...@@ -2,109 +2,127 @@
* Basic, non-interactive streaming example for processing integers. * * Basic, non-interactive streaming example for processing integers. *
******************************************************************************/ ******************************************************************************/
// Manual refs: lines 17-46, 48-78, 80-107, 121-125 (Streaming)
#include <iostream> #include <iostream>
#include <vector> #include <vector>
#include "caf/all.hpp" #include "caf/all.hpp"
CAF_BEGIN_TYPE_ID_BLOCK(integer_stream, first_custom_type_id)
CAF_ADD_TYPE_ID(integer_stream, (caf::stream<int32_t>) )
CAF_ADD_TYPE_ID(integer_stream, (std::vector<int32_t>) )
CAF_END_TYPE_ID_BLOCK(integer_stream)
using std::endl; using std::endl;
using namespace caf; using namespace caf;
namespace { namespace {
// --(rst-source-begin)--
// Simple source for generating a stream of integers from [0, n). // Simple source for generating a stream of integers from [0, n).
behavior int_source(event_based_actor* self) { behavior int_source(event_based_actor* self) {
return {[=](open_atom, int n) { return {
// Produce at least one value. [=](open_atom, int32_t n) {
if (n <= 0) // Produce at least one value.
n = 1; if (n <= 0)
// Create a stream manager for implementing a stream source. The n = 1;
// streaming logic requires three functions: initializer, generator, and // Create a stream manager for implementing a stream source. The
// predicate. // streaming logic requires three functions: initializer, generator, and
return attach_stream_source( // predicate.
self, return attach_stream_source(
// Initializer. The type of the first argument (state) is freely self,
// chosen. If no state is required, `caf::unit_t` can be used here. // Initializer. The type of the first argument (state) is freely
[](int& x) { x = 0; }, // chosen. If no state is required, `caf::unit_t` can be used here.
// Generator. This function is called by CAF to produce new stream [](int32_t& x) { x = 0; },
// elements for downstream actors. The `x` argument is our state again // Generator. This function is called by CAF to produce new stream
// (with our freely chosen type). The second argument `out` points to // elements for downstream actors. The `x` argument is our state again
// the output buffer. The template argument (here: int) determines what // (with our freely chosen type). The second argument `out` points to
// elements downstream actors receive in this stream. Finally, `num` is // the output buffer. The template argument (here: int) determines what
// a hint from CAF how many elements we should ideally insert into // elements downstream actors receive in this stream. Finally, `num` is
// `out`. We can always insert fewer or more items. // a hint from CAF how many elements we should ideally insert into
[n](int& x, downstream<int>& out, size_t num) { // `out`. We can always insert fewer or more items.
auto max_x = std::min(x + static_cast<int>(num), n); [n](int32_t& x, downstream<int32_t>& out, size_t num) {
for (; x < max_x; ++x) auto max_x = std::min(x + static_cast<int>(num), n);
out.push(x); for (; x < max_x; ++x)
}, out.push(x);
// Predicate. This function tells CAF when we reached the end. },
[n](const int& x) { return x == n; }); // Predicate. This function tells CAF when we reached the end.
}}; [n](const int32_t& x) { return x == n; });
},
};
} }
// --(rst-source-end)--
// --(rst-stage-begin)--
// Simple stage that only selects even numbers. // Simple stage that only selects even numbers.
behavior int_selector(event_based_actor* self) { behavior int_selector(event_based_actor* self) {
return {[=](stream<int> in) { return {
// Create a stream manager for implementing a stream stage. Similar to [=](stream<int32_t> in) {
// `make_source`, we need three functions: initialzer, processor, and // Create a stream manager for implementing a stream stage. Similar to
// finalizer. // `make_source`, we need three functions: initialzer, processor, and
return attach_stream_stage( // finalizer.
self, return attach_stream_stage(
// Our input source. self,
in, // Our input source.
// Initializer. Here, we don't need any state and simply use unit_t. in,
[](unit_t&) { // Initializer. Here, we don't need any state and simply use unit_t.
// nop [](unit_t&) {
}, // nop
// Processor. This function takes individual input elements as `val` },
// and forwards even integers to `out`. // Processor. This function takes individual input elements as `val`
[](unit_t&, downstream<int>& out, int val) { // and forwards even integers to `out`.
if (val % 2 == 0) [](unit_t&, downstream<int32_t>& out, int32_t val) {
out.push(val); if (val % 2 == 0)
}, out.push(val);
// Finalizer. Allows us to run cleanup code once the stream terminates. },
[=](unit_t&, const error& err) { // Finalizer. Allows us to run cleanup code once the stream terminates.
if (err) { [=](unit_t&, const error& err) {
aout(self) << "int_selector aborted with error: " << err << std::endl; if (err) {
} else { aout(self) << "int_selector aborted with error: " << err
aout(self) << "int_selector finalized" << std::endl; << std::endl;
} } else {
// else: regular stream shutdown aout(self) << "int_selector finalized" << std::endl;
}); }
}}; // else: regular stream shutdown
});
},
};
} }
// --(rst-stage-end)--
// --(rst-sink-begin)--
behavior int_sink(event_based_actor* self) { behavior int_sink(event_based_actor* self) {
return {[=](stream<int> in) { return {
// Create a stream manager for implementing a stream sink. Once more, we [=](stream<int32_t> in) {
// have to provide three functions: Initializer, Consumer, Finalizer. // Create a stream manager for implementing a stream sink. Once more, we
return attach_stream_sink( // have to provide three functions: Initializer, Consumer, Finalizer.
self, return attach_stream_sink(
// Our input source. self,
in, // Our input source.
// Initializer. Here, we store all values we receive. Note that streams in,
// are potentially unbound, so this is usually a bad idea outside small // Initializer. Here, we store all values we receive. Note that streams
// examples like this one. // are potentially unbound, so this is usually a bad idea outside small
[](std::vector<int>&) { // examples like this one.
// nop [](std::vector<int>&) {
}, // nop
// Consumer. Takes individual input elements as `val` and stores them },
// in our history. // Consumer. Takes individual input elements as `val` and stores them
[](std::vector<int>& xs, int val) { xs.emplace_back(val); }, // in our history.
// Finalizer. Allows us to run cleanup code once the stream terminates. [](std::vector<int32_t>& xs, int32_t val) { xs.emplace_back(val); },
[=](std::vector<int>& xs, const error& err) { // Finalizer. Allows us to run cleanup code once the stream terminates.
if (err) { [=](std::vector<int32_t>& xs, const error& err) {
aout(self) << "int_sink aborted with error: " << err << std::endl; if (err) {
} else { aout(self) << "int_sink aborted with error: " << err << std::endl;
aout(self) << "int_sink finalized after receiving: " << xs } else {
<< std::endl; aout(self) << "int_sink finalized after receiving: " << xs
} << std::endl;
}); }
}}; });
},
};
} }
// --(rst-sink-end)--
struct config : actor_system_config { struct config : actor_system_config {
config() { config() {
...@@ -114,17 +132,19 @@ struct config : actor_system_config { ...@@ -114,17 +132,19 @@ struct config : actor_system_config {
} }
bool with_stage = false; bool with_stage = false;
int n = 100; int32_t n = 100;
}; };
// --(rst-main-begin)--
void caf_main(actor_system& sys, const config& cfg) { void caf_main(actor_system& sys, const config& cfg) {
auto src = sys.spawn(int_source); auto src = sys.spawn(int_source);
auto snk = sys.spawn(int_sink); auto snk = sys.spawn(int_sink);
auto pipeline = cfg.with_stage ? snk * sys.spawn(int_selector) * src auto pipeline = cfg.with_stage ? snk * sys.spawn(int_selector) * src
: snk * src; : snk * src;
anon_send(pipeline, open_atom::value, cfg.n); anon_send(pipeline, open_atom_v, cfg.n);
} }
// --(rst-main-end)--
} // namespace } // namespace
CAF_MAIN() CAF_MAIN(id_block::integer_stream)
// Manual refs: lines 12-65 (Testing) // Manual refs: lines 12-60 (Testing)
#define CAF_SUITE ping_pong #define CAF_SUITE ping_pong
...@@ -11,24 +11,19 @@ using namespace caf; ...@@ -11,24 +11,19 @@ using namespace caf;
namespace { namespace {
using ping_atom = atom_constant<atom("ping")>;
using pong_atom = atom_constant<atom("pong")>;
behavior ping(event_based_actor* self, actor pong_actor, int n) { behavior ping(event_based_actor* self, actor pong_actor, int n) {
self->send(pong_actor, ping_atom::value, n); self->send(pong_actor, ping_atom_v, n);
return { return {
[=](pong_atom, int x) { [=](pong_atom, int x) {
if (x > 1) if (x > 1)
self->send(pong_actor, ping_atom::value, x - 1); self->send(pong_actor, ping_atom_v, x - 1);
} },
}; };
} }
behavior pong() { behavior pong() {
return { return {
[=](ping_atom, int x) { [=](ping_atom, int x) { return make_result(pong_atom_v, x); },
return std::make_tuple(pong_atom::value, x);
}
}; };
} }
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include "caf/stream.hpp" #include "caf/stream.hpp"
#include "caf/thread_hook.hpp" #include "caf/thread_hook.hpp"
#include "caf/type_erased_value.hpp" #include "caf/type_erased_value.hpp"
#include "caf/type_id.hpp"
#include "caf/detail/safe_equal.hpp" #include "caf/detail/safe_equal.hpp"
#include "caf/detail/type_traits.hpp" #include "caf/detail/type_traits.hpp"
...@@ -185,6 +186,14 @@ public: ...@@ -185,6 +186,14 @@ public:
return *this; return *this;
} }
/// Loads new types by calling add_message_type for each type in `IdBlock`.
template <class IdBlock>
actor_system_config& add_message_types() {
typename detail::il_range<IdBlock::begin, IdBlock::end>::type token;
add_message_types(token);
return *this;
}
/// Enables the actor system to convert errors of this error category /// Enables the actor system to convert errors of this error category
/// to human-readable strings via `renderer`. /// to human-readable strings via `renderer`.
actor_system_config& add_error_category(atom_value x, actor_system_config& add_error_category(atom_value x,
...@@ -211,13 +220,25 @@ public: ...@@ -211,13 +220,25 @@ public:
/// Loads module `T` with optional template parameters `Ts...`. /// Loads module `T` with optional template parameters `Ts...`.
template <class T, class... Ts> template <class T, class... Ts>
actor_system_config& load() { detail::enable_if_t<std::is_base_of<actor_system::module, T>::value,
actor_system_config&>
load() {
module_factories.push_back([](actor_system& sys) -> actor_system::module* { module_factories.push_back([](actor_system& sys) -> actor_system::module* {
return T::make(sys, detail::type_list<Ts...>{}); return T::make(sys, detail::type_list<Ts...>{});
}); });
return *this; return *this;
} }
/// @private
template <class IdBlock>
detail::enable_if_t<!std::is_base_of<actor_system::module, IdBlock>::value,
actor_system_config&>
load() {
// Allows exec_main to use one syntax for IdBlocks and modules.
add_message_types<IdBlock>();
return *this;
}
/// Adds a factory for a new hook type to the middleman (if loaded). /// Adds a factory for a new hook type to the middleman (if loaded).
template <class Factory> template <class Factory>
actor_system_config& add_hook_factory(Factory f) { actor_system_config& add_hook_factory(Factory f) {
...@@ -408,6 +429,16 @@ private: ...@@ -408,6 +429,16 @@ private:
&make_type_erased_value<T>); &make_type_erased_value<T>);
} }
void add_message_types(detail::int_list<>) {
// End of recursion.
}
template <long I, long... Is>
void add_message_types(detail::int_list<I, Is...>) {
add_message_type<typename type_by_id<I>::type>(type_name_by_id<I>::value);
add_message_types(detail::int_list<Is...>{});
}
actor_system_config& set_impl(string_view name, config_value value); actor_system_config& set_impl(string_view name, config_value value);
error extract_config_file_path(string_list& args); error extract_config_file_path(string_list& args);
......
...@@ -115,105 +115,6 @@ std::string to_string(const atom_constant<V>&) { ...@@ -115,105 +115,6 @@ std::string to_string(const atom_constant<V>&) {
template <atom_value V> template <atom_value V>
const atom_constant<V> atom_constant<V>::value = atom_constant<V>{}; const atom_constant<V> atom_constant<V>::value = atom_constant<V>{};
/// Used for request operations.
using add_atom = atom_constant<atom("add")>;
/// Used for request operations.
using get_atom = atom_constant<atom("get")>;
/// Used for request operations.
using put_atom = atom_constant<atom("put")>;
/// Used for signalizing resolved paths.
using resolve_atom = atom_constant<atom("resolve")>;
/// Used for signalizing updates, e.g., in a key-value store.
using update_atom = atom_constant<atom("update")>;
/// Used for request operations.
using delete_atom = atom_constant<atom("delete")>;
/// Used for response messages.
using ok_atom = atom_constant<atom("ok")>;
/// Used for triggering system-level message handling.
using sys_atom = atom_constant<atom("sys")>;
/// Used for signaling group subscriptions.
using join_atom = atom_constant<atom("join")>;
/// Used for signaling group unsubscriptions.
using leave_atom = atom_constant<atom("leave")>;
/// Used for signaling forwarding paths.
using forward_atom = atom_constant<atom("forward")>;
/// Used for buffer management.
using flush_atom = atom_constant<atom("flush")>;
/// Used for I/O redirection.
using redirect_atom = atom_constant<atom("redirect")>;
/// Used for link requests over network.
using link_atom = atom_constant<atom("link")>;
/// Used for removing networked links.
using unlink_atom = atom_constant<atom("unlink")>;
/// Used for monitor requests over network.
using monitor_atom = atom_constant<atom("monitor")>;
/// Used for removing networked monitors.
using demonitor_atom = atom_constant<atom("demonitor")>;
/// Used for publishing actors at a given port.
using publish_atom = atom_constant<atom("publish")>;
/// Used for publishing actors at a given port.
using publish_udp_atom = atom_constant<atom("pub_udp")>;
/// Used for removing an actor/port mapping.
using unpublish_atom = atom_constant<atom("unpublish")>;
/// Used for removing an actor/port mapping.
using unpublish_udp_atom = atom_constant<atom("unpub_udp")>;
/// Used for signalizing group membership.
using subscribe_atom = atom_constant<atom("subscribe")>;
/// Used for withdrawing group membership.
using unsubscribe_atom = atom_constant<atom("unsubscrib")>;
/// Used for establishing network connections.
using connect_atom = atom_constant<atom("connect")>;
/// Used for contacting a remote UDP endpoint
using contact_atom = atom_constant<atom("contact")>;
/// Used for opening ports or files.
using open_atom = atom_constant<atom("open")>;
/// Used for closing ports or files.
using close_atom = atom_constant<atom("close")>;
/// Used for spawning remote actors.
using spawn_atom = atom_constant<atom("spawn")>;
/// Used for migrating actors to other nodes.
using migrate_atom = atom_constant<atom("migrate")>;
/// Used for triggering periodic operations.
using tick_atom = atom_constant<atom("tick")>;
/// Used for pending out of order messages.
using pending_atom = atom_constant<atom("pending")>;
/// Used as timeout type for `timeout_msg`.
using receive_atom = atom_constant<atom("receive")>;
/// Used as timeout type for `timeout_msg`.
using stream_atom = atom_constant<atom("stream")>;
} // namespace caf } // namespace caf
namespace std { namespace std {
...@@ -227,3 +128,8 @@ struct hash<caf::atom_value> { ...@@ -227,3 +128,8 @@ struct hash<caf::atom_value> {
}; };
} // namespace std } // namespace std
// Ugly, but pull in type_id header since it declares atom constants that used
// to live in this header.
#include "caf/type_id.hpp"
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
#include "caf/config.hpp" #include "caf/config.hpp"
/// Evaluates to nothing /// Evaluates to nothing.
#define CAF_PP_EMPTY() #define CAF_PP_EMPTY()
/// Concatenates x and y into a single token. /// Concatenates x and y into a single token.
...@@ -29,6 +29,10 @@ ...@@ -29,6 +29,10 @@
/// Evaluate x and y before concatenating into a single token. /// Evaluate x and y before concatenating into a single token.
#define CAF_PP_PASTE(x, y) CAF_PP_CAT(x, y) #define CAF_PP_PASTE(x, y) CAF_PP_CAT(x, y)
/// Evaluates to __COUNTER__. Allows delaying evaluation of __COUNTER__ in some
/// edge cases where it otherwise could increment the internal counter twice.
#define CAF_PP_COUNTER() __COUNTER__
#ifdef CAF_MSVC #ifdef CAF_MSVC
/// Computes the number of arguments of a variadic pack. /// Computes the number of arguments of a variadic pack.
...@@ -65,3 +69,24 @@ ...@@ -65,3 +69,24 @@
#define CAF_PP_OVERLOAD(PREFIX, ...) \ #define CAF_PP_OVERLOAD(PREFIX, ...) \
CAF_PP_PASTE(PREFIX, CAF_PP_SIZE(__VA_ARGS__)) CAF_PP_PASTE(PREFIX, CAF_PP_SIZE(__VA_ARGS__))
#define CAF_PP_EXPAND(...) __VA_ARGS__
// clang-format off
#define CAF_PP_STR_1(x1) #x1
#define CAF_PP_STR_2(x1, x2) #x1 ", " #x2
#define CAF_PP_STR_3(x1, x2, x3) #x1 ", " #x2 ", " #x3
#define CAF_PP_STR_4(x1, x2, x3, x4) #x1 ", " #x2 ", " #x3 ", " #x4
#define CAF_PP_STR_5(x1, x2, x3, x4, x5) #x1 ", " #x2 ", " #x3 ", " #x4 ", " #x5
#define CAF_PP_STR_6(x1, x2, x3, x4, x5, x6) #x1 ", " #x2 ", " #x3 ", " #x4 ", " #x5 ", " #x6
#define CAF_PP_STR_7(x1, x2, x3, x4, x5, x6, x7) #x1 ", " #x2 ", " #x3 ", " #x4 ", " #x5 ", " #x6 ", " #x7
#define CAF_PP_STR_8(x1, x2, x3, x4, x5, x6, x7, x8) #x1 ", " #x2 ", " #x3 ", " #x4 ", " #x5 ", " #x6 ", " #x7 ", " #x8
#define CAF_PP_STR_9(x1, x2, x3, x4, x5, x6, x7, x8, x9) #x1 ", " #x2 ", " #x3 ", " #x4 ", " #x5 ", " #x6 ", " #x7 ", " #x8 ", " #x9
// clang-format on
#ifdef CAF_MSVC
# define CAF_PP_STR(...) \
CAF_PP_CAT(CAF_PP_OVERLOAD(CAF_PP_STR_, __VA_ARGS__)(__VA_ARGS__), \
CAF_PP_EMPTY())
#else
# define CAF_PP_STR(...) CAF_PP_OVERLOAD(CAF_PP_STR_, __VA_ARGS__)(__VA_ARGS__)
#endif
...@@ -53,10 +53,25 @@ public: ...@@ -53,10 +53,25 @@ public:
&& std::is_same<error, type>::value; && std::is_same<error, type>::value;
}; };
/// Convenience alias for `std::enable_if<has_make_error<T>::value, U>::type`. namespace detail {
// Enables `CAF_ERROR_CODE_ENUM` for forward compatibility with CAF 0.18.
template <class T>
struct error_factory {
static constexpr bool specialized = false;
};
} // namespace detail
/// Convenience alias to detect enums that provide `make_error` overloads.
template <class T, class U = void> template <class T, class U = void>
using enable_if_has_make_error_t = typename std::enable_if< using enable_if_has_make_error_t = typename std::enable_if<
has_make_error<T>::value, U>::type; !detail::error_factory<T>::specialized && has_make_error<T>::value, U>::type;
/// @private
template <class T, class U = void>
using enable_if_has_error_factory_t =
typename std::enable_if<detail::error_factory<T>::specialized, U>::type;
/// A serializable type for storing error codes with category and optional, /// A serializable type for storing error codes with category and optional,
/// human-readable context information. Unlike error handling classes from /// human-readable context information. Unlike error handling classes from
...@@ -115,13 +130,28 @@ public: ...@@ -115,13 +130,28 @@ public:
// nop // nop
} }
template <class E, class = enable_if_has_make_error_t<E>> template <class E>
error& operator=(E error_value) { enable_if_has_make_error_t<E, error&> operator=(E error_value) {
auto tmp = make_error(error_value); auto tmp = make_error(error_value);
std::swap(data_, tmp.data_); std::swap(data_, tmp.data_);
return *this; return *this;
} }
template <class E, class = enable_if_has_error_factory_t<E>, class... Ts>
error(E error_value, Ts&&... xs)
: error(static_cast<uint8_t>(error_value),
detail::error_factory<E>::category,
detail::error_factory<E>::context(std::forward<Ts>(xs)...)) {
// nop
}
template <class E>
enable_if_has_error_factory_t<E, error&> operator=(E error_value) {
auto tmp = error{error_value};
std::swap(data_, tmp.data_);
return *this;
}
~error(); ~error();
// -- observers -------------------------------------------------------------- // -- observers --------------------------------------------------------------
...@@ -249,3 +279,18 @@ bool operator!=(E x, const error& y) { ...@@ -249,3 +279,18 @@ bool operator!=(E x, const error& y) {
} }
} // namespace caf } // namespace caf
#define CAF_ERROR_CODE_ENUM(enum_name) \
namespace caf { \
namespace detail { \
template <> \
struct error_factory<enum_name> { \
static constexpr bool specialized = true; \
static constexpr atom_value category = atom(#enum_name); \
template <class... Ts> \
static message context(Ts&&... xs) { \
return make_message(std::forward<Ts>(xs)...); \
} \
}; \
} \
}
...@@ -176,9 +176,11 @@ config_option make_config_option(T& storage, string_view category, ...@@ -176,9 +176,11 @@ config_option make_config_option(T& storage, string_view category,
enum class atom_value : uint64_t; enum class atom_value : uint64_t;
enum class byte : uint8_t; enum class byte : uint8_t;
enum class exit_reason : uint8_t;
enum class invoke_message_result;
enum class pec : uint8_t;
enum class sec : uint8_t; enum class sec : uint8_t;
enum class stream_priority; enum class stream_priority;
enum class invoke_message_result;
// -- aliases ------------------------------------------------------------------ // -- aliases ------------------------------------------------------------------
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2020 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
namespace caf {
/// Customization point for enabling conversion from an enum type to an
/// @ref error or @ref error_code.
template <class T>
struct is_error_code_enum {
static constexpr bool value = false;
};
/// @relates is_error_code_enum
template <class T>
constexpr bool is_error_code_enum_v = is_error_code_enum<T>::value;
} // namespace caf
#define CAF_ERROR_CODE_ENUM(type_name) \
namespace caf { \
template <> \
struct is_error_code_enum<type_name> { \
static constexpr bool value = true; \
}; \
}
...@@ -53,8 +53,15 @@ public: ...@@ -53,8 +53,15 @@ public:
value = make_message(Ts{std::forward<Us>(xs)}...); value = make_message(Ts{std::forward<Us>(xs)}...);
} }
template <class E, class = enable_if_has_make_error_t<E>> template <class E>
result(E x) : flag(rt_error), err(make_error(x)) { result(E x, enable_if_has_make_error_t<E, void*> = nullptr)
: flag(rt_error), err(make_error(x)) {
// nop
}
template <class E>
result(E x, enable_if_has_error_factory_t<E, int> = 0)
: flag(rt_error), err(x) {
// nop // nop
} }
...@@ -185,5 +192,13 @@ struct is_result : std::false_type {}; ...@@ -185,5 +192,13 @@ struct is_result : std::false_type {};
template <class... Ts> template <class... Ts>
struct is_result<result<Ts...>> : std::true_type {}; struct is_result<result<Ts...>> : std::true_type {};
} // namespace caf // -- free functions -----------------------------------------------------------
/// Convenience function for wrapping the parameter pack `xs...` into a
/// `result`.
template <class... Ts>
result<detail::decay_t<Ts>...> make_result(Ts&&... xs) {
return {std::forward<Ts>(xs)...};
}
} // namespace caf
This diff is collapsed.
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