Commit a3424580 authored by Dominik Charousset's avatar Dominik Charousset

Port examples to new API

parent c2a66286
...@@ -22,11 +22,9 @@ macro(add name folder) ...@@ -22,11 +22,9 @@ macro(add name folder)
add_dependencies(${name} all_examples) add_dependencies(${name} all_examples)
endmacro() endmacro()
add(announce_1 type_system) add(custom_types_1 custom_types)
add(announce_2 type_system) add(custom_types_2 custom_types)
add(announce_3 type_system) add(custom_types_3 custom_types)
add(announce_4 type_system)
add(announce_5 type_system)
add(dancing_kirby message_passing) add(dancing_kirby message_passing)
add(dining_philosophers message_passing) add(dining_philosophers message_passing)
add(hello_world .) add(hello_world .)
......
...@@ -13,8 +13,9 @@ using namespace caf; ...@@ -13,8 +13,9 @@ using namespace caf;
using std::endl; using std::endl;
int main() { int main() {
actor_system system;
for (int i = 1; i <= 50; ++i) { for (int i = 1; i <= 50; ++i) {
spawn<blocking_api>([i](blocking_actor* self) { system.spawn<blocking_api>([i](blocking_actor* self) {
aout(self) << "Hi there! This is actor nr. " aout(self) << "Hi there! This is actor nr. "
<< i << "!" << endl; << i << "!" << endl;
std::random_device rd; std::random_device rd;
...@@ -30,7 +31,5 @@ int main() { ...@@ -30,7 +31,5 @@ int main() {
}); });
} }
// wait until all other actors we've spawned are done // wait until all other actors we've spawned are done
await_all_actors_done(); system.await_all_actors_done();
// done
shutdown();
} }
...@@ -144,20 +144,29 @@ maybe<uint16_t> as_u16(const std::string& str) { ...@@ -144,20 +144,29 @@ maybe<uint16_t> as_u16(const std::string& str) {
} }
int main(int argc, char** argv) { int main(int argc, char** argv) {
actor_system_config cfg;
cfg.load<io::middleman>();
actor_system system{cfg};
message_builder{argv + 1, argv + argc}.apply({ message_builder{argv + 1, argv + argc}.apply({
on("-s", as_u16) >> [&](uint16_t port) { on("-s", as_u16) >> [&](uint16_t port) {
cout << "run in server mode" << endl; cout << "run in server mode" << endl;
auto pong_actor = spawn(pong); auto pong_actor = system.spawn(pong);
auto sever_actor = spawn_io_server(server, port, pong_actor); auto server_actor = system.middleman().spawn_server(server, port,
print_on_exit(sever_actor, "server"); pong_actor);
print_on_exit(pong_actor, "pong"); if (server_actor) {
print_on_exit(*server_actor, "server");
print_on_exit(pong_actor, "pong");
}
}, },
on("-c", val<string>, as_u16) >> [&](const string& host, uint16_t port) { on("-c", val<string>, as_u16) >> [&](const string& host, uint16_t port) {
auto ping_actor = spawn(ping, 20); auto ping_actor = system.spawn(ping, 20);
auto io_actor = spawn_io_client(protobuf_io, host, port, ping_actor); auto io_actor = system.middleman().spawn_client(protobuf_io, host,
print_on_exit(io_actor, "protobuf_io"); port, ping_actor);
print_on_exit(ping_actor, "ping"); if (io_actor) {
send_as(io_actor, ping_actor, atom("kickoff"), io_actor); print_on_exit(ping_actor, "ping");
print_on_exit(*io_actor, "protobuf_io");
send_as(*io_actor, ping_actor, atom("kickoff"), *io_actor);
}
}, },
others >> [] { others >> [] {
cerr << "use with eihter '-s PORT' as server or " cerr << "use with eihter '-s PORT' as server or "
...@@ -165,6 +174,5 @@ int main(int argc, char** argv) { ...@@ -165,6 +174,5 @@ int main(int argc, char** argv) {
<< endl; << endl;
} }
}); });
await_all_actors_done(); system.await_all_actors_done();
shutdown();
} }
...@@ -193,40 +193,39 @@ int main(int argc, char** argv) { ...@@ -193,40 +193,39 @@ int main(int argc, char** argv) {
{"port,p", "set port", port}, {"port,p", "set port", port},
{"host,H", "set host (client mode only"} {"host,H", "set host (client mode only"}
}); });
if (! res.error.empty()) { if (! res.error.empty())
cerr << res.error << endl; return cerr << res.error << endl, 1;
return 1; if (res.opts.count("help") > 0)
} return cout << res.helptext << endl, 0;
if (res.opts.count("help") > 0) { if (! res.remainder.empty())
cout << res.helptext << endl;
return 0;
}
if (! res.remainder.empty()) {
// not all CLI arguments could be consumed // not all CLI arguments could be consumed
cerr << "*** too many arguments" << endl << res.helptext << endl; return cerr << "*** too many arguments" << endl << res.helptext << endl, 1;
return 1; if (res.opts.count("port") == 0)
} return cerr << "*** no port given" << endl << res.helptext << endl, 1;
if (res.opts.count("port") == 0) { actor_system_config cfg;
cerr << "*** no port given" << endl << res.helptext << endl; cfg.load<io::middleman>();
return 1; actor_system system{cfg};
}
if (res.opts.count("server") > 0) { if (res.opts.count("server") > 0) {
cout << "run in server mode" << endl; cout << "run in server mode" << endl;
auto pong_actor = spawn(pong); auto pong_actor = system.spawn(pong);
auto server_actor = spawn_io_server(server, port, pong_actor); auto server_actor = system.middleman().spawn_server(server, port, pong_actor);
print_on_exit(server_actor, "server"); if (server_actor) {
print_on_exit(pong_actor, "pong"); print_on_exit(*server_actor, "server");
print_on_exit(pong_actor, "pong");
}
} else if (res.opts.count("client") > 0) { } else if (res.opts.count("client") > 0) {
auto ping_actor = spawn(ping, size_t{20}); auto ping_actor = system.spawn(ping, size_t{20});
auto io_actor = spawn_io_client(broker_impl, host, port, ping_actor); auto io_actor = system.middleman().spawn_client(broker_impl, host,
print_on_exit(io_actor, "protobuf_io"); port, ping_actor);
print_on_exit(ping_actor, "ping"); if (io_actor) {
send_as(io_actor, ping_actor, kickoff_atom::value, io_actor); print_on_exit(ping_actor, "ping");
print_on_exit(*io_actor, "protobuf_io");
send_as(*io_actor, ping_actor, kickoff_atom::value, *io_actor);
}
} else { } else {
cerr << "*** neither client nor server mode set" << endl cerr << "*** neither client nor server mode set" << endl
<< res.helptext << endl; << res.helptext << endl;
return 1; return 1;
} }
await_all_actors_done(); system.await_all_actors_done();
shutdown();
} }
...@@ -95,12 +95,15 @@ int main(int argc, const char** argv) { ...@@ -95,12 +95,15 @@ int main(int argc, const char** argv) {
} }
cout << "*** run in server mode listen on: " << port << endl; cout << "*** run in server mode listen on: " << port << endl;
cout << "*** to quit the program, simply press <enter>" << endl; cout << "*** to quit the program, simply press <enter>" << endl;
auto server_actor = spawn_io_server(server, port); actor_system system;
auto server_actor = system.middleman().spawn_server(server, port);
if (! server_actor)
return cerr << "*** spawn_server failed: "
<< server_actor.error().message() << endl, 1;
// wait for any input // wait for any input
std::string dummy; std::string dummy;
std::getline(std::cin, dummy); std::getline(std::cin, dummy);
// kill server // kill server
anon_send_exit(server_actor, exit_reason::user_shutdown); anon_send_exit(*server_actor, exit_reason::user_shutdown);
await_all_actors_done(); system.await_all_actors_done();
shutdown();
} }
; This file shows all possible parameters with defaults.
; Values enclosed in <> are detected at runtime per default.
; when using the default scheduler
[scheduler]
; accepted alternative: "work-sharing"
policy="work-stealing"
; configures whether the scheduler generates profiling output
enable-profiling=false
; can be overriden to force a fixed number of threads
max-threads=<detected at runtime>
; configures the maximum number of messages actors can consume in one run
max-throughput=<numeric_limits<size_t>::max()>
; measurement resolution in milliseconds (only if profiling is enabled)
profiling-ms-resolution=100
; output file for profiler data (only if profiling is enabled)
profiling-output-file="/dev/null"
; when loading io::middleman
[middleman]
; configures whether MMs try to span a full mesh
enable-automatic-connections=false
...@@ -346,28 +346,25 @@ int main() { ...@@ -346,28 +346,25 @@ int main() {
set_sighandler(); set_sighandler();
// initialize CURL // initialize CURL
curl_global_init(CURL_GLOBAL_DEFAULT); curl_global_init(CURL_GLOBAL_DEFAULT);
{ // lifetime scope of self actor_system system;
scoped_actor self; scoped_actor self{system};
// spawn client and curl_master // spawn client and curl_master
auto master = self->spawn<detached>(curl_master); auto master = self->spawn<detached>(curl_master);
self->spawn<detached>(client, master); self->spawn<detached>(client, master);
// poll CTRL+C flag every second // poll CTRL+C flag every second
while (! shutdown_flag) while (! shutdown_flag)
std::this_thread::sleep_for(std::chrono::seconds(1)); std::this_thread::sleep_for(std::chrono::seconds(1));
aout(self) << color::cyan << "received CTRL+C" << color::reset_endl; aout(self) << color::cyan << "received CTRL+C" << color::reset_endl;
// shutdown actors // shutdown actors
anon_send_exit(master, exit_reason::user_shutdown); anon_send_exit(master, exit_reason::user_shutdown);
// await actors // await actors
act.sa_handler = [](int) { abort(); }; act.sa_handler = [](int) { abort(); };
set_sighandler(); set_sighandler();
aout(self) << color::cyan aout(self) << color::cyan
<< "await CURL; this may take a while " << "await CURL; this may take a while "
"(press CTRL+C again to abort)" "(press CTRL+C again to abort)"
<< color::reset_endl; << color::reset_endl;
self->await_all_other_actors_done(); self->await_all_other_actors_done();
}
// shutdown libcaf
shutdown();
// shutdown CURL // shutdown CURL
curl_global_cleanup(); curl_global_cleanup();
} }
// showcases how to add custom POD message types to CAF
#include <tuple>
#include <string>
#include <vector> #include <vector>
#include <cassert> #include <cassert>
#include <utility> #include <utility>
...@@ -20,102 +24,106 @@ struct foo { ...@@ -20,102 +24,106 @@ struct foo {
int b; int b;
}; };
// announce requires foo to have the equal operator implemented // foo needs to be comparable ...
bool operator==(const foo& lhs, const foo& rhs) { bool operator==(const foo& lhs, const foo& rhs) {
return lhs.a == rhs.a && lhs.b == rhs.b; return lhs.a == rhs.a && lhs.b == rhs.b;
} }
// ... and to be serializable
template <class T>
void serialize(T& in_or_out, foo& x, const unsigned int) {
in_or_out & x.a;
in_or_out & x.b;
}
// also, CAF gives us `deep_to_string` for implementing `to_string` easily
std::string to_string(const foo& x) {
// `to_string(foo{{1, 2, 3}, 4})` prints: "foo([1, 2, 3], 4)"
return "foo" + deep_to_string(std::forward_as_tuple(x.a, x.b));
}
// a pair of two ints // a pair of two ints
using foo_pair = std::pair<int, int>; using foo_pair = std::pair<int, int>;
// another pair of two ints // another alias for pairs of two ints
using foo_pair2 = std::pair<int, int>; using foo_pair2 = std::pair<int, int>;
// a struct with member vector<vector<...>> // a struct with a nested container
struct foo2 { struct foo2 {
int a; int a;
vector<vector<double>> b; vector<vector<double>> b;
}; };
// foo2 also needs to be comparable ...
bool operator==(const foo2& lhs, const foo2& rhs) { bool operator==(const foo2& lhs, const foo2& rhs) {
return lhs.a == rhs.a && lhs.b == rhs.b; return lhs.a == rhs.a && lhs.b == rhs.b;
} }
// receives `remaining` messages // ... and to be serializable
template <class T>
void serialize(T& in_or_out, foo2& x, const unsigned int) {
in_or_out & x.a;
in_or_out & x.b; // traversed automatically and recursively
}
// `deep_to_string` also traverses nested containers
std::string to_string(const foo2& x) {
return "foo" + deep_to_string(std::forward_as_tuple(x.a, x.b));
}
// receives our custom message types
void testee(event_based_actor* self, size_t remaining) { void testee(event_based_actor* self, size_t remaining) {
auto set_next_behavior = [=] { auto set_next_behavior = [=] {
if (remaining > 1) testee(self, remaining - 1); if (remaining > 1)
else self->quit(); testee(self, remaining - 1);
else
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's safe 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) {
cout << "foo_pair(" aout(self) << "foo_pair" << deep_to_string(val) << endl;
<< val.first << ", "
<< val.second << ")"
<< endl;
set_next_behavior(); set_next_behavior();
}, },
[=](const foo& val) { [=](const foo& val) {
cout << "foo({"; aout(self) << to_string(val) << endl;
auto i = val.a.begin();
auto end = val.a.end();
if (i != end) {
cout << *i;
while (++i != end) {
cout << ", " << *i;
}
}
cout << "}, " << val.b << ")" << endl;
set_next_behavior(); set_next_behavior();
} }
); );
} }
int main(int, char**) { int main(int, char**) {
// announces foo to the libcaf type system; actor_system_config cfg;
// the function expects member pointers to all elements of foo cfg.add_message_type<foo>("foo");
announce<foo>("foo", &foo::a, &foo::b); cfg.add_message_type<foo2>("foo2");
// announce foo2 to the libcaf type system, cfg.add_message_type<foo_pair>("foo_pair");
// note that recursive containers are managed automatically by libcaf // this actor system can now serialize our custom types when running
announce<foo2>("foo2", &foo2::a, &foo2::b); // a distributed CAF application or we can serialize them manually
// serialization can throw if types are not announced properly actor_system system{cfg};
try { // two variables for testing serialization
// init some test data foo2 f1;
foo2 vd; foo2 f2;
vd.a = 5; // init some test data
vd.b.resize(1); f1.a = 5;
vd.b.back().push_back(42); f1.b.resize(1);
// serialize test data f1.b.back().push_back(42);
vector<char> buf; // I/O buffer
binary_serializer bs(std::back_inserter(buf)); vector<char> buf;
bs << vd; // write f1 to buffer
// deserialize written test data from buffer binary_serializer bs{system, buf};
binary_deserializer bd(buf.data(), buf.size()); bs << f1;
foo2 vd2; // read f2 back from buffer
uniform_typeid<foo2>()->deserialize(&vd2, &bd); binary_deserializer bd{system, buf};
// deserialized data must be equal to original input bd >> f2;
assert(vd == vd2); // must be equal
// announce std::pair<int, int> to the type system assert(f1 == f2);
announce<foo_pair>("foo_pair", &foo_pair::first, &foo_pair::second);
// libcaf returns the same uniform_type_info
// instance for the type aliases foo_pair and foo_pair2
assert(uniform_typeid<foo_pair>() == uniform_typeid<foo_pair2>());
}
catch (std::exception& e) {
cerr << "error during type (de)serialization: " << e.what() << endl;
return -1;
}
// spawn a testee that receives two messages of user-defined type // spawn a testee that receives two messages of user-defined type
auto t = spawn(testee, size_t{2}); auto t = system.spawn(testee, 2);
{ // lifetime scope of self scoped_actor self{system};
scoped_actor self; // 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}); self->await_all_other_actors_done();
}
await_all_actors_done();
shutdown();
} }
// showcases how to add custom message types to CAF
// if friend access for serialization is available
#include <utility> #include <utility>
#include <iostream> #include <iostream>
...@@ -11,57 +14,67 @@ using namespace caf; ...@@ -11,57 +14,67 @@ using namespace caf;
// a simple class using getter and setter member functions // a simple class using getter and setter member functions
class foo { class foo {
int a_;
int b_;
public: public:
foo(int a0 = 0, int b0 = 0) : a_(a0), b_(b0) {
foo(int a0 = 0, int b0 = 0) : a_(a0), b_(b0) { } // nop
}
foo(const foo&) = default; foo(const foo&) = default;
foo& operator=(const foo&) = default; foo& operator=(const foo&) = default;
int a() const { return a_; } int a() const {
return a_;
}
void set_a(int val) { a_ = val; } void set_a(int val) {
a_ = val;
}
int b() const { return b_; } int b() const {
return b_;
}
void set_b(int val) { b_ = val; } void set_b(int val) {
b_ = val;
}
}; // compare
friend bool operator==(const foo& x, const foo& y) {
return x.a_ == y.a_ && x.b_ == y.b_;
}
// announce requires foo to be comparable // serialize
bool operator==(const foo& lhs, const foo& rhs) { template <class T>
return lhs.a() == rhs.a() friend void serialize(T& in_or_out, foo& x, const unsigned int) {
&& lhs.b() == rhs.b(); in_or_out & x.a_;
} in_or_out & x.b_;
}
// print
friend std::string to_string(const foo& x) {
return "foo" + deep_to_string(std::forward_as_tuple(x.a_, x.b_));
}
private:
int a_;
int b_;
};
void testee(event_based_actor* self) { void testee(event_based_actor* self) {
self->become ( self->become (
[=](const foo& val) { [=](const foo& x) {
aout(self) << "foo(" aout(self) << to_string(x) << endl;
<< val.a() << ", "
<< val.b() << ")"
<< endl;
self->quit(); self->quit();
} }
); );
} }
int main(int, char**) { int main(int, char**) {
// if a class uses getter and setter member functions, actor_system_config cfg;
// we pass those to the announce function as { getter, setter } pairs. cfg.add_message_type<foo>("foo");
announce<foo>("foo", make_pair(&foo::a, &foo::set_a), actor_system system{cfg};
make_pair(&foo::b, &foo::set_b)); scoped_actor self{system};
{ auto t = self->spawn(testee);
scoped_actor self; self->send(t, foo{1, 2});
auto t = spawn(testee); self->await_all_other_actors_done();
self->send(t, foo{1, 2});
}
await_all_actors_done();
shutdown();
return 0;
} }
// showcases how to add custom message types to CAF
// if no friend access for serialization is possible
#include <utility>
#include <iostream>
#include "caf/all.hpp"
using std::cout;
using std::endl;
using std::make_pair;
using namespace caf;
// identical to our second custom type example,
// but without friend declarations
class foo {
public:
foo(int a0 = 0, int b0 = 0) : a_(a0), b_(b0) {
// nop
}
foo(const foo&) = default;
foo& operator=(const foo&) = default;
int a() const {
return a_;
}
void set_a(int val) {
a_ = val;
}
int b() const {
return b_;
}
void set_b(int val) {
b_ = val;
}
private:
int a_;
int b_;
};
// comparing is straightforward ...
bool operator==(const foo& x, const foo& y) {
return x.a() == y.a() && x.b() == y.b();
}
// ... and so is to_string, ...
std::string to_string(const foo& x) {
return "foo" + deep_to_string(std::forward_as_tuple(x.a(), x.b()));
}
// ... but we need to split serialization into a saving ...
template <class T>
typename std::enable_if<T::is_saving::value>::type
serialize(T& out, const foo& x, const unsigned int) {
out << x.a() << x.b();
}
// ... and a loading function
template <class T>
typename std::enable_if<T::is_loading::value>::type
serialize(T& in, foo& x, const unsigned int) {
int tmp;
in >> tmp;
x.set_a(tmp);
in >> tmp;
x.set_b(tmp);
}
void testee(event_based_actor* self) {
self->become (
[=](const foo& x) {
aout(self) << to_string(x) << endl;
self->quit();
}
);
}
int main(int, char**) {
actor_system_config cfg;
cfg.add_message_type<foo>("foo");
actor_system system{cfg};
scoped_actor self{system};
auto t = self->spawn(testee);
self->send(t, foo{1, 2});
self->await_all_other_actors_done();
}
...@@ -36,12 +36,11 @@ void hello_world(event_based_actor* self, const actor& buddy) { ...@@ -36,12 +36,11 @@ void hello_world(event_based_actor* self, const actor& buddy) {
} }
int main() { int main() {
actor_system system;
// create a new actor that calls 'mirror()' // create a new actor that calls 'mirror()'
auto mirror_actor = spawn(mirror); auto mirror_actor = system.spawn(mirror);
// create another actor that calls 'hello_world(mirror_actor)'; // create another actor that calls 'hello_world(mirror_actor)';
spawn(hello_world, mirror_actor); system.spawn(hello_world, mirror_actor);
// wait until all other actors we have spawned are done // wait until all other actors we have spawned are done
await_all_actors_done(); system.await_all_actors_done();
// run cleanup code before exiting main
shutdown();
} }
...@@ -88,20 +88,17 @@ void tester(event_based_actor* self, const Handle& testee, int x, int y) { ...@@ -88,20 +88,17 @@ void tester(event_based_actor* self, const Handle& testee, int x, int y) {
); );
} }
void test_calculators() { int main() {
scoped_actor self; actor_system system;
scoped_actor self{system};
aout(self) << "blocking actor:" << endl; aout(self) << "blocking actor:" << endl;
self->spawn(tester<actor>, spawn<blocking_api>(blocking_calculator), 1, 2); self->spawn(tester<actor>,
self->spawn<blocking_api>(blocking_calculator), 1, 2);
self->await_all_other_actors_done(); self->await_all_other_actors_done();
aout(self) << "event-based actor:" << endl; aout(self) << "event-based actor:" << endl;
self->spawn(tester<actor>, spawn(calculator), 3, 4); self->spawn(tester<actor>, self->spawn(calculator), 3, 4);
self->await_all_other_actors_done(); self->await_all_other_actors_done();
aout(self) << "typed actor:" << endl; aout(self) << "typed actor:" << endl;
self->spawn(tester<calculator_actor>, spawn(typed_calculator), 5, 6); self->spawn(tester<calculator_actor>, self->spawn(typed_calculator), 5, 6);
self->await_all_other_actors_done(); self->await_all_other_actors_done();
} }
int main() {
test_calculators();
shutdown();
}
...@@ -71,7 +71,7 @@ void dancing_kirby(event_based_actor* self) { ...@@ -71,7 +71,7 @@ void dancing_kirby(event_based_actor* self) {
} }
int main() { int main() {
spawn(dancing_kirby); actor_system system;
await_all_actors_done(); system.spawn(dancing_kirby);
shutdown(); system.await_all_actors_done();
} }
...@@ -4,11 +4,22 @@ ...@@ -4,11 +4,22 @@
\ ******************************************************************************/ \ ******************************************************************************/
#include <map> #include <map>
#include <thread>
#include <vector> #include <vector>
#include <chrono> #include <chrono>
#include <sstream> #include <sstream>
#include <iostream> #include <iostream>
namespace std {
string to_string(const thread::id& x) {
ostringstream os;
os << x;
return os.str();
}
}
#include "caf/all.hpp" #include "caf/all.hpp"
using std::cout; using std::cout;
...@@ -58,9 +69,8 @@ chopstick::behavior_type taken_chopstick(chopstick::pointer self, ...@@ -58,9 +69,8 @@ chopstick::behavior_type taken_chopstick(chopstick::pointer self,
return busy_atom::value; return busy_atom::value;
}, },
[=](put_atom) { [=](put_atom) {
if (self->current_sender() == user) { if (self->current_sender() == user)
self->become(available_chopstick(self)); self->become(available_chopstick(self));
}
} }
}; };
} }
...@@ -96,8 +106,12 @@ chopstick::behavior_type taken_chopstick(chopstick::pointer self, ...@@ -96,8 +106,12 @@ chopstick::behavior_type taken_chopstick(chopstick::pointer self,
class philosopher : public event_based_actor { class philosopher : public event_based_actor {
public: public:
philosopher(const std::string& n, const chopstick& l, const chopstick& r) philosopher(actor_config& cfg,
: name(n), const std::string& n,
const chopstick& l,
const chopstick& r)
: event_based_actor(cfg),
name(n),
left(l), left(l),
right(r) { right(r) {
// a philosopher that receives {eat} stops thinking and becomes hungry // a philosopher that receives {eat} stops thinking and becomes hungry
...@@ -180,32 +194,26 @@ private: ...@@ -180,32 +194,26 @@ private:
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; // waits for some time, then go thinking again behavior eating; // wait for some time, then go thinking again
}; };
void dining_philosophers() { } // namespace <anonymous>
scoped_actor self;
int main(int, char**) {
actor_system system;
scoped_actor self{system};
// create five chopsticks // create five chopsticks
aout(self) << "chopstick ids are:"; aout(self) << "chopstick ids are:";
std::vector<chopstick> chopsticks; std::vector<chopstick> chopsticks;
for (size_t i = 0; i < 5; ++i) { for (size_t i = 0; i < 5; ++i) {
chopsticks.push_back(spawn(available_chopstick)); chopsticks.push_back(self->spawn(available_chopstick));
aout(self) << " " << chopsticks.back()->id(); aout(self) << " " << chopsticks.back()->id();
} }
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", "Descartes"}; "Nietzsche", "Descartes"};
for (size_t i = 0; i < 5; ++i) { for (size_t i = 0; i < 5; ++i)
spawn<philosopher>(names[i], chopsticks[i], chopsticks[(i + 1) % 5]); self->spawn<philosopher>(names[i], chopsticks[i], chopsticks[(i + 1) % 5]);
} system.await_all_actors_done();
}
} // namespace <anonymous>
int main(int, char**) {
dining_philosophers();
// real philosophers are never done
await_all_actors_done();
shutdown();
} }
...@@ -33,6 +33,10 @@ calculator_type::behavior_type typed_calculator(calculator_type::pointer) { ...@@ -33,6 +33,10 @@ calculator_type::behavior_type typed_calculator(calculator_type::pointer) {
class typed_calculator_class : public calculator_type::base { class typed_calculator_class : public calculator_type::base {
protected: protected:
typed_calculator_class(actor_config& cfg) : calculator_type::base(cfg) {
// nop
}
behavior_type make_behavior() override { behavior_type make_behavior() override {
return { return {
[](plus_atom, int x, int y) { [](plus_atom, int x, int y) {
...@@ -73,12 +77,11 @@ void tester(event_based_actor* self, const calculator_type& testee) { ...@@ -73,12 +77,11 @@ void tester(event_based_actor* self, const calculator_type& testee) {
} // namespace <anonymous> } // namespace <anonymous>
int main() { int main() {
actor_system system;
// test function-based impl // test function-based impl
spawn(tester, spawn(typed_calculator)); system.spawn(tester, system.spawn(typed_calculator));
await_all_actors_done(); system.await_all_actors_done();
// test class-based impl // test class-based impl
spawn(tester, spawn<typed_calculator_class>()); system.spawn(tester, system.spawn<typed_calculator_class>());
await_all_actors_done(); system.await_all_actors_done();
// done
shutdown();
} }
...@@ -51,8 +51,9 @@ behavior calculator() { ...@@ -51,8 +51,9 @@ behavior calculator() {
class client_impl : public event_based_actor { class client_impl : public event_based_actor {
public: public:
client_impl(string hostaddr, uint16_t port) client_impl(actor_config& cfg, string hostaddr, uint16_t port)
: host_(std::move(hostaddr)), : event_based_actor(cfg),
host_(std::move(hostaddr)),
port_(port) { port_(port) {
// nop // nop
} }
...@@ -103,7 +104,7 @@ private: ...@@ -103,7 +104,7 @@ private:
behavior reconnecting(std::function<void()> continuation = nullptr) { behavior reconnecting(std::function<void()> continuation = nullptr) {
using std::chrono::seconds; using std::chrono::seconds;
auto mm = io::get_middleman_actor(); auto mm = system().middleman().actor_handle();
send(mm, connect_atom::value, host_, port_); send(mm, connect_atom::value, host_, port_);
return { return {
[=](ok_atom, node_id&, actor_addr& new_server, std::set<std::string>&) { [=](ok_atom, node_id&, actor_addr& new_server, std::set<std::string>&) {
...@@ -179,16 +180,14 @@ maybe<int> toint(const string& str) { ...@@ -179,16 +180,14 @@ maybe<int> toint(const string& str) {
// converts "+" to the atom '+' and "-" to the atom '-' // converts "+" to the atom '+' and "-" to the atom '-'
maybe<atom_value> plus_or_minus(const string& str) { maybe<atom_value> plus_or_minus(const string& str) {
if (str == "+") { if (str == "+")
return maybe<atom_value>{plus_atom::value}; return plus_atom::value;
} if (str == "-")
if (str == "-") { return minus_atom::value;
return maybe<atom_value>{minus_atom::value};
}
return none; return none;
} }
void client_repl(string host, uint16_t port) { void client_repl(actor_system& system, string host, uint16_t port) {
// keeps track of requests and tries to reconnect on server failures // keeps track of requests and tries to reconnect on server failures
auto usage = [] { auto usage = [] {
cout << "Usage:" << endl cout << "Usage:" << endl
...@@ -200,7 +199,7 @@ void client_repl(string host, uint16_t port) { ...@@ -200,7 +199,7 @@ void client_repl(string host, uint16_t port) {
}; };
usage(); usage();
bool done = false; bool done = false;
auto client = spawn<client_impl>(std::move(host), port); auto client = system.spawn<client_impl>(std::move(host), port);
// defining the handler outside the loop is more efficient as it avoids // defining the handler outside the loop is more efficient as it avoids
// re-creating the same object over and over again // re-creating the same object over and over again
message_handler eval{ message_handler eval{
...@@ -258,54 +257,42 @@ int main(int argc, char** argv) { ...@@ -258,54 +257,42 @@ int main(int argc, char** argv) {
{"server,s", "run in server mode"}, {"server,s", "run in server mode"},
{"client,c", "run in client mode"} {"client,c", "run in client mode"}
}); });
if (! res.error.empty()) { if (! res.error.empty())
cerr << res.error << endl; return cerr << res.error << endl, 1;
return 1; if (res.opts.count("help") > 0)
} return cout << res.helptext << endl, 0;
if (res.opts.count("help") > 0) { // not all CLI arguments could be consumed
cout << res.helptext << endl; if (! res.remainder.empty())
return 0; return cerr << "*** invalid CLI options" << endl << res.helptext << endl, 1;
}
if (! res.remainder.empty()) {
// not all CLI arguments could be consumed
cerr << "*** invalid command line options" << endl << res.helptext << endl;
return 1;
}
bool is_server = res.opts.count("server") > 0; bool is_server = res.opts.count("server") > 0;
if (is_server == (res.opts.count("client") > 0)) { if (is_server == (res.opts.count("client") > 0)) {
if (is_server) { if (is_server)
cerr << "*** cannot be server and client at the same time" << endl; cerr << "*** cannot be server and client at the same time" << endl;
} else { else
cerr << "*** either --server or --client option must be set" << endl; cerr << "*** either --server or --client option must be set" << endl;
}
return 1;
}
if (! is_server && port == 0) {
cerr << "*** no port to server specified" << endl;
return 1; return 1;
} }
if (! is_server && port == 0)
return cerr << "*** no port to server specified" << endl, 1;
actor_system_config cfg;
cfg.load<io::middleman>();
actor_system system{cfg};
if (is_server) { if (is_server) {
auto calc = spawn(calculator); auto calc = system.spawn(calculator);
try { // try to publish math actor at given port
// try to publish math actor at given port cout << "*** try publish at port " << port << endl;
cout << "*** try publish at port " << port << endl; auto p = system.middleman().publish(calc, port);
auto p = io::publish(calc, port); if (! p)
cout << "*** server successfully published at port " << p << endl; return cerr << "*** error: " << p.error().message() << endl, 1;
cout << "*** press [enter] to quit" << endl; cout << "*** server successfully published at port " << *p << endl;
string dummy; cout << "*** press [enter] to quit" << endl;
std::getline(std::cin, dummy); string dummy;
cout << "... cya" << endl; std::getline(std::cin, dummy);
} cout << "... cya" << endl;
catch (std::exception& e) {
cerr << "*** unable to publish math actor at port " << port << "\n"
<< to_verbose_string(e) // prints exception type and e.what()
<< endl;
}
anon_send_exit(calc, exit_reason::user_shutdown); anon_send_exit(calc, exit_reason::user_shutdown);
} }
else { else {
client_repl(host, port); client_repl(system, host, port);
} }
await_all_actors_done(); system.await_all_actors_done();
shutdown();
} }
...@@ -71,18 +71,12 @@ int main(int argc, char** argv) { ...@@ -71,18 +71,12 @@ int main(int argc, char** argv) {
{"name,n", "set name", name}, {"name,n", "set name", name},
{"group,g", "join group", group_id} {"group,g", "join group", group_id}
}); });
if (! res.error.empty()) { if (! res.error.empty())
cerr << res.error << endl; return cerr << res.error << endl, 1;
return 1; if (! res.remainder.empty())
} return std::cout << res.helptext << std::endl, 1;
if (! res.remainder.empty()) { if (res.opts.count("help") > 0)
std::cout << res.helptext << std::endl; return cout << res.helptext << endl, 0;
return 1;
}
if (res.opts.count("help") > 0) {
cout << res.helptext << endl;
return 0;
}
while (name.empty()) { while (name.empty()) {
cout << "please enter your name: " << flush; cout << "please enter your name: " << flush;
if (! getline(cin, name)) { if (! getline(cin, name)) {
...@@ -90,7 +84,10 @@ int main(int argc, char** argv) { ...@@ -90,7 +84,10 @@ int main(int argc, char** argv) {
return 1; return 1;
} }
} }
auto client_actor = spawn(client, name); actor_system_config cfg;
cfg.load<io::middleman>();
actor_system system{cfg};
auto client_actor = system.spawn(client, name);
// evaluate group parameters // evaluate group parameters
if (! group_id.empty()) { if (! group_id.empty()) {
auto p = group_id.find(':'); auto p = group_id.find(':');
...@@ -101,14 +98,15 @@ int main(int argc, char** argv) { ...@@ -101,14 +98,15 @@ int main(int argc, char** argv) {
try { try {
auto module = group_id.substr(0, p); auto module = group_id.substr(0, p);
auto group_uri = group_id.substr(p + 1); auto group_uri = group_id.substr(p + 1);
auto g = (module == "remote") ? io::remote_group(group_uri) auto g = (module == "remote")
: group::get(module, group_uri); ? system.middleman().remote_group(group_uri)
: system.groups().get(module, group_uri);
anon_send(client_actor, join_atom::value, g); anon_send(client_actor, join_atom::value, g);
} }
catch (exception& e) { catch (exception& e) {
cerr << "*** exception: group::get(\"" << group_id.substr(0, p) cerr << "*** exception: group::get(\"" << group_id.substr(0, p)
<< "\", \"" << group_id.substr(p + 1) << "\") failed; " << "\", \"" << group_id.substr(p + 1) << "\") failed: "
<< to_verbose_string(e) << endl; << e.what() << endl;
} }
} }
} }
...@@ -127,12 +125,12 @@ int main(int argc, char** argv) { ...@@ -127,12 +125,12 @@ int main(int argc, char** argv) {
[&](const string& cmd, const string& mod, const string& id) { [&](const string& cmd, const string& mod, const string& id) {
if (cmd == "/join") { if (cmd == "/join") {
try { try {
group grp = (mod == "remote") ? io::remote_group(id) group grp = (mod == "remote") ? system.middleman().remote_group(id)
: group::get(mod, id); : system.groups().get(mod, id);
anon_send(client_actor, join_atom::value, grp); anon_send(client_actor, join_atom::value, grp);
} }
catch (exception& e) { catch (exception& e) {
cerr << "*** exception: " << to_verbose_string(e) << endl; cerr << "*** exception: " << e.what() << endl;
} }
} }
else { else {
...@@ -158,6 +156,5 @@ int main(int argc, char** argv) { ...@@ -158,6 +156,5 @@ int main(int argc, char** argv) {
} }
// force actor to quit // force actor to quit
anon_send_exit(client_actor, exit_reason::user_shutdown); anon_send_exit(client_actor, exit_reason::user_shutdown);
await_all_actors_done(); system.await_all_actors_done();
shutdown();
} }
...@@ -23,50 +23,23 @@ int main(int argc, char** argv) { ...@@ -23,50 +23,23 @@ int main(int argc, char** argv) {
auto res = message_builder(argv + 1, argv + argc).extract_opts({ auto res = message_builder(argv + 1, argv + argc).extract_opts({
{"port,p", "set port", port} {"port,p", "set port", port}
}); });
if (! res.error.empty()) { if (! res.error.empty())
cerr << res.error << endl; return cerr << res.error << endl, 1;
return 1; if (res.opts.count("help") > 0)
} return cout << res.helptext << endl, 0;
if (res.opts.count("help") > 0) { if (! res.remainder.empty())
cout << res.helptext << endl; return cerr << "*** too many arguments" << endl << res.helptext << endl, 1;
return 0; if (res.opts.count("port") == 0 || port <= 1024)
} return cerr << "*** no valid port given" << endl << res.helptext << endl, 1;
if (! res.remainder.empty()) { actor_system system{actor_system_config{}.load<io::middleman>()};
// not all CLI arguments could be consumed auto pres = system.middleman().publish_local_groups(port);
cerr << "*** too many arguments" << endl << res.helptext << endl; if (! pres)
return 1; return cerr << "*** error: " << pres.error().message() << endl, 1;
}
if (res.opts.count("port") == 0 || port <= 1024) {
cerr << "*** no valid port (>1024) given" << endl << res.helptext << endl;
return 1;
}
try {
// try to bind the group server to the given port,
// this allows other nodes to access groups of this server via
// group::get("remote", "<group>@<host>:<port>");
// note: it is not needed to explicitly create a <group> on the server,
// as groups are created on-the-fly on first usage
io::publish_local_groups(port);
}
catch (bind_failure& e) {
// thrown if <port> is already in use
cerr << "*** bind_failure: " << e.what() << endl;
return 2;
}
catch (network_error& e) {
// thrown on errors in the socket API
cerr << "*** network error: " << e.what() << endl;
return 2;
}
cout << "type 'quit' to shutdown the server" << endl; cout << "type 'quit' to shutdown the server" << endl;
string line; string line;
while (getline(cin, line)) { while (getline(cin, line))
if (line == "quit") { if (line == "quit")
return 0; return 0;
} else
else {
cerr << "illegal command" << endl; cerr << "illegal command" << endl;
}
}
shutdown();
} }
#include <utility>
#include <iostream>
#include "caf/all.hpp"
using std::cout;
using std::endl;
using std::make_pair;
using namespace caf;
// a simple class using overloaded getter and setter member functions
class foo {
int a_;
int b_;
public:
foo() : a_(0), b_(0) { }
foo(int a0, int b0) : a_(a0), b_(b0) { }
foo(const foo&) = default;
foo& operator=(const foo&) = default;
int a() const { return a_; }
void a(int val) { a_ = val; }
int b() const { return b_; }
void b(int val) { b_ = val; }
};
// announce requires foo to have the equal operator implemented
bool operator==(const foo& lhs, const foo& rhs) {
return lhs.a() == rhs.a()
&& lhs.b() == rhs.b();
}
// a member function pointer to get an attribute of foo
using foo_getter = int (foo::*)() const;
// a member function pointer to set an attribute of foo
using foo_setter = void (foo::*)(int);
void testee(event_based_actor* self) {
self->become (
[=](const foo& val) {
aout(self) << "foo("
<< val.a() << ", "
<< val.b() << ")"
<< endl;
self->quit();
}
);
}
int main(int, char**) {
// since the member function "a" is ambiguous, the compiler
// also needs a type to select the correct overload
foo_getter g1 = &foo::a;
foo_setter s1 = &foo::a;
// same is true for b
foo_getter g2 = &foo::b;
foo_setter s2 = &foo::b;
// equal to example 3
announce<foo>("foo", make_pair(g1, s1), make_pair(g2, s2));
// alternative syntax that uses casts instead of variables
// (returns false since foo is already announced)
announce<foo>("foo",
make_pair(static_cast<foo_getter>(&foo::a),
static_cast<foo_setter>(&foo::a)),
make_pair(static_cast<foo_getter>(&foo::b),
static_cast<foo_setter>(&foo::b)));
// spawn a new testee and send it a foo
{
scoped_actor self;
self->send(spawn(testee), foo{1, 2});
}
await_all_actors_done();
shutdown();
return 0;
}
#include <iostream>
#include "caf/all.hpp"
#include "caf/to_string.hpp"
using std::cout;
using std::endl;
using std::make_pair;
using namespace caf;
// the foo class from example 3
class foo {
int a_;
int b_;
public:
foo() : a_(0), b_(0) { }
foo(int a0, int b0) : a_(a0), b_(b0) { }
foo(const foo&) = default;
foo& operator=(const foo&) = default;
int a() const { return a_; }
void set_a(int val) { a_ = val; }
int b() const { return b_; }
void set_b(int val) { b_ = val; }
};
// needed for operator==() of bar
bool operator==(const foo& lhs, const foo& rhs) {
return lhs.a() == rhs.a()
&& lhs.b() == rhs.b();
}
// simple struct that has foo as a member
struct bar {
foo f;
int i;
};
// announce requires bar to have the equal operator implemented
bool operator==(const bar& lhs, const bar& rhs) {
return lhs.f == rhs.f
&& lhs.i == rhs.i;
}
// "worst case" class ... not a good software design at all ;)
class baz {
foo f_;
public:
bar b;
// announce requires a default constructor
baz() = default;
inline baz(const foo& mf, const bar& mb) : f_(mf), b(mb) { }
const foo& f() const { return f_; }
void set_f(const foo& val) { f_ = val; }
};
// even worst case classes have to implement operator==
bool operator==(const baz& lhs, const baz& rhs) {
return lhs.f() == rhs.f()
&& lhs.b == rhs.b;
}
// receives `remaining` messages
void testee(event_based_actor* self, size_t remaining) {
auto set_next_behavior = [=] {
if (remaining > 1) testee(self, remaining - 1);
else self->quit();
};
self->become (
[=](const bar& val) {
aout(self) << "bar(foo("
<< val.f.a() << ", "
<< val.f.b() << "), "
<< val.i << ")"
<< endl;
set_next_behavior();
},
[=](const baz& val) {
// prints: baz ( foo ( 1, 2 ), bar ( foo ( 3, 4 ), 5 ) )
aout(self) << to_string(make_message(val)) << endl;
set_next_behavior();
}
);
}
int main(int, char**) {
// bar has a non-trivial data member f, thus, we have to told
// announce how to serialize/deserialize this member;
// this is was the compound_member function is for;
// it takes a pointer to the non-trivial member as first argument
// followed by all "sub-members" either as member pointer or
// { getter, setter } pair
auto meta_bar_f = [] {
return compound_member(&bar::f,
make_pair(&foo::a, &foo::set_a),
make_pair(&foo::b, &foo::set_b));
};
// with meta_bar_f, we can now announce bar
announce<bar>("bar", meta_bar_f(), &bar::i);
// baz has non-trivial data members with getter/setter pair
// and getter returning a mutable reference
announce<baz>("baz", compound_member(make_pair(&baz::f, &baz::set_f),
make_pair(&foo::a, &foo::set_a),
make_pair(&foo::b, &foo::set_b)),
// compound member that has a compound member
compound_member(&baz::b, meta_bar_f(), &bar::i));
// spawn a testee that receives two messages
auto t = spawn(testee, size_t{2});
{
scoped_actor self;
self->send(t, bar{foo{1, 2}, 3});
self->send(t, baz{foo{1, 2}, bar{foo{3, 4}, 5}});
}
await_all_actors_done();
shutdown();
return 0;
}
/******************************************************************************
* This example shows how to implement serialize/deserialize to announce *
* non-trivial data structures to the libcaf type system. *
* *
* Announce() auto-detects STL compliant containers and provides *
* an easy way to tell libcaf how to serialize user defined types. *
* See announce_example 1-4 for usage examples. *
* *
* You should use "hand written" serialize/deserialize implementations *
* if and only if there is no other way. *
******************************************************************************/
#include <cstdint>
#include <iostream>
#include "caf/all.hpp"
#include "caf/to_string.hpp"
using std::cout;
using std::endl;
using namespace caf;
namespace {
// a node containing an integer and a vector of children
struct tree_node {
std::uint32_t value;
std::vector<tree_node> children;
explicit tree_node(std::uint32_t v = 0) : value(v) {
// nop
}
tree_node& add_child(std::uint32_t v = 0) {
children.emplace_back(v);
return *this;
}
tree_node(const tree_node&) = default;
// recursively print this node and all of its children to stdout
void print() const {
// format is: value { children0, children1, ..., childrenN }
// e.g., 10 { 20 { 21, 22 }, 30 }
cout << value;
if (children.empty() == false) {
cout << " { ";
auto begin = children.begin();
auto end = children.end();
for (auto i = begin; i != end; ++i) {
if (i != begin) {
cout << ", ";
}
i->print();
}
cout << " } ";
}
}
};
// a very primitive tree implementation
struct tree {
tree_node root;
// print tree to stdout
void print() const {
cout << "tree::print: ";
root.print();
cout << endl;
}
};
// tree nodes are equals if values and all values of all children are equal
bool operator==(const tree_node& lhs, const tree_node& rhs) {
return (lhs.value == rhs.value) && (lhs.children == rhs.children);
}
bool operator==(const tree& lhs, const tree& rhs) {
return lhs.root == rhs.root;
}
// abstract_uniform_type_info implements all functions of uniform_type_info
// except for serialize() and deserialize() if the template parameter T:
// - does have a default constructor
// - does have a copy constructor
// - does provide operator==
class tree_type_info : public abstract_uniform_type_info<tree> {
public:
tree_type_info() : abstract_uniform_type_info<tree>("tree") {
// nop
}
protected:
void serialize(const void* ptr, serializer* sink) const {
// ptr is guaranteed to be a pointer of type tree
auto tree_ptr = reinterpret_cast<const tree*>(ptr);
// recursively serialize nodes, beginning with root
serialize_node(tree_ptr->root, sink);
}
void deserialize(void* ptr, deserializer* source) const {
// ptr is guaranteed to be a pointer of type tree
auto tree_ptr = reinterpret_cast<tree*>(ptr);
tree_ptr->root.children.clear();
// recursively deserialize nodes, beginning with root
deserialize_node(tree_ptr->root, source);
}
private:
void serialize_node(const tree_node& node, serializer* sink) const {
// value, ... children ...
sink->write_value(node.value);
sink->begin_sequence(node.children.size());
for (const tree_node& subnode : node.children) {
serialize_node(subnode, sink);
}
sink->end_sequence();
}
void deserialize_node(tree_node& node, deserializer* source) const {
// value, ... children ...
auto value = source->read<std::uint32_t>();
node.value = value;
auto num_children = source->begin_sequence();
for (size_t i = 0; i < num_children; ++i) {
node.add_child();
deserialize_node(node.children.back(), source);
}
source->end_sequence();
}
};
using tree_vector = std::vector<tree>;
// receives `remaining` messages
void testee(event_based_actor* self, size_t remaining) {
auto set_next_behavior = [=] {
if (remaining > 1) testee(self, remaining - 1);
else self->quit();
};
self->become (
[=](const tree& tmsg) {
// prints the tree in its serialized format:
// @<> ( { tree ( 0, { 10, { 11, { }, 12, { }, 13, { } }, 20, { 21, { }, 22, { } } } ) } )
cout << "to_string(self->current_message()): "
<< to_string(self->current_message())
<< endl;
// prints the tree using the print member function:
// 0 { 10 { 11, 12, 13 } , 20 { 21, 22 } }
tmsg.print();
set_next_behavior();
},
[=](const tree_vector& trees) {
// prints "received 2 trees"
cout << "received " << trees.size() << " trees" << endl;
// prints:
// @<> ( {
// std::vector<tree, std::allocator<tree>> ( {
// tree ( 0, { 10, { 11, { }, 12, { }, 13, { } }, 20, { 21, { }, 22, { } } } ),
// tree ( 0, { 10, { 11, { }, 12, { }, 13, { } }, 20, { 21, { }, 22, { } } } )
// )
// } )
cout << "to_string: " << to_string(self->current_message()) << endl;
set_next_behavior();
}
);
}
} // namespace <anonymous>
int main() {
// the tree_type_info is owned by libcaf after this function call
announce(typeid(tree), std::unique_ptr<uniform_type_info>{new tree_type_info});
announce<tree_vector>("tree_vector");
tree t0; // create a tree and fill it with some data
t0.root.add_child(10);
t0.root.children.back().add_child(11).add_child(12).add_child(13);
t0.root.add_child(20);
t0.root.children.back().add_child(21).add_child(22);
/*
tree t is now:
0
/ \
/ \
/ \
10 20
/ |\ / \
/ | \ / \
11 12 13 21 22
*/
{ // lifetime scope of self
scoped_actor self;
// spawn a testee that receives two messages
auto t = spawn(testee, size_t{2});
// send a tree
self->send(t, t0);
// send a vector of trees
tree_vector tvec;
tvec.push_back(t0);
tvec.push_back(t0);
self->send(t, tvec);
}
await_all_actors_done();
shutdown();
return 0;
}
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