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 @@
#include <iostream>
#include "caf/all.hpp"
#include "caf/io/all.hpp"
using namespace caf;
using std::endl;
......
......@@ -30,8 +30,7 @@ using kickoff_atom = atom_constant<atom("kickoff")>;
// utility function to print an exit message with custom name
void print_on_exit(scheduled_actor* self, const std::string& name) {
self->attach_functor([=](const error& reason) {
aout(self) << name << " exited: " << self->home_system().render(reason)
<< endl;
aout(self) << name << " exited: " << to_string(reason) << endl;
});
}
......@@ -174,8 +173,8 @@ void run_server(actor_system& system, const config& cfg) {
auto server_actor = system.middleman().spawn_server(server, cfg.port,
pong_actor);
if (!server_actor)
cerr << "unable to spawn server: "
<< system.render(server_actor.error()) << endl;
cerr << "unable to spawn server: " << to_string(server_actor.error())
<< endl;
}
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,
cfg.port, ping_actor);
if (!io_actor) {
cout << "cannot connect to " << cfg.host << " at port " << cfg.port
<< ": " << system.render(io_actor.error()) << endl;
cout << "cannot connect to " << cfg.host << " at port " << cfg.port << ": "
<< to_string(io_actor.error()) << endl;
return;
}
send_as(*io_actor, ping_actor, kickoff_atom::value, *io_actor);
......
This diff is collapsed.
......@@ -13,8 +13,6 @@ using namespace caf::io;
namespace {
using tick_atom = atom_constant<atom("tick")>;
constexpr const char http_ok[] = R"__(HTTP/1.1 200 OK
Content-Type: text/plain
Connection: keep-alive
......@@ -51,7 +49,7 @@ behavior server(broker* self) {
self->set_down_handler([=](down_msg&) {
++*counter;
});
self->delayed_send(self, std::chrono::seconds(1), tick_atom::value);
self->delayed_send(self, std::chrono::seconds(1), tick_atom_v);
return {
[=](const new_connection_msg& ncm) {
auto worker = self->fork(connection_worker, ncm.handle);
......@@ -61,7 +59,7 @@ behavior server(broker* self) {
[=](tick_atom) {
aout(self) << "Finished " << *counter << " requests per second." << endl;
*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:
void caf_main(actor_system& system, const config& cfg) {
auto server_actor = system.middleman().spawn_server(server, cfg.port);
if (!server_actor) {
cerr << "*** cannot spawn server: "
<< system.render(server_actor.error()) << endl;
cerr << "*** cannot spawn server: " << to_string(server_actor.error())
<< endl;
return;
}
cout << "*** listening on port " << cfg.port << endl;
......
......@@ -38,19 +38,12 @@ relaxed-sleep-duration=10ms
; configures whether MMs try to span a full mesh
enable-automatic-connections=false
; application identifier of this node, prevents connection to other CAF
; instances with incompatible identifiers (at least one entry must match)
app-identifiers=["generic-caf-app"]
; instances with different identifier
app-identifier=""
; maximum number of consecutive I/O reads per broker
max-consecutive-reads=50
; heartbeat message interval for periodic traffic
; (0 disables heartbeating)
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
; heartbeat message interval in ms (0 disables heartbeating)
heartbeat-interval=0ms
; configures whether the MM attaches its internal utility actors to the
; scheduler instead of dedicating individual threads (needed only for
; deterministic testing)
......@@ -59,6 +52,10 @@ attach-utility-actors=false
; setting this to true allows fully deterministic execution in unit test and
; requires the user to trigger I/O manually
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,
; by default CAF uses 1-4 workers depending on the number of cores
workers=<min(3, number of cores / 4) + 1>
......
This diff is collapsed.
......@@ -3,34 +3,45 @@
// Manual refs: 24-27, 30-34, 75-78, 81-84 (ConfiguringActorApplications)
// 23-33 (TypeInspection)
#include <string>
#include <vector>
#include <cassert>
#include <utility>
#include <iostream>
#include <string>
#include <utility>
#include <vector>
#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::cout;
using std::endl;
using std::vector;
using namespace caf;
namespace {
// POD struct foo
// --(rst-foo-begin)--
struct foo {
std::vector<int> a;
int b;
};
// foo needs to be serializable
template <class Inspector>
typename Inspector::result_type inspect(Inspector& f, foo& x) {
return f(meta::type_name("foo"), x.a, x.b);
}
// --(rst-foo-end)--
// a pair of two ints
using foo_pair = std::pair<int, int>;
......@@ -58,7 +69,7 @@ void testee(event_based_actor* self, size_t remaining) {
else
self->quit();
};
self->become (
self->become(
// note: we sent a foo_pair2, but match on foo_pair
// that works because both are aliases for std::pair<int, int>
[=](const foo_pair& val) {
......@@ -68,20 +79,10 @@ void testee(event_based_actor* self, size_t remaining) {
[=](const foo& val) {
aout(self) << to_string(val) << endl;
set_next_behavior();
}
);
});
}
class config : public actor_system_config {
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&) {
void caf_main(actor_system& sys) {
// two variables for testing serialization
foo2 f1;
foo2 f2;
......@@ -90,34 +91,30 @@ void caf_main(actor_system& system, const config&) {
f1.b.resize(1);
f1.b.back().push_back(42);
// I/O buffer
vector<char> buf;
binary_serializer::container_type buf;
// write f1 to buffer
binary_serializer bs{system, buf};
binary_serializer bs{sys, buf};
auto e = bs(f1);
if (e) {
std::cerr << "*** unable to serialize foo2: "
<< system.render(e) << std::endl;
std::cerr << "*** unable to serialize foo2: " << to_string(e) << '\n';
return;
}
// read f2 back from buffer
binary_deserializer bd{system, buf};
binary_deserializer bd{sys, buf};
e = bd(f2);
if (e) {
std::cerr << "*** unable to serialize foo2: "
<< system.render(e) << std::endl;
std::cerr << "*** unable to serialize foo2: " << to_string(e) << '\n';
return;
}
// must be equal
assert(to_string(f1) == to_string(f2));
// spawn a testee that receives two messages of user-defined type
auto t = system.spawn(testee, 2u);
scoped_actor self{system};
auto t = sys.spawn(testee, 2u);
scoped_actor self{sys};
// send t a foo
self->send(t, foo{std::vector<int>{1, 2, 3, 4}, 5});
// send t a foo_pair2
self->send(t, foo_pair2{3, 4});
}
} // namespace
CAF_MAIN()
CAF_MAIN(id_block::custom_types_1)
......@@ -6,14 +6,20 @@
#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::endl;
using std::make_pair;
using namespace caf;
namespace {
// a simple class using getter and setter member functions
class foo {
public:
......@@ -58,17 +64,8 @@ behavior testee(event_based_actor* self) {
};
}
class config : public actor_system_config {
public:
config() {
add_message_type<foo>("foo");
}
};
void caf_main(actor_system& system, const config&) {
anon_send(system.spawn(testee), foo{1, 2});
void caf_main(actor_system& sys) {
anon_send(sys.spawn(testee), foo{1, 2});
}
} // namespace
CAF_MAIN()
CAF_MAIN(id_block::custom_types_2)
// Showcases custom message types that cannot provide
// friend access to the inspect() function.
// Manual refs: 20-49, 76-103 (TypeInspection)
#include <utility>
#include <iostream>
#include <utility>
#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::endl;
using std::make_pair;
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 no friend access for
// `inspect`.
// --(rst-foo-begin)--
class foo {
public:
foo(int a0 = 0, int b0 = 0) : a_(a0), b_(b0) {
......@@ -47,72 +52,31 @@ private:
int a_;
int b_;
};
// --(rst-foo-end)--
// A lightweight scope guard implementation.
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)};
}
// --(rst-inspect-begin)--
template <class Inspector>
typename std::enable_if<Inspector::reads_state,
typename Inspector::result_type>::type
inspect(Inspector& f, foo& x) {
return f(meta::type_name("foo"), x.a(), x.b());
}
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([&] {
typename Inspector::result_type inspect(Inspector& f, foo& x) {
auto a = x.a();
auto b = x.b();
auto load = meta::load_callback([&]() -> error {
// Write back to x when loading values from the inspector.
x.set_a(a);
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) {
return {
[=](const foo& x) {
aout(self) << to_string(x) << endl;
}
[=](const foo& x) { aout(self) << to_string(x) << endl; },
};
}
class config : public actor_system_config {
public:
config() {
add_message_type<foo>("foo");
}
};
void caf_main(actor_system& system, const config&) {
void caf_main(actor_system& system) {
anon_send(system.spawn(testee), foo{1, 2});
}
} // namespace
CAF_MAIN()
CAF_MAIN(id_block::custom_types_3)
......@@ -3,33 +3,36 @@
* exercise using only libcaf's event-based actor implementation. *
\******************************************************************************/
#include <chrono>
#include <iostream>
#include <map>
#include <sstream>
#include <thread>
#include <utility>
#include <vector>
#include <chrono>
#include <sstream>
#include <iostream>
#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::cout;
using std::endl;
using std::chrono::seconds;
using namespace caf;
using namespace custom;
namespace {
// atoms for chopstick interface
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")>;
// atoms for chopstick and philosopher interfaces
// a chopstick
using chopstick = typed_actor<replies_to<take_atom>::with<taken_atom, bool>,
......@@ -41,26 +44,24 @@ chopstick::behavior_type taken_chopstick(chopstick::pointer,
// either taken by a philosopher or available
chopstick::behavior_type available_chopstick(chopstick::pointer self) {
return {
[=](take_atom) -> std::tuple<taken_atom, bool> {
[=](take_atom) -> result<taken_atom, bool> {
self->become(taken_chopstick(self, self->current_sender()));
return std::make_tuple(taken_atom::value, true);
return {taken_atom_v, true};
},
[](put_atom) {
cerr << "chopstick received unexpected 'put'" << endl;
}
[](put_atom) { cerr << "chopstick received unexpected 'put'" << endl; },
};
}
chopstick::behavior_type taken_chopstick(chopstick::pointer self,
const strong_actor_ptr& user) {
return {
[](take_atom) -> std::tuple<taken_atom, bool> {
return std::make_tuple(taken_atom::value, false);
[](take_atom) -> result<taken_atom, bool> {
return {taken_atom_v, false};
},
[=](put_atom) {
if (self->current_sender() == user)
self->become(available_chopstick(self));
}
},
};
}
......@@ -94,71 +95,56 @@ chopstick::behavior_type taken_chopstick(chopstick::pointer self,
class philosopher : public event_based_actor {
public:
philosopher(actor_config& cfg,
std::string n,
chopstick l,
chopstick r)
: event_based_actor(cfg),
name_(std::move(n)),
left_(std::move(l)),
right_(std::move(r)) {
philosopher(actor_config& cfg, std::string n, chopstick l, chopstick r)
: event_based_actor(cfg),
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
set_default_handler(skip);
// a philosopher that receives {eat} stops thinking and becomes hungry
thinking_.assign(
[=](eat_atom) {
become(hungry_);
send(left_, take_atom::value);
send(right_, take_atom::value);
}
);
thinking_.assign([=](eat_atom) {
become(hungry_);
send(left_, take_atom_v);
send(right_, take_atom_v);
});
// wait for the first answer of a chopstick
hungry_.assign(
[=](taken_atom, bool result) {
if (result)
become(granted_);
else
become(denied_);
}
);
hungry_.assign([=](taken_atom, bool result) {
if (result)
become(granted_);
else
become(denied_);
});
// philosopher was able to obtain the first chopstick
granted_.assign(
[=](taken_atom, bool result) {
if (result) {
aout(this) << name_
<< " has picked up chopsticks with IDs "
<< left_->id() << " and " << right_->id()
<< " and starts to eat\n";
// eat some time
delayed_send(this, seconds(5), think_atom::value);
become(eating_);
} else {
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);
granted_.assign([=](taken_atom, bool result) {
if (result) {
aout(this) << name_ << " has picked up chopsticks with IDs "
<< left_->id() << " and " << right_->id()
<< " and starts to eat\n";
// eat some time
delayed_send(this, seconds(5), think_atom_v);
become(eating_);
} else {
send(current_sender() == left_ ? right_ : left_, put_atom_v);
send(this, eat_atom_v);
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)
eating_.assign(
[=](think_atom) {
send(left_, put_atom::value);
send(right_, put_atom::value);
delayed_send(this, seconds(5), eat_atom::value);
aout(this) << name_
<< " puts down his chopsticks and starts to think\n";
become(thinking_);
}
);
eating_.assign([=](think_atom) {
send(left_, put_atom_v);
send(right_, put_atom_v);
delayed_send(this, seconds(5), eat_atom_v);
aout(this) << name_ << " puts down his chopsticks and starts to think\n";
become(thinking_);
});
}
const char* name() const override {
......@@ -168,26 +154,26 @@ public:
protected:
behavior make_behavior() override {
// start thinking
send(this, think_atom::value);
send(this, think_atom_v);
// philosophers start to think after receiving {think}
return (
return {
[=](think_atom) {
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_);
}
);
},
};
}
private:
std::string name_; // the name of this philosopher
chopstick left_; // left chopstick
chopstick right_; // right chopstick
behavior thinking_; // initial behavior
behavior hungry_; // tries to take chopsticks
behavior granted_; // has one chopstick and waits for the second one
behavior denied_; // could not get first chopsticks
behavior eating_; // wait for some time, then go thinking again
std::string name_; // the name of this philosopher
chopstick left_; // left chopstick
chopstick right_; // right chopstick
behavior thinking_; // initial behavior
behavior hungry_; // tries to take chopsticks
behavior granted_; // has one chopstick and waits for the second one
behavior denied_; // could not get first chopsticks
behavior eating_; // wait for some time, then go thinking again
};
} // namespace
......@@ -203,10 +189,10 @@ void caf_main(actor_system& system) {
}
aout(self) << endl;
// spawn five philosophers
std::vector<std::string> names {"Plato", "Hume", "Kant",
"Nietzsche", "Descartes"};
std::vector<std::string> names{"Plato", "Hume", "Kant", "Nietzsche",
"Descartes"};
for (size_t i = 0; i < 5; ++i)
self->spawn<philosopher>(names[i], chopsticks[i], chopsticks[(i + 1) % 5]);
}
CAF_MAIN()
CAF_MAIN(id_block::dining_philosophers)
#include "caf/all.hpp"
using namespace caf;
using idle_atom = atom_constant<atom("idle")>;
using request_atom = atom_constant<atom("request")>;
using response_atom = atom_constant<atom("response")>;
using namespace caf;
behavior server(event_based_actor* self) {
self->set_default_handler(skip);
return {
[=](idle_atom, const actor& worker) {
self->become (
keep_behavior,
[=](request_atom atm) {
self->delegate(worker, atm);
self->unbecome();
},
[=](idle_atom) {
return skip();
}
);
self->become(keep_behavior, [=](ping_atom atm) {
self->delegate(worker, atm);
self->unbecome();
});
},
[=](request_atom) {
return skip();
}
};
}
behavior client(event_based_actor* self, const actor& serv) {
self->link_to(serv);
self->send(serv, idle_atom::value, self);
self->send(serv, idle_atom_v, self);
return {
[=](request_atom) {
self->send(serv, idle_atom::value, self);
return response_atom::value;
}
[=](ping_atom) {
self->send(serv, idle_atom_v, self);
return pong_atom_v;
},
};
}
......@@ -40,20 +29,18 @@ void caf_main(actor_system& system) {
auto serv = system.spawn(server);
auto worker = system.spawn(client, serv);
scoped_actor self{system};
self->request(serv, std::chrono::seconds(10), request_atom::value).receive(
[&](response_atom) {
aout(self) << "received response from "
<< (self->current_sender() == worker ? "worker\n"
: "server\n");
},
[&](error& err) {
aout(self) << "received error "
<< system.render(err)
<< " from "
<< (self->current_sender() == worker ? "worker\n"
: "server\n");
}
);
self->request(serv, std::chrono::seconds(10), ping_atom_v)
.receive(
[&](pong_atom) {
aout(self) << "received response from "
<< (self->current_sender() == worker ? "worker\n"
: "server\n");
},
[&](error& err) {
aout(self) << "received error " << to_string(err) << " from "
<< (self->current_sender() == worker ? "worker\n"
: "server\n");
});
self->send_exit(serv, exit_reason::user_shutdown);
}
......
......@@ -3,7 +3,7 @@
* 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>
......@@ -14,11 +14,9 @@ using namespace caf;
namespace {
using add_atom = atom_constant<atom("add")>;
using sub_atom = atom_constant<atom("sub")>;
using calculator_actor = typed_actor<replies_to<add_atom, int, int>::with<int>,
replies_to<sub_atom, int, int>::with<int>>;
using calculator_actor
= typed_actor<replies_to<add_atom, int32_t, int32_t>::with<int32_t>,
replies_to<sub_atom, int32_t, int32_t>::with<int32_t>>;
// prototypes and forward declarations
behavior calculator_fun(event_based_actor* self);
......@@ -31,43 +29,30 @@ class typed_calculator;
// function-based, dynamically typed, event-based API
behavior calculator_fun(event_based_actor*) {
return {
[](add_atom, int a, int b) {
return a + b;
},
[](sub_atom, int a, int b) {
return a - b;
}
[](add_atom, int32_t a, int32_t b) { return a + b; },
[](sub_atom, int32_t a, int32_t b) { return a - b; },
};
}
// function-based, dynamically typed, blocking API
void blocking_calculator_fun(blocking_actor* self) {
bool running = true;
self->receive_while(running) (
[](add_atom, int a, int b) {
return a + b;
},
[](sub_atom, int a, int b) {
return a - b;
},
self->receive_while(running)( //
[](add_atom, int32_t a, int32_t b) { return a + b; },
[](sub_atom, int32_t a, int32_t b) { return a - b; },
[&](exit_msg& em) {
if (em.reason) {
self->fail_state(std::move(em.reason));
running = false;
}
}
);
});
}
// function-based, statically typed, event-based API
calculator_actor::behavior_type typed_calculator_fun() {
return {
[](add_atom, int a, int b) {
return a + b;
},
[](sub_atom, int a, int b) {
return a - b;
}
[](add_atom, int32_t a, int32_t b) { return a + b; },
[](sub_atom, int32_t a, int32_t b) { return a - b; },
};
}
......@@ -113,25 +98,25 @@ void tester(scoped_actor&) {
// tests a calculator instance
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) {
aout(self) << "AUT (actor under test) failed: "
<< self->system().render(err) << endl;
aout(self) << "AUT (actor under test) failed: " << to_string(err) << endl;
};
// first test: x + y = z
self->request(hdl, infinite, add_atom::value, x, y).receive(
[&](int res1) {
aout(self) << x << " + " << y << " = " << res1 << endl;
// second test: x - y = z
self->request(hdl, infinite, sub_atom::value, x, y).receive(
[&](int res2) {
aout(self) << x << " - " << y << " = " << res2 << endl;
},
handle_err
);
},
handle_err
);
self->request(hdl, infinite, add_atom_v, x, y)
.receive(
[&](int32_t res1) {
aout(self) << x << " + " << y << " = " << res1 << endl;
// second test: x - y = z
self->request(hdl, infinite, sub_atom_v, x, y)
.receive(
[&](int32_t res2) {
aout(self) << x << " - " << y << " = " << res2 << endl;
},
handle_err);
},
handle_err);
tester(self, std::forward<Ts>(xs)...);
}
......
......@@ -7,6 +7,7 @@
// without updating the references in the *.tex files!
// Manual references: lines 18-44, and 49-50 (Actor.tex)
#include <cstdint>
#include <iostream>
#include "caf/all.hpp"
......@@ -15,33 +16,21 @@ using std::cout;
using std::endl;
using namespace caf;
using cell = typed_actor<reacts_to<put_atom, int>,
replies_to<get_atom>::with<int>>;
using cell = typed_actor<reacts_to<put_atom, int32_t>,
replies_to<get_atom>::with<int32_t>>;
struct cell_state {
int value = 0;
int32_t value = 0;
};
cell::behavior_type type_checked_cell(cell::stateful_pointer<cell_state> self) {
return {
[=](put_atom, int val) {
self->state.value = val;
},
[=](get_atom) {
return self->state.value;
}
};
return {[=](put_atom, int32_t val) { self->state.value = val; },
[=](get_atom) { return self->state.value; }};
}
behavior unchecked_cell(stateful_actor<cell_state>* self) {
return {
[=](put_atom, int val) {
self->state.value = val;
},
[=](get_atom) {
return self->state.value;
}
};
return {[=](put_atom, int32_t val) { self->state.value = val; },
[=](get_atom) { return self->state.value; }};
}
void caf_main(actor_system& system) {
......@@ -49,9 +38,9 @@ void caf_main(actor_system& system) {
auto cell1 = system.spawn(type_checked_cell);
auto cell2 = system.spawn(unchecked_cell);
auto f = make_function_view(cell1);
cout << "cell value: " << f(get_atom::value) << endl;
f(put_atom::value, 20);
cout << "cell value (after setting to 20): " << f(get_atom::value) << endl;
cout << "cell value: " << f(get_atom_v) << endl;
f(put_atom_v, 20);
cout << "cell value (after setting to 20): " << f(get_atom_v) << endl;
// get an unchecked cell and send it some garbage
anon_send(cell2, "hello there!");
}
......
/******************************************************************************\
* This example illustrates how to do time-triggered loops in libcaf. *
\ ******************************************************************************/
\******************************************************************************/
#include <algorithm>
#include <chrono>
#include <iostream>
#include <algorithm>
#include "caf/all.hpp"
// This file is partially included in the manual, do not modify
// 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::endl;
......@@ -17,23 +18,24 @@ using std::pair;
using namespace caf;
using step_atom = atom_constant<atom("step")>;
// ASCII art 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
constexpr animation_step animation_steps[] = {
{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},
{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}
{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},
{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},
};
constexpr size_t animation_width = 20;
......@@ -56,22 +58,20 @@ void draw_kirby(const animation_step& animation) {
// uses a message-based loop to iterate over all animation steps
void dancing_kirby(event_based_actor* self) {
// let's get it started
self->send(self, step_atom::value, size_t{0});
self->become (
[=](step_atom, size_t step) {
if (step == sizeof(animation_step)) {
// we've printed all animation steps (done)
cout << endl;
self->quit();
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);
self->send(self, update_atom_v, size_t{0});
self->become([=](update_atom, size_t step) {
if (step == sizeof(animation_step)) {
// we've printed all animation steps (done)
cout << endl;
self->quit();
return;
}
);
// 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) {
......
#include <iostream>
#include "caf/all.hpp"
// This file is partially included in the manual, do not modify
// 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 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) {
self->request(worker, std::chrono::seconds(10), add_atom::value, 1, 2).then(
[=](int result) {
aout(self) << "1 + 2 = " << result << endl;
}
);
self->request(worker, std::chrono::seconds(10), add_atom_v, 1, 2)
.then([=](int32_t result) { //
aout(self) << "1 + 2 = " << result << std::endl;
});
}
calc::behavior_type actor_b(calc::pointer self, const calc& worker) {
return {
[=](add_atom add, int x, int y) {
[=](add_atom add, int32_t x, int32_t y) {
return self->delegate(worker, add, x, y);
}
},
};
}
calc::behavior_type actor_c() {
return {
[](add_atom, int x, int y) {
return x + y;
}
[](add_atom, int32_t x, int32_t y) { return x + y; },
};
}
......
......@@ -2,8 +2,8 @@
* A very basic, interactive divider. *
\******************************************************************************/
// Manual refs: 19-25, 35-48, 68-77 (MessagePassing);
// 19-34, 50-58 (Error)
// Manual refs: 17-19, 49-59, 70-76 (MessagePassing);
// 17-47 (Error)
#include <iostream>
......@@ -14,16 +14,10 @@ using std::endl;
using std::flush;
using namespace caf;
namespace {
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) {
switch (x) {
case math_error::division_by_zero:
......@@ -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>>;
......@@ -43,21 +43,11 @@ divider::behavior_type divider_impl() {
if (y == 0.0)
return math_error::division_by_zero;
return x / y;
}
},
};
}
class config : public actor_system_config {
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&) {
void caf_main(actor_system& system) {
double x;
double y;
cout << "x: " << flush;
......@@ -66,17 +56,13 @@ void caf_main(actor_system& system, const config&) {
std::cin >> y;
auto div = system.spawn(divider_impl);
scoped_actor self{system};
self->request(div, std::chrono::seconds(10), div_atom::value, x, y).receive(
[&](double z) {
aout(self) << x << " / " << y << " = " << z << endl;
},
[&](const error& err) {
aout(self) << "*** cannot compute " << x << " / " << y << " => "
<< system.render(err) << endl;
}
);
self->request(div, std::chrono::seconds(10), div_atom_v, x, y)
.receive(
[&](double z) { aout(self) << x << " / " << y << " = " << z << endl; },
[&](const error& err) {
aout(self) << "*** cannot compute " << x << " / " << y << " => "
<< to_string(err) << endl;
});
}
} // namespace
CAF_MAIN()
CAF_MAIN(id_block::divider)
#include "caf/all.hpp"
#include <cassert>
#include <cstdint>
#include <iostream>
#include "caf/all.hpp"
using std::endl;
using namespace caf;
enum class ec : uint8_t {
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")>;
using push_atom = atom_constant<atom("push")>;
CAF_ADD_TYPE_ID(fixed_stack, (ec))
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) {
return error{static_cast<uint8_t>(x), atom("FixedStack")};
}
CAF_END_TYPE_ID_BLOCK(fixed_stack)
CAF_ERROR_CODE_ENUM(ec)
using std::endl;
using namespace caf;
using namespace custom;
class fixed_stack : public event_based_actor {
public:
fixed_stack(actor_config& cfg, size_t stack_size)
: event_based_actor(cfg),
size_(stack_size) {
full_.assign(
[=](push_atom, int) -> error {
return fixed_stack_errc::push_to_full;
},
: event_based_actor(cfg), size_(stack_size) {
full_.assign( //
[=](push_atom, int) -> error { return ec::push_to_full; },
[=](pop_atom) -> int {
auto result = data_.back();
data_.pop_back();
become(filled_);
return result;
}
);
filled_.assign(
});
filled_.assign( //
[=](push_atom, int what) {
data_.push_back(what);
if (data_.size() == size_)
......@@ -45,17 +48,13 @@ public:
if (data_.empty())
become(empty_);
return result;
}
);
empty_.assign(
});
empty_.assign( //
[=](push_atom, int what) {
data_.push_back(what);
become(filled_);
},
[=](pop_atom) -> error {
return fixed_stack_errc::pop_from_empty;
}
);
[=](pop_atom) -> error { return ec::pop_from_empty; });
}
behavior make_behavior() override {
......@@ -76,24 +75,17 @@ void caf_main(actor_system& system) {
auto st = self->spawn<fixed_stack>(5u);
// fill stack
for (int i = 0; i < 10; ++i)
self->send(st, push_atom::value, i);
self->send(st, push_atom_v, i);
// drain stack
aout(self) << "stack: { ";
bool stack_empty = false;
while (!stack_empty) {
self->request(st, std::chrono::seconds(10), pop_atom::value).receive(
[&](int x) {
aout(self) << x << " ";
},
[&](const error&) {
stack_empty = true;
}
);
self->request(st, std::chrono::seconds(10), pop_atom_v)
.receive([&](int x) { aout(self) << x << " "; },
[&](const error&) { stack_empty = true; });
}
aout(self) << "}" << endl;
self->send_exit(st, exit_reason::user_shutdown);
}
} // namespace
CAF_MAIN()
CAF_MAIN(id_block::fixed_stack)
......@@ -15,16 +15,13 @@ using std::endl;
using namespace caf;
// using add_atom = atom_constant<atom("add")>; (defined in atom.hpp)
using adder = typed_actor<replies_to<add_atom, int, int>::with<int>>;
using adder
= typed_actor<replies_to<add_atom, int32_t, int32_t>::with<int32_t>>;
// function-based, statically typed, event-based API
adder::behavior_type worker() {
return {
[](add_atom, int a, int b) {
return a + b;
}
[](add_atom, int32_t a, int32_t b) { return a + b; },
};
}
......@@ -32,19 +29,19 @@ adder::behavior_type worker() {
adder::behavior_type calculator_master(adder::pointer self) {
auto w = self->spawn(worker);
return {
[=](add_atom x, int y, int z) -> result<int> {
auto rp = self->make_response_promise<int>();
self->request(w, infinite, x, y, z).then([=](int result) mutable {
[=](add_atom x, int32_t y, int32_t z) -> result<int32_t> {
auto rp = self->make_response_promise<int32_t>();
self->request(w, infinite, x, y, z).then([=](int32_t result) mutable {
rp.deliver(result);
});
return rp;
}
},
};
}
void caf_main(actor_system& system) {
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()
......@@ -6,9 +6,10 @@
// without updating the references in the *.tex files!
// Manual references: lines 20-37, 39-51, 53-64, 67-69 (MessagePassing.tex)
#include <vector>
#include <chrono>
#include <cstdint>
#include <iostream>
#include <vector>
#include "caf/all.hpp"
......@@ -17,50 +18,46 @@ using std::vector;
using std::chrono::seconds;
using namespace caf;
using cell = typed_actor<reacts_to<put_atom, int>,
replies_to<get_atom>::with<int>>;
using cell = typed_actor<reacts_to<put_atom, int32_t>,
replies_to<get_atom>::with<int32_t>>;
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;
return {
[=](put_atom, int val) {
self->state.value = val;
},
[=](get_atom) {
return self->state.value;
}
[=](put_atom, int32_t val) { self->state.value = val; },
[=](get_atom) { return self->state.value; },
};
}
void waiting_testee(event_based_actor* self, vector<cell> 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;
});
}
void multiplexed_testee(event_based_actor* self, vector<cell> 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;
});
}
void blocking_testee(blocking_actor* self, vector<cell> cells) {
for (auto& x : cells)
self->request(x, seconds(1), get_atom::value).receive(
[&](int y) {
aout(self) << "cell #" << x.id() << " -> " << y << endl;
},
[&](error& err) {
aout(self) << "cell #" << x.id()
<< " -> " << self->system().render(err) << endl;
}
);
self->request(x, seconds(1), get_atom_v)
.receive(
[&](int32_t y) {
aout(self) << "cell #" << x.id() << " -> " << y << endl;
},
[&](error& err) {
aout(self) << "cell #" << x.id() << " -> " << to_string(err) << endl;
});
}
void caf_main(actor_system& system) {
......
/******************************************************************************\
* This example is a very basic, non-interactive math service implemented *
* using typed actors. *
\ ******************************************************************************/
\******************************************************************************/
#include "caf/all.hpp"
#include <cassert>
#include <iostream>
#include "caf/all.hpp"
using std::endl;
using namespace caf;
namespace {
using plus_atom = atom_constant<atom("plus")>;
using minus_atom = atom_constant<atom("minus")>;
using result_atom = atom_constant<atom("result")>;
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>>;
using calculator_type
= typed_actor<replies_to<add_atom, int32_t, int32_t>::with<int32_t>,
replies_to<sub_atom, int32_t, int32_t>::with<int32_t>>;
calculator_type::behavior_type typed_calculator_fun(calculator_type::pointer) {
return {
[](plus_atom, int x, int y) {
return std::make_tuple(result_atom::value, x + y);
},
[](minus_atom, int x, int y) {
return std::make_tuple(result_atom::value, x - y);
}
[](add_atom, int32_t x, int32_t y) { return x + y; },
[](sub_atom, int32_t x, int32_t y) { return x - y; },
};
}
......@@ -40,12 +32,8 @@ public:
protected:
behavior_type make_behavior() override {
return {
[](plus_atom, int x, int y) {
return std::make_tuple(result_atom::value, x + y);
},
[](minus_atom, int x, int y) {
return std::make_tuple(result_atom::value, x - y);
}
[](add_atom, int32_t x, int32_t y) { return x + y; },
[](sub_atom, int32_t x, int32_t y) { return x - y; },
};
}
};
......@@ -53,26 +41,23 @@ protected:
void tester(event_based_actor* self, const calculator_type& testee) {
self->link_to(testee);
// first test: 2 + 1 = 3
self->request(testee, infinite, plus_atom::value, 2, 1).then(
[=](result_atom, int r1) {
// second test: 2 - 1 = 1
self->request(testee, infinite, minus_atom::value, 2, 1).then(
[=](result_atom, int r2) {
self->request(testee, infinite, add_atom_v, 2, 1)
.then(
[=](int32_t r1) {
// second test: 2 - 1 = 1
self->request(testee, infinite, sub_atom_v, 2, 1).then([=](int32_t r2) {
// both tests succeeded
if (r1 == 3 && r2 == 1) {
aout(self) << "AUT (actor under test) seems to be ok"
<< endl;
aout(self) << "AUT (actor under test) seems to be ok" << endl;
}
self->send_exit(testee, exit_reason::user_shutdown);
}
);
},
[=](const error& err) {
aout(self) << "AUT (actor under test) failed: "
<< self->system().render(err) << endl;
self->quit(exit_reason::user_shutdown);
}
);
});
},
[=](const error& err) {
aout(self) << "AUT (actor under test) failed: " << to_string(err)
<< endl;
self->quit(exit_reason::user_shutdown);
});
}
void caf_main(actor_system& system) {
......
......@@ -79,7 +79,7 @@ void ChatWidget::sendChatMessage() {
auto x = system().groups().get(mod, g);
if (! x)
print("*** error: "
+ QString::fromUtf8(system().render(x.error()).c_str()));
+ QString::fromUtf8(to_string(x.error()).c_str()));
else
self()->send(self(), atom("join"), std::move(*x));
},
......@@ -128,7 +128,7 @@ void ChatWidget::joinGroup() {
string gid = gname.midRef(pos+1).toUtf8().constData();
auto x = system().groups().get(mod, gid);
if (! x)
QMessageBox::critical(this, "Error", system().render(x.error()).c_str());
QMessageBox::critical(this, "Error", to_string(x.error()).c_str());
else
self()->send(self(), join_atom::value, std::move(*x));
}
......
......@@ -59,9 +59,8 @@ int main(int argc, char** argv) {
auto group_uri = cfg.group_id.substr(p + 1);
auto g = system.groups().get(module, group_uri);
if (! g) {
cerr << "*** unable to get group " << group_uri
<< " from module " << module << ": "
<< system.render(g.error()) << endl;
cerr << "*** unable to get group " << group_uri << " from module "
<< module << ": " << to_string(g.error()) << endl;
return -1;
}
grp = std::move(*g);
......
......@@ -6,59 +6,63 @@
* - ./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 bob *
\ ******************************************************************************/
\******************************************************************************/
#include <set>
#include <map>
#include <vector>
#include <cstdlib>
#include <sstream>
#include <iostream>
#include <map>
#include <set>
#include <sstream>
#include <vector>
#include "caf/all.hpp"
#include "caf/io/all.hpp"
#include "caf/string_algorithms.hpp"
using namespace std;
using namespace caf;
CAF_BEGIN_TYPE_ID_BLOCK(group_chat, first_custom_type_id)
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) {
getline(is, l.str);
struct line {
std::string str;
};
std::istream& operator>>(std::istream& is, line& l) {
std::getline(is, l.str);
return is;
}
behavior client(event_based_actor* self, const string& name) {
behavior client(event_based_actor* self, const std::string& name) {
return {
[=](broadcast_atom, const string& message) {
for(auto& dest : self->joined_groups()) {
[=](broadcast_atom, const std::string& message) {
for (auto& dest : self->joined_groups()) {
self->send(dest, name + ": " + message);
}
},
[=](join_atom, const group& what) {
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->leave(g);
}
cout << "*** join " << to_string(what) << endl;
std::cout << "*** join " << to_string(what) << std::endl;
self->join(what);
self->send(what, name + " has entered the chatroom");
},
[=](const string& txt) {
[=](const std::string& txt) {
// don't print own messages
if (self->current_sender() != self)
cout << txt << endl;
std::cout << txt << std::endl;
},
[=](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:
config() {
opt_group{custom_options_, "global"}
.add(name, "name,n", "set name")
.add(group_uris, "group,g", "join group")
.add(server_mode, "server,s", "run in server mode")
.add(port, "port,p", "set port (ignored in client mode)");
.add(name, "name,n", "set name")
.add(group_uris, "group,g", "join group")
.add(server_mode, "server,s", "run in server mode")
.add(port, "port,p", "set port (ignored in client mode)");
}
};
void run_server(actor_system& system, const config& cfg) {
auto res = system.middleman().publish_local_groups(cfg.port);
if (! res) {
if (!res) {
std::cerr << "*** publishing local groups failed: "
<< system.render(res.error()) << endl;
<< to_string(res.error()) << std::endl;
return;
}
cout << "*** listening at port " << *res << endl
<< "*** press [enter] to quit" << endl;
string dummy;
std::cout << "*** listening at port " << *res << std::endl
<< "*** press [enter] to quit" << std::endl;
std::string dummy;
std::getline(std::cin, dummy);
cout << "... cya" << endl;
std::cout << "... cya" << std::endl;
}
void run_client(actor_system& system, const config& cfg) {
auto name = cfg.name;
while (name.empty()) {
cout << "please enter your name: " << flush;
if (!getline(cin, name)) {
cerr << "*** no name given... terminating" << endl;
std::cout << "please enter your name: " << std::flush;
if (!std::getline(std::cin, name)) {
std::cerr << "*** no name given... terminating" << std::endl;
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);
for (auto& uri : cfg.group_uris) {
auto tmp = system.groups().get(uri);
if (tmp)
anon_send(client_actor, join_atom::value, std::move(*tmp));
anon_send(client_actor, join_atom_v, std::move(*tmp));
else
cerr << R"(*** failed to parse ")" << uri << R"(" as group URI: )"
<< system.render(tmp.error()) << endl;
std::cerr << R"(*** failed to parse ")" << uri << R"(" as group URI: )"
<< to_string(tmp.error()) << std::endl;
}
istream_iterator<line> eof;
vector<string> words;
for (istream_iterator<line> i(cin); i != eof; ++i) {
std::istream_iterator<line> eof;
std::vector<std::string> words;
for (std::istream_iterator<line> i{std::cin}; i != eof; ++i) {
auto send_input = [&] {
if (!i->str.empty())
anon_send(client_actor, broadcast_atom::value, i->str);
anon_send(client_actor, broadcast_atom_v, i->str);
};
words.clear();
split(words, i->str, is_any_of(" "));
auto res = message_builder(words.begin(), words.end()).apply({
[&](const string& cmd, const string& mod, const string& id) {
message_handler f{
[&](const std::string& cmd, const std::string& mod,
const std::string& id) {
if (cmd == "/join") {
auto grp = system.groups().get(mod, id);
if (grp)
anon_send(client_actor, join_atom::value, *grp);
}
else {
anon_send(client_actor, join_atom_v, *grp);
} else {
send_input();
}
},
[&](const string& cmd) {
[&](const std::string& cmd) {
if (cmd == "/quit") {
cin.setstate(ios_base::eofbit);
}
else if (cmd[0] == '/') {
cout << "*** available commands:\n"
" /join <module> <group> join a new chat channel\n"
" /quit quit the program\n"
" /help print this text\n" << flush;
}
else {
std::cin.setstate(std::ios_base::eofbit);
} else if (cmd[0] == '/') {
std::cout << "*** available commands:\n"
" /join <module> <group> join a new chat channel\n"
" /quit quit the program\n"
" /help print this text\n";
} else {
send_input();
}
}
});
},
};
auto msg = message_builder(words.begin(), words.end()).move_to_message();
auto res = f(msg);
if (!res)
send_input();
}
......@@ -158,6 +162,4 @@ void caf_main(actor_system& system, const config& cfg) {
f(system, cfg);
}
} // namespace
CAF_MAIN(io::middleman)
CAF_MAIN(id_block::group_chat, io::middleman)
......@@ -7,45 +7,46 @@
// Run client at the same host:
// - remote_spawn -H localhost -p 4242
// Manual refs: 33-39, 99-101,106,110 (ConfiguringActorApplications)
// 125-143 (RemoteSpawn)
#include <array>
#include <vector>
#include <string>
#include <sstream>
#include <cassert>
#include <iostream>
#include <functional>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include "caf/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::cout;
using std::endl;
using std::string;
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) {
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;
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;
return a - b;
}
},
};
}
......@@ -62,10 +63,11 @@ string trim(string s) {
// implements our main loop for reading user input
void client_repl(function_view<calculator> f) {
auto usage = [] {
cout << "Usage:" << endl
<< " quit : terminate program" << endl
<< " <x> + <y> : adds two integers" << endl
<< " <x> - <y> : subtracts two integers" << endl << endl;
cout << "Usage:" << endl
<< " quit : terminate program" << endl
<< " <x> + <y> : adds two int32_tegers" << endl
<< " <x> - <y> : subtracts two int32_tegers" << endl
<< endl;
};
usage();
// read next line, split it, and evaluate user input
......@@ -79,20 +81,21 @@ void client_repl(function_view<calculator> f) {
usage();
continue;
}
auto to_int = [](const string& str) -> optional<int> {
auto to_int32_t = [](const string& str) -> optional<int32_t> {
char* end = nullptr;
auto res = strtol(str.c_str(), &end, 10);
if (end == str.c_str() + str.size())
return static_cast<int>(res);
return static_cast<int32_t>(res);
return none;
};
auto x = to_int(words[0]);
auto y = to_int(words[2]);
auto x = to_int32_t(words[0]);
auto y = to_int32_t(words[2]);
if (!x || !y || (words[1] != "+" && words[1] != "-"))
usage();
else
cout << " = " << (words[1] == "+" ? f(add_atom::value, *x, *y)
: f(sub_atom::value, *x, *y)) << "\n";
cout << " = "
<< (words[1] == "+" ? f(add_atom_v, *x, *y) : f(sub_atom_v, *x, *y))
<< "\n";
}
}
......@@ -100,9 +103,9 @@ struct config : actor_system_config {
config() {
add_actor_type("calculator", calculator_fun);
opt_group{custom_options_, "global"}
.add(port, "port,p", "set port")
.add(host, "host,H", "set node (ignored in server mode)")
.add(server_mode, "server-mode,s", "enable server mode");
.add(port, "port,p", "set port")
.add(host, "host,H", "set node (ignored in server mode)")
.add(server_mode, "server-mode,s", "enable server mode");
}
uint16_t port = 0;
string host = "localhost";
......@@ -112,31 +115,28 @@ struct config : actor_system_config {
void server(actor_system& system, const config& cfg) {
auto res = system.middleman().open(cfg.port);
if (!res) {
cerr << "*** cannot open port: "
<< system.render(res.error()) << endl;
cerr << "*** cannot open port: " << to_string(res.error()) << endl;
return;
}
cout << "*** running on port: "
<< *res << endl
cout << "*** running on port: " << *res << endl
<< "*** press <enter> to shutdown server" << endl;
getchar();
}
// --(rst-client-begin)--
void client(actor_system& system, const config& cfg) {
auto node = system.middleman().connect(cfg.host, cfg.port);
if (!node) {
cerr << "*** connect failed: "
<< system.render(node.error()) << endl;
cerr << "*** connect failed: " << to_string(node.error()) << endl;
return;
}
auto type = "calculator"; // type of the actor we wish to spawn
auto args = make_message(); // arguments to construct the actor
auto type = "calculator"; // type of the actor we wish to spawn
auto args = make_message(); // arguments to construct the actor
auto tout = std::chrono::seconds(30); // wait no longer than 30s
auto worker = system.middleman().remote_spawn<calculator>(*node, type,
args, tout);
auto worker = system.middleman().remote_spawn<calculator>(*node, type, args,
tout);
if (!worker) {
cerr << "*** remote spawn failed: "
<< system.render(worker.error()) << endl;
cerr << "*** remote spawn failed: " << to_string(worker.error()) << endl;
return;
}
// start using worker in main loop
......@@ -144,12 +144,11 @@ void client(actor_system& system, const config& cfg) {
// be a good citizen and terminate remotely spawned actor before exiting
anon_send_exit(*worker, exit_reason::kill);
}
// --(rst-client-end)--
void caf_main(actor_system& system, const config& cfg) {
auto f = cfg.server_mode ? server : client;
f(system, cfg);
}
} // namespace
CAF_MAIN(io::middleman)
CAF_MAIN(id_block::remote_spawn, io::middleman)
......@@ -2,109 +2,127 @@
* Basic, non-interactive streaming example for processing integers. *
******************************************************************************/
// Manual refs: lines 17-46, 48-78, 80-107, 121-125 (Streaming)
#include <iostream>
#include <vector>
#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 namespace caf;
namespace {
// --(rst-source-begin)--
// Simple source for generating a stream of integers from [0, n).
behavior int_source(event_based_actor* self) {
return {[=](open_atom, int n) {
// Produce at least one value.
if (n <= 0)
n = 1;
// Create a stream manager for implementing a stream source. The
// streaming logic requires three functions: initializer, generator, and
// predicate.
return attach_stream_source(
self,
// Initializer. The type of the first argument (state) is freely
// chosen. If no state is required, `caf::unit_t` can be used here.
[](int& x) { x = 0; },
// Generator. This function is called by CAF to produce new stream
// elements for downstream actors. The `x` argument is our state again
// (with our freely chosen type). The second argument `out` points to
// the output buffer. The template argument (here: int) determines what
// elements downstream actors receive in this stream. Finally, `num` is
// a hint from CAF how many elements we should ideally insert into
// `out`. We can always insert fewer or more items.
[n](int& x, downstream<int>& out, size_t num) {
auto max_x = std::min(x + static_cast<int>(num), n);
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; });
}};
return {
[=](open_atom, int32_t n) {
// Produce at least one value.
if (n <= 0)
n = 1;
// Create a stream manager for implementing a stream source. The
// streaming logic requires three functions: initializer, generator, and
// predicate.
return attach_stream_source(
self,
// Initializer. The type of the first argument (state) is freely
// chosen. If no state is required, `caf::unit_t` can be used here.
[](int32_t& x) { x = 0; },
// Generator. This function is called by CAF to produce new stream
// elements for downstream actors. The `x` argument is our state again
// (with our freely chosen type). The second argument `out` points to
// the output buffer. The template argument (here: int) determines what
// elements downstream actors receive in this stream. Finally, `num` is
// a hint from CAF how many elements we should ideally insert into
// `out`. We can always insert fewer or more items.
[n](int32_t& x, downstream<int32_t>& out, size_t num) {
auto max_x = std::min(x + static_cast<int>(num), n);
for (; x < max_x; ++x)
out.push(x);
},
// 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.
behavior int_selector(event_based_actor* self) {
return {[=](stream<int> in) {
// Create a stream manager for implementing a stream stage. Similar to
// `make_source`, we need three functions: initialzer, processor, and
// finalizer.
return attach_stream_stage(
self,
// Our input source.
in,
// Initializer. Here, we don't need any state and simply use unit_t.
[](unit_t&) {
// nop
},
// Processor. This function takes individual input elements as `val`
// and forwards even integers to `out`.
[](unit_t&, downstream<int>& out, int val) {
if (val % 2 == 0)
out.push(val);
},
// Finalizer. Allows us to run cleanup code once the stream terminates.
[=](unit_t&, const error& err) {
if (err) {
aout(self) << "int_selector aborted with error: " << err << std::endl;
} else {
aout(self) << "int_selector finalized" << std::endl;
}
// else: regular stream shutdown
});
}};
return {
[=](stream<int32_t> in) {
// Create a stream manager for implementing a stream stage. Similar to
// `make_source`, we need three functions: initialzer, processor, and
// finalizer.
return attach_stream_stage(
self,
// Our input source.
in,
// Initializer. Here, we don't need any state and simply use unit_t.
[](unit_t&) {
// nop
},
// Processor. This function takes individual input elements as `val`
// and forwards even integers to `out`.
[](unit_t&, downstream<int32_t>& out, int32_t val) {
if (val % 2 == 0)
out.push(val);
},
// Finalizer. Allows us to run cleanup code once the stream terminates.
[=](unit_t&, const error& err) {
if (err) {
aout(self) << "int_selector aborted with error: " << err
<< std::endl;
} else {
aout(self) << "int_selector finalized" << std::endl;
}
// else: regular stream shutdown
});
},
};
}
// --(rst-stage-end)--
// --(rst-sink-begin)--
behavior int_sink(event_based_actor* self) {
return {[=](stream<int> in) {
// Create a stream manager for implementing a stream sink. Once more, we
// have to provide three functions: Initializer, Consumer, Finalizer.
return attach_stream_sink(
self,
// Our input source.
in,
// Initializer. Here, we store all values we receive. Note that streams
// are potentially unbound, so this is usually a bad idea outside small
// examples like this one.
[](std::vector<int>&) {
// nop
},
// Consumer. Takes individual input elements as `val` and stores them
// in our history.
[](std::vector<int>& xs, int val) { xs.emplace_back(val); },
// Finalizer. Allows us to run cleanup code once the stream terminates.
[=](std::vector<int>& xs, const error& err) {
if (err) {
aout(self) << "int_sink aborted with error: " << err << std::endl;
} else {
aout(self) << "int_sink finalized after receiving: " << xs
<< std::endl;
}
});
}};
return {
[=](stream<int32_t> in) {
// Create a stream manager for implementing a stream sink. Once more, we
// have to provide three functions: Initializer, Consumer, Finalizer.
return attach_stream_sink(
self,
// Our input source.
in,
// Initializer. Here, we store all values we receive. Note that streams
// are potentially unbound, so this is usually a bad idea outside small
// examples like this one.
[](std::vector<int>&) {
// nop
},
// Consumer. Takes individual input elements as `val` and stores them
// in our history.
[](std::vector<int32_t>& xs, int32_t val) { xs.emplace_back(val); },
// Finalizer. Allows us to run cleanup code once the stream terminates.
[=](std::vector<int32_t>& xs, const error& err) {
if (err) {
aout(self) << "int_sink aborted with error: " << err << std::endl;
} else {
aout(self) << "int_sink finalized after receiving: " << xs
<< std::endl;
}
});
},
};
}
// --(rst-sink-end)--
struct config : actor_system_config {
config() {
......@@ -114,17 +132,19 @@ struct config : actor_system_config {
}
bool with_stage = false;
int n = 100;
int32_t n = 100;
};
// --(rst-main-begin)--
void caf_main(actor_system& sys, const config& cfg) {
auto src = sys.spawn(int_source);
auto snk = sys.spawn(int_sink);
auto pipeline = cfg.with_stage ? snk * sys.spawn(int_selector) * src
: snk * src;
anon_send(pipeline, open_atom::value, cfg.n);
anon_send(pipeline, open_atom_v, cfg.n);
}
// --(rst-main-end)--
} // 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
......@@ -11,24 +11,19 @@ using namespace caf;
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) {
self->send(pong_actor, ping_atom::value, n);
self->send(pong_actor, ping_atom_v, n);
return {
[=](pong_atom, int x) {
if (x > 1)
self->send(pong_actor, ping_atom::value, x - 1);
}
self->send(pong_actor, ping_atom_v, x - 1);
},
};
}
behavior pong() {
return {
[=](ping_atom, int x) {
return std::make_tuple(pong_atom::value, x);
}
[=](ping_atom, int x) { return make_result(pong_atom_v, x); },
};
}
......
......@@ -40,6 +40,7 @@
#include "caf/stream.hpp"
#include "caf/thread_hook.hpp"
#include "caf/type_erased_value.hpp"
#include "caf/type_id.hpp"
#include "caf/detail/safe_equal.hpp"
#include "caf/detail/type_traits.hpp"
......@@ -185,6 +186,14 @@ public:
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
/// to human-readable strings via `renderer`.
actor_system_config& add_error_category(atom_value x,
......@@ -211,13 +220,25 @@ public:
/// Loads module `T` with optional template parameters `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* {
return T::make(sys, detail::type_list<Ts...>{});
});
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).
template <class Factory>
actor_system_config& add_hook_factory(Factory f) {
......@@ -408,6 +429,16 @@ private:
&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);
error extract_config_file_path(string_list& args);
......
......@@ -115,105 +115,6 @@ std::string to_string(const atom_constant<V>&) {
template <atom_value 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 std {
......@@ -227,3 +128,8 @@ struct hash<caf::atom_value> {
};
} // 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 @@
#include "caf/config.hpp"
/// Evaluates to nothing
/// Evaluates to nothing.
#define CAF_PP_EMPTY()
/// Concatenates x and y into a single token.
......@@ -29,6 +29,10 @@
/// Evaluate x and y before concatenating into a single token.
#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
/// Computes the number of arguments of a variadic pack.
......@@ -65,3 +69,24 @@
#define CAF_PP_OVERLOAD(PREFIX, ...) \
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:
&& 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>
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,
/// human-readable context information. Unlike error handling classes from
......@@ -115,13 +130,28 @@ public:
// nop
}
template <class E, class = enable_if_has_make_error_t<E>>
error& operator=(E error_value) {
template <class E>
enable_if_has_make_error_t<E, error&> operator=(E error_value) {
auto tmp = make_error(error_value);
std::swap(data_, tmp.data_);
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();
// -- observers --------------------------------------------------------------
......@@ -249,3 +279,18 @@ bool operator!=(E x, const error& y) {
}
} // 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,
enum class atom_value : uint64_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 stream_priority;
enum class invoke_message_result;
// -- 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:
value = make_message(Ts{std::forward<Us>(xs)}...);
}
template <class E, class = enable_if_has_make_error_t<E>>
result(E x) : flag(rt_error), err(make_error(x)) {
template <class E>
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
}
......@@ -185,5 +192,13 @@ struct is_result : std::false_type {};
template <class... Ts>
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