Commit 30c319af authored by Dominik Charousset's avatar Dominik Charousset

Clean up manual sections and examples

parent f503a0f4
/******************************************************************************\ // This example program showcases how to manually manage socket I/O using a
* This example program showcases how to manually manage socket IO using * // broker. Server and client exchange integers in a "ping-pong protocol".
* a broker. Server and client exchange integers in a 'ping-pong protocol'. * //
* * // Minimal setup:
* Minimal setup: * // - ./build/bin/broker -s 4242
* - ./build/bin/broker -s 4242 * // - ./build/bin/broker -c localhost 4242
* - ./build/bin/broker -c localhost 4242 *
\
******************************************************************************/
// Manual refs: 42-47 (Actors.tex)
#include "caf/config.hpp" #include "caf/config.hpp"
...@@ -39,12 +34,14 @@ using namespace caf::io; ...@@ -39,12 +34,14 @@ using namespace caf::io;
namespace { namespace {
// --(rst-attach-begin)--
// Utility function to print an exit message with custom name. // Utility function to print an exit message with custom name.
void print_on_exit(const actor& hdl, const std::string& name) { void print_on_exit(const actor& hdl, const std::string& name) {
hdl->attach_functor([=](const error& reason) { hdl->attach_functor([=](const error& reason) {
cout << name << " exited: " << to_string(reason) << endl; cout << name << " exited: " << to_string(reason) << endl;
}); });
} }
// --(rst-attach-end)--
enum class op : uint8_t { enum class op : uint8_t {
ping, ping,
......
// Showcases how to add custom POD message types. // Showcases how to add custom POD message types.
// Manual refs: 24-27, 30-34, 75-78, 81-84 (ConfiguringActorApplications)
// 23-33 (TypeInspection)
#include <cassert> #include <cassert>
#include <iostream> #include <iostream>
#include <string> #include <string>
...@@ -117,4 +114,4 @@ void caf_main(actor_system& sys) { ...@@ -117,4 +114,4 @@ void caf_main(actor_system& sys) {
self->send(t, foo_pair2{3, 4}); self->send(t, foo_pair2{3, 4});
} }
CAF_MAIN(id_block::custom_types_1) CAF_MAIN(caf::id_block::custom_types_1)
// showcases how to add custom message types to CAF // Showcases how to add custom message types to CAF if friend access for
// if friend access for serialization is available // serialization is available.
#include <utility> #include <utility>
#include <iostream> #include <iostream>
......
// Showcases custom message types that cannot provide // Showcases custom message types that cannot provide friend access to the
// friend access to the inspect() function. // inspect() function.
#include <iostream> #include <iostream>
#include <utility> #include <utility>
......
/******************************************************************************\ // This example is an implementation of the classical Dining Philosophers
* This example is an implementation of the classical Dining Philosophers * // exercise using only libcaf's event-based actor implementation.
* exercise using only libcaf's event-based actor implementation. *
\******************************************************************************/
#include <chrono> #include <chrono>
#include <iostream> #include <iostream>
......
/******************************************************************************\ // This example is a very basic, non-interactive math service implemented for
* This example is a very basic, non-interactive math service implemented * // both the blocking and the event-based API.
* for both the blocking and the event-based API. *
\******************************************************************************/
// Manual refs: lines 17-18, 21-26, 28-56, 58-92, 123-128 (Actor)
#include <iostream> #include <iostream>
...@@ -14,10 +10,13 @@ using namespace caf; ...@@ -14,10 +10,13 @@ using namespace caf;
namespace { namespace {
// --(rst-actor-begin)--
using calculator_actor using calculator_actor
= typed_actor<replies_to<add_atom, int32_t, int32_t>::with<int32_t>, = typed_actor<replies_to<add_atom, int32_t, int32_t>::with<int32_t>,
replies_to<sub_atom, int32_t, int32_t>::with<int32_t>>; replies_to<sub_atom, int32_t, int32_t>::with<int32_t>>;
// --(rst-actor-end)--
// --(rst-fwd-begin)--
// prototypes and forward declarations // prototypes and forward declarations
behavior calculator_fun(event_based_actor* self); behavior calculator_fun(event_based_actor* self);
void blocking_calculator_fun(blocking_actor* self); void blocking_calculator_fun(blocking_actor* self);
...@@ -25,7 +24,9 @@ calculator_actor::behavior_type typed_calculator_fun(); ...@@ -25,7 +24,9 @@ calculator_actor::behavior_type typed_calculator_fun();
class calculator; class calculator;
class blocking_calculator; class blocking_calculator;
class typed_calculator; class typed_calculator;
// --(rst-fwd-end)--
// --(rst-funs-begin)--
// function-based, dynamically typed, event-based API // function-based, dynamically typed, event-based API
behavior calculator_fun(event_based_actor*) { behavior calculator_fun(event_based_actor*) {
return { return {
...@@ -55,7 +56,9 @@ calculator_actor::behavior_type typed_calculator_fun() { ...@@ -55,7 +56,9 @@ calculator_actor::behavior_type typed_calculator_fun() {
[](sub_atom, int32_t a, int32_t b) { return a - b; }, [](sub_atom, int32_t a, int32_t b) { return a - b; },
}; };
} }
// --(rst-funs-end)--
// --(rst-classes-begin)--
// class-based, dynamically typed, event-based API // class-based, dynamically typed, event-based API
class calculator : public event_based_actor { class calculator : public event_based_actor {
public: public:
...@@ -91,6 +94,7 @@ public: ...@@ -91,6 +94,7 @@ public:
return typed_calculator_fun(); return typed_calculator_fun();
} }
}; };
// --(rst-classes-end)--
void tester(scoped_actor&) { void tester(scoped_actor&) {
// end of recursion // end of recursion
...@@ -121,12 +125,14 @@ void tester(scoped_actor& self, const Handle& hdl, int32_t x, int32_t y, ...@@ -121,12 +125,14 @@ void tester(scoped_actor& self, const Handle& hdl, int32_t x, int32_t y,
} }
void caf_main(actor_system& system) { void caf_main(actor_system& system) {
// --(rst-spawn-begin)--
auto a1 = system.spawn(blocking_calculator_fun); auto a1 = system.spawn(blocking_calculator_fun);
auto a2 = system.spawn(calculator_fun); auto a2 = system.spawn(calculator_fun);
auto a3 = system.spawn(typed_calculator_fun); auto a3 = system.spawn(typed_calculator_fun);
auto a4 = system.spawn<blocking_calculator>(); auto a4 = system.spawn<blocking_calculator>();
auto a5 = system.spawn<calculator>(); auto a5 = system.spawn<calculator>();
auto a6 = system.spawn<typed_calculator>(); auto a6 = system.spawn<typed_calculator>();
// --(rst-spawn-end)--
scoped_actor self{system}; scoped_actor self{system};
tester(self, a1, 1, 2, a2, 3, 4, a3, 5, 6, a4, 7, 8, a5, 9, 10, a6, 11, 12); tester(self, a1, 1, 2, a2, 3, 4, a3, 5, 6, a4, 7, 8, a5, 9, 10, a6, 11, 12);
self->send_exit(a1, exit_reason::user_shutdown); self->send_exit(a1, exit_reason::user_shutdown);
......
/******************************************************************************\ // This example is a very basic, non-interactive math service implemented for
* This example is a very basic, non-interactive math service implemented * // both the blocking and the event-based API.
* for both the blocking and the event-based API. *
\******************************************************************************/
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 18-44, and 49-50 (Actor.tex)
#include <cstdint> #include <cstdint>
#include <iostream> #include <iostream>
...@@ -16,6 +10,7 @@ using std::cout; ...@@ -16,6 +10,7 @@ using std::cout;
using std::endl; using std::endl;
using namespace caf; using namespace caf;
// --(rst-cell-begin)--
using cell = typed_actor<reacts_to<put_atom, int32_t>, using cell = typed_actor<reacts_to<put_atom, int32_t>,
replies_to<get_atom>::with<int32_t>>; replies_to<get_atom>::with<int32_t>>;
...@@ -24,14 +19,19 @@ struct cell_state { ...@@ -24,14 +19,19 @@ struct cell_state {
}; };
cell::behavior_type type_checked_cell(cell::stateful_pointer<cell_state> self) { cell::behavior_type type_checked_cell(cell::stateful_pointer<cell_state> self) {
return {[=](put_atom, int32_t val) { self->state.value = val; }, return {
[=](get_atom) { return self->state.value; }}; [=](put_atom, int32_t val) { self->state.value = val; },
[=](get_atom) { return self->state.value; },
};
} }
behavior unchecked_cell(stateful_actor<cell_state>* self) { behavior unchecked_cell(stateful_actor<cell_state>* self) {
return {[=](put_atom, int32_t val) { self->state.value = val; }, return {
[=](get_atom) { return self->state.value; }}; [=](put_atom, int32_t val) { self->state.value = val; },
[=](get_atom) { return self->state.value; },
};
} }
// --(rst-cell-end)--
void caf_main(actor_system& system) { void caf_main(actor_system& system) {
// create one cell for each implementation // create one cell for each implementation
......
/******************************************************************************\ // This example illustrates how to do time-triggered loops in CAF.
* This example illustrates how to do time-triggered loops in libcaf. *
\******************************************************************************/
#include <algorithm> #include <algorithm>
#include <chrono> #include <chrono>
...@@ -8,29 +6,26 @@ ...@@ -8,29 +6,26 @@
#include "caf/all.hpp" #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 58-75 (MessagePassing.tex)
using std::cout; using std::cout;
using std::endl; using std::endl;
using std::pair; using std::pair;
using namespace caf; using namespace caf;
// ASCII art figures // ASCII art figures.
constexpr const char* figures[] = { constexpr const char* figures[] = {
"<(^.^<)", "<(^.^<)",
"<(^.^)>", "<(^.^)>",
"(>^.^)>", "(>^.^)>",
}; };
// Bundes an index to an ASCII art figure plus its position on screen.
struct animation_step { struct animation_step {
size_t figure_idx; size_t figure_idx;
size_t offset; size_t offset;
}; };
// array of {figure, offset} pairs // Array of {figure, offset} pairs.
constexpr animation_step animation_steps[] = { constexpr animation_step animation_steps[] = {
{1, 7}, {0, 7}, {0, 6}, {0, 5}, {1, 5}, {2, 5}, {2, 6}, {1, 7}, {0, 7}, {0, 6}, {0, 5}, {1, 5}, {2, 5}, {2, 6},
{2, 7}, {2, 8}, {2, 9}, {2, 10}, {1, 10}, {0, 10}, {0, 9}, {2, 7}, {2, 8}, {2, 9}, {2, 10}, {1, 10}, {0, 10}, {0, 9},
...@@ -38,41 +33,48 @@ constexpr animation_step animation_steps[] = { ...@@ -38,41 +33,48 @@ constexpr animation_step animation_steps[] = {
{0, 12}, {0, 11}, {0, 10}, {0, 9}, {0, 8}, {0, 7}, {1, 7}, {0, 12}, {0, 11}, {0, 10}, {0, 9}, {0, 8}, {0, 7}, {1, 7},
}; };
// Width of the printed area.
constexpr size_t animation_width = 20; constexpr size_t animation_width = 20;
// "draws" an animation step by printing "{offset_whitespaces}{figure}{padding}" // Draws an animation step by printing "{offset_whitespaces}{figure}{padding}".
void draw_kirby(const animation_step& animation) { void draw_kirby(const animation_step& animation) {
cout.width(animation_width); cout.width(animation_width);
// override last figure // Override last figure.
cout << '\r'; cout << '\r';
// print offset // Print left padding (offset).
std::fill_n(std::ostream_iterator<char>{cout}, animation.offset, ' '); std::fill_n(std::ostream_iterator<char>{cout}, animation.offset, ' ');
// print figure // Print figure.
cout << figures[animation.figure_idx]; cout << figures[animation.figure_idx];
// print padding // Print right padding.
cout.fill(' '); cout.fill(' ');
// make sure figure is printed // Make sure figure is visible.
cout.flush(); cout.flush();
} }
// uses a message-based loop to iterate over all animation steps // --(rst-dancing-kirby-begin)--
void dancing_kirby(event_based_actor* self) { // Uses a message-based loop to iterate over all animation steps.
// let's get it started behavior dancing_kirby(event_based_actor* self) {
self->send(self, update_atom_v, size_t{0}); // Let's get started.
self->become([=](update_atom, size_t step) { auto i = std::begin(animation_steps);
if (step == sizeof(animation_step)) { auto e = std::end(animation_steps);
// we've printed all animation steps (done) self->send(self, update_atom_v);
cout << endl; return {
self->quit(); [=](update_atom) mutable {
return; // We're done when reaching the past-the-end position.
} if (i == e) {
// print given step cout << endl;
draw_kirby(animation_steps[step]); self->quit();
// animate next step in 150ms return;
self->delayed_send(self, std::chrono::milliseconds(150), update_atom_v, }
step + 1); // Print current animation step.
}); draw_kirby(*i);
// Animate next step in 150ms.
++i;
self->delayed_send(self, std::chrono::milliseconds(150), update_atom_v);
},
};
} }
// --(rst-dancing-kirby-end)--
void caf_main(actor_system& system) { void caf_main(actor_system& system) {
system.spawn(dancing_kirby); system.spawn(dancing_kirby);
......
#include <cstdint>
#include <iostream> #include <iostream>
#include "caf/all.hpp" #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-36 (MessagePassing.tex)
using namespace caf; using namespace caf;
// using add_atom = atom_constant<atom("add")>; (defined in atom.hpp) // --(rst-delegate-begin)--
using calc = typed_actor<replies_to<add_atom, int32_t, int32_t>::with<int32_t>>; using calc = typed_actor<replies_to<add_atom, int32_t, int32_t>::with<int32_t>>;
void actor_a(event_based_actor* self, const calc& worker) { void actor_a(event_based_actor* self, const calc& worker) {
...@@ -32,6 +28,7 @@ calc::behavior_type actor_c() { ...@@ -32,6 +28,7 @@ calc::behavior_type actor_c() {
[](add_atom, int32_t x, int32_t y) { return x + y; }, [](add_atom, int32_t x, int32_t y) { return x + y; },
}; };
} }
// --(rst-delegate-end)--
void caf_main(actor_system& system) { void caf_main(actor_system& system) {
system.spawn(actor_a, system.spawn(actor_b, system.spawn(actor_c))); system.spawn(actor_a, system.spawn(actor_b, system.spawn(actor_c)));
......
/******************************************************************************\ // A basic, interactive divider.
* A very basic, interactive divider. *
\******************************************************************************/
// Manual refs: 17-19, 49-59, 70-76 (MessagePassing);
// 17-47 (Error)
#include <iostream> #include <iostream>
...@@ -14,10 +9,21 @@ using std::endl; ...@@ -14,10 +9,21 @@ using std::endl;
using std::flush; using std::flush;
using namespace caf; using namespace caf;
enum class math_error : uint8_t;
CAF_BEGIN_TYPE_ID_BLOCK(divider, first_custom_type_id)
CAF_ADD_TYPE_ID(divider, (math_error))
CAF_END_TYPE_ID_BLOCK(divider)
// --(rst-divider-begin)--
enum class math_error : uint8_t { enum class math_error : uint8_t {
division_by_zero = 1, division_by_zero = 1,
}; };
CAF_ERROR_CODE_ENUM(math_error)
std::string to_string(math_error x) { std::string to_string(math_error x) {
switch (x) { switch (x) {
case math_error::division_by_zero: case math_error::division_by_zero:
...@@ -27,14 +33,6 @@ std::string to_string(math_error x) { ...@@ -27,14 +33,6 @@ std::string to_string(math_error x) {
} }
} }
CAF_BEGIN_TYPE_ID_BLOCK(divider, first_custom_type_id)
CAF_ADD_TYPE_ID(divider, (math_error))
CAF_END_TYPE_ID_BLOCK(divider)
CAF_ERROR_CODE_ENUM(math_error)
using divider = typed_actor<replies_to<div_atom, double, double>::with<double>>; using divider = typed_actor<replies_to<div_atom, double, double>::with<double>>;
divider::behavior_type divider_impl() { divider::behavior_type divider_impl() {
...@@ -46,6 +44,7 @@ divider::behavior_type divider_impl() { ...@@ -46,6 +44,7 @@ divider::behavior_type divider_impl() {
}, },
}; };
} }
// --(rst-divider-end)--
void caf_main(actor_system& system) { void caf_main(actor_system& system) {
double x; double x;
...@@ -54,6 +53,7 @@ void caf_main(actor_system& system) { ...@@ -54,6 +53,7 @@ void caf_main(actor_system& system) {
std::cin >> x; std::cin >> x;
cout << "y: " << flush; cout << "y: " << flush;
std::cin >> y; std::cin >> y;
// --(rst-request-begin)--
auto div = system.spawn(divider_impl); auto div = system.spawn(divider_impl);
scoped_actor self{system}; scoped_actor self{system};
self->request(div, std::chrono::seconds(10), div_atom_v, x, y) self->request(div, std::chrono::seconds(10), div_atom_v, x, y)
...@@ -63,6 +63,7 @@ void caf_main(actor_system& system) { ...@@ -63,6 +63,7 @@ void caf_main(actor_system& system) {
aout(self) << "*** cannot compute " << x << " / " << y << " => " aout(self) << "*** cannot compute " << x << " / " << y << " => "
<< to_string(err) << endl; << to_string(err) << endl;
}); });
// --(rst-request-end)--
} }
CAF_MAIN(id_block::divider) CAF_MAIN(id_block::divider)
#include "caf/all.hpp"
#include <cassert> #include <cassert>
#include <cstdint> #include <cstdint>
#include <iostream> #include <iostream>
#include "caf/all.hpp"
enum class ec : uint8_t { enum class ec : uint8_t {
push_to_full = 1, push_to_full = 1,
pop_from_empty, pop_from_empty,
......
/******************************************************************************\ // Illustrates how to use response promises.
* Illustrates response promises. *
\******************************************************************************/
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 18-43 (MessagePassing.tex)
#include <iostream> #include <iostream>
...@@ -15,17 +9,16 @@ using std::endl; ...@@ -15,17 +9,16 @@ using std::endl;
using namespace caf; using namespace caf;
// --(rst-promises-begin)--
using adder using adder
= typed_actor<replies_to<add_atom, int32_t, int32_t>::with<int32_t>>; = typed_actor<replies_to<add_atom, int32_t, int32_t>::with<int32_t>>;
// function-based, statically typed, event-based API
adder::behavior_type worker() { adder::behavior_type worker() {
return { return {
[](add_atom, int32_t a, int32_t b) { return a + b; }, [](add_atom, int32_t a, int32_t b) { return a + b; },
}; };
} }
// function-based, statically typed, event-based API
adder::behavior_type calculator_master(adder::pointer self) { adder::behavior_type calculator_master(adder::pointer self) {
auto w = self->spawn(worker); auto w = self->spawn(worker);
return { return {
...@@ -38,6 +31,7 @@ adder::behavior_type calculator_master(adder::pointer self) { ...@@ -38,6 +31,7 @@ adder::behavior_type calculator_master(adder::pointer self) {
}, },
}; };
} }
// --(rst-promises-end)--
void caf_main(actor_system& system) { void caf_main(actor_system& system) {
auto f = make_function_view(system.spawn(calculator_master)); auto f = make_function_view(system.spawn(calculator_master));
......
/******************************************************************************\ // Illustrates semantics of request().{then|await|receive}.
* Illustrates semantics of request().{then|await|receive}. *
\******************************************************************************/
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 20-37, 39-51, 53-64, 67-69 (MessagePassing.tex)
#include <chrono> #include <chrono>
#include <cstdint> #include <cstdint>
...@@ -16,8 +10,10 @@ ...@@ -16,8 +10,10 @@
using std::endl; using std::endl;
using std::vector; using std::vector;
using std::chrono::seconds; using std::chrono::seconds;
using namespace caf; using namespace caf;
// --(rst-cell-begin)--
using cell = typed_actor<reacts_to<put_atom, int32_t>, using cell = typed_actor<reacts_to<put_atom, int32_t>,
replies_to<get_atom>::with<int32_t>>; replies_to<get_atom>::with<int32_t>>;
...@@ -59,11 +55,14 @@ void blocking_testee(blocking_actor* self, vector<cell> cells) { ...@@ -59,11 +55,14 @@ void blocking_testee(blocking_actor* self, vector<cell> cells) {
aout(self) << "cell #" << x.id() << " -> " << to_string(err) << endl; aout(self) << "cell #" << x.id() << " -> " << to_string(err) << endl;
}); });
} }
// --(rst-cell-end)--
void caf_main(actor_system& system) { void caf_main(actor_system& system) {
vector<cell> cells; vector<cell> cells;
// --(rst-spawn-begin)--
for (auto i = 0; i < 5; ++i) for (auto i = 0; i < 5; ++i)
cells.emplace_back(system.spawn(cell_impl, i * i)); cells.emplace_back(system.spawn(cell_impl, i * i));
// --(rst-spawn-end)--
scoped_actor self{system}; scoped_actor self{system};
aout(self) << "waiting_testee" << endl; aout(self) << "waiting_testee" << endl;
auto x1 = self->spawn(waiting_testee, cells); auto x1 = self->spawn(waiting_testee, cells);
......
/******************************************************************************\ // This example is a very basic, non-interactive math service implemented using
* This example is a very basic, non-interactive math service implemented * // typed actors.
* using typed actors. *
\******************************************************************************/
#include "caf/all.hpp" #include "caf/all.hpp"
#include <cassert> #include <cassert>
......
...@@ -9,8 +9,6 @@ ...@@ -9,8 +9,6 @@
// Run client at the same host: // Run client at the same host:
// - ./build/bin/distributed_math_actor -c -p 4242 // - ./build/bin/distributed_math_actor -c -p 4242
// Manual refs: 206-218 (ConfiguringActorSystems)
#include <array> #include <array>
#include <cassert> #include <cassert>
#include <functional> #include <functional>
...@@ -199,6 +197,7 @@ optional<int> toint(const string& str) { ...@@ -199,6 +197,7 @@ optional<int> toint(const string& str) {
return none; return none;
} }
// --(rst-config-begin)--
class config : public actor_system_config { class config : public actor_system_config {
public: public:
uint16_t port = 0; uint16_t port = 0;
...@@ -212,6 +211,7 @@ public: ...@@ -212,6 +211,7 @@ public:
.add(server_mode, "server-mode,s", "enable server mode"); .add(server_mode, "server-mode,s", "enable server mode");
} }
}; };
// --(rst-config-end)--
void client_repl(actor_system& system, const config& cfg) { void client_repl(actor_system& system, const config& cfg) {
// keeps track of requests and tries to reconnect on server failures // keeps track of requests and tries to reconnect on server failures
......
/******************************************************************************\ // This example program represents a minimal terminal chat program based on
* This example program represents a minimal terminal chat program * // group communication.
* based on group communication. * //
* * // Setup for a minimal chat between "alice" and "bob":
* Setup for a minimal chat between "alice" and "bob": * // - ./build/bin/group_chat -s -p 4242
* - ./build/bin/group_chat -s -p 4242 * // - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n alice
* - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n alice * // - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n bob
* - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n bob *
\******************************************************************************/
#include <cstdlib> #include <cstdlib>
#include <iostream> #include <iostream>
......
/******************************************************************************\ // This example program represents a minimal IRC-like group communication
* This example program represents a minimal IRC-like group * // server.
* communication server. * //
* * // Setup for a minimal chat between "alice" and "bob":
* Setup for a minimal chat between "alice" and "bob": * // - ./build/bin/group_server -p 4242
* - ./build/bin/group_server -p 4242 * // - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n alice
* - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n alice * // - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n bob
* - ./build/bin/group_chat -g remote:chatroom@localhost:4242 -n bob *
\ ******************************************************************************/
#include <string> #include <string>
#include <cstdlib> #include <cstdlib>
......
// This program illustrates how to spawn a simple calculator // This program illustrates how to spawn a simple calculator across the network.
// across the network.
// //
// Run server at port 4242: // Run server at port 4242:
// - remote_spawn -s -p 4242 // - remote_spawn -s -p 4242
......
/****************************************************************************** // Basic, non-interactive streaming example for processing integers.
* Basic, non-interactive streaming example for processing integers. *
******************************************************************************/
#include <iostream> #include <iostream>
#include <vector> #include <vector>
......
// Manual refs: lines 12-60 (Testing)
#define CAF_SUITE ping_pong #define CAF_SUITE ping_pong
#include "caf/test/dsl.hpp" #include "caf/test/dsl.hpp"
...@@ -9,6 +7,7 @@ ...@@ -9,6 +7,7 @@
using namespace caf; using namespace caf;
// --(rst-ping-pong-begin)--
namespace { namespace {
behavior ping(event_based_actor* self, actor pong_actor, int n) { behavior ping(event_based_actor* self, actor pong_actor, int n) {
...@@ -58,3 +57,4 @@ CAF_TEST(three pings) { ...@@ -58,3 +57,4 @@ CAF_TEST(three pings) {
} }
CAF_TEST_FIXTURE_SCOPE_END() CAF_TEST_FIXTURE_SCOPE_END()
// --(rst-ping-pong-end)--
...@@ -16,9 +16,6 @@ ...@@ -16,9 +16,6 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
// This file is referenced in the manual, do not modify without updating refs!
// ConfiguringActorApplications: 50-54
#pragma once #pragma once
#include <type_traits> #include <type_traits>
...@@ -45,9 +42,10 @@ struct is_allowed_unsafe_message_type<const T&> ...@@ -45,9 +42,10 @@ struct is_allowed_unsafe_message_type<const T&>
} // namespace caf } // namespace caf
// --(rst-macro-begin)--
#define CAF_ALLOW_UNSAFE_MESSAGE_TYPE(type_name) \ #define CAF_ALLOW_UNSAFE_MESSAGE_TYPE(type_name) \
namespace caf { \ namespace caf { \
template <> \ template <> \
struct allowed_unsafe_message_type<type_name> : std::true_type {}; \ struct allowed_unsafe_message_type<type_name> : std::true_type {}; \
} }
// --(rst-macro-end)--
...@@ -16,10 +16,6 @@ ...@@ -16,10 +16,6 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 29-49 (Error.tex)
#pragma once #pragma once
#include <string> #include <string>
...@@ -28,6 +24,7 @@ ...@@ -28,6 +24,7 @@
namespace caf { namespace caf {
// --(rst-exit-reason-begin)--
/// This error category represents fail conditions for actors. /// This error category represents fail conditions for actors.
enum class exit_reason : uint8_t { enum class exit_reason : uint8_t {
/// Indicates that an actor finished execution without error. /// Indicates that an actor finished execution without error.
...@@ -49,6 +46,7 @@ enum class exit_reason : uint8_t { ...@@ -49,6 +46,7 @@ enum class exit_reason : uint8_t {
/// Indicates that an actor was killed because it became unreachable. /// Indicates that an actor was killed because it became unreachable.
unreachable unreachable
}; };
// --(rst-exit-reason-end)--
/// Returns a string representation of given exit reason. /// Returns a string representation of given exit reason.
std::string to_string(exit_reason); std::string to_string(exit_reason);
......
...@@ -16,10 +16,6 @@ ...@@ -16,10 +16,6 @@
* http://www.boost.org/LICENSE_1_0.txt. * * http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/ ******************************************************************************/
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 32-117 (Error.tex)
#pragma once #pragma once
#include "caf/error.hpp" #include "caf/error.hpp"
...@@ -27,6 +23,7 @@ ...@@ -27,6 +23,7 @@
namespace caf { namespace caf {
// --(rst-sec-begin)--
/// SEC stands for "System Error Code". This enum contains error codes for /// SEC stands for "System Error Code". This enum contains error codes for
/// ::actor_system and its modules. /// ::actor_system and its modules.
enum class sec : uint8_t { enum class sec : uint8_t {
...@@ -140,6 +137,7 @@ enum class sec : uint8_t { ...@@ -140,6 +137,7 @@ enum class sec : uint8_t {
/// Disconnected from a BASP node after reaching the connection timeout. /// Disconnected from a BASP node after reaching the connection timeout.
connection_timeout, connection_timeout,
}; };
// --(rst-sec-end)--
/// @relates sec /// @relates sec
std::string to_string(sec); std::string to_string(sec);
......
This diff is collapsed.
This diff is collapsed.
...@@ -12,55 +12,63 @@ include a message to provide additional context information. ...@@ -12,55 +12,63 @@ include a message to provide additional context information.
Class Interface Class Interface
--------------- ---------------
+-----------------------------------------+--------------------------------------------------------------------+ +------------------------------------------+--------------------------------------------------------------------+
| **Constructors** | | | **Constructors** |
+-----------------------------------------+--------------------------------------------------------------------+ +------------------------------------------+--------------------------------------------------------------------+
| ``(Enum x)`` | Construct error by calling ``make_error(x)`` | | ``(Enum x)`` | Construct error by calling ``make_error(x)`` |
+-----------------------------------------+--------------------------------------------------------------------+ +------------------------------------------+--------------------------------------------------------------------+
| ``(uint8_t x, atom_value y)`` | Construct error with code ``x`` and category ``y`` | | ``(uint8_t x, atom_value y)`` | Construct error with code ``x`` and category ``y`` |
+-----------------------------------------+--------------------------------------------------------------------+ +------------------------------------------+--------------------------------------------------------------------+
| ``(uint8_t x, atom_value y, message z)``| Construct error with code ``x``, category ``y``, and context ``z`` | | ``(uint8_t x, atom_value y, message z)`` | Construct error with code ``x``, category ``y``, and context ``z`` |
+-----------------------------------------+--------------------------------------------------------------------+ +------------------------------------------+--------------------------------------------------------------------+
| | | | **Observers** |
+-----------------------------------------+--------------------------------------------------------------------+ +------------------------------------------+--------------------------------------------------------------------+
| **Observers** | | | ``uint8_t code()`` | Returns the error code |
+-----------------------------------------+--------------------------------------------------------------------+ +------------------------------------------+--------------------------------------------------------------------+
| ``uint8_t code()`` | Returns the error code | | ``atom_value category()`` | Returns the error category |
+-----------------------------------------+--------------------------------------------------------------------+ +------------------------------------------+--------------------------------------------------------------------+
| ``atom_value category()`` | Returns the error category | | ``message context()`` | Returns additional context information |
+-----------------------------------------+--------------------------------------------------------------------+ +------------------------------------------+--------------------------------------------------------------------+
| ``message context()`` | Returns additional context information | | ``explicit operator bool()`` | Returns ``code() != 0`` |
+-----------------------------------------+--------------------------------------------------------------------+ +------------------------------------------+--------------------------------------------------------------------+
| ``explicit operator bool()`` | Returns ``code() != 0`` |
+-----------------------------------------+--------------------------------------------------------------------+
.. _custom-error: .. _custom-error:
Add Custom Error Categories Add Custom Error Categories
--------------------------- ---------------------------
Adding custom error categories requires three steps: (1) declare an enum class Adding custom error categories requires two steps: (1) declare an enum class of
of type ``uint8_t`` with the first value starting at 1, (2) specialize type ``uint8_t`` with the first value starting at 1, (2) enable conversions from
``error_category`` to give your type a custom ID (value 0-99 are the error code enum to ``error`` by using the macro ``CAF_ERROR_CODE_ENUM``. The
reserved by CAF), and (3) add your error category to the actor system config. following example illustrates these two steps with a ``math_error`` enum to
The following example adds custom error codes for math errors. signal divisions by zero:
.. literalinclude:: /examples/message_passing/divider.cpp .. literalinclude:: /examples/message_passing/divider.cpp
:language: C++ :language: C++
:lines: 17-47 :start-after: --(rst-divider-begin)--
:end-before: --(rst-divider-end)--
.. note::
CAF stores the integer value with an atom that disambiguates error codes from
different categories. The macro ``CAF_ERROR_CODE_ENUM`` uses the given type
name as atom as well. This means the same limitations apply. In particular,
10 characters or less. If the type name exceeds this limit, users can pass
two arguments instead. In the example above, we could call
``CAF_ERROR_CODE_ENUM(math_error, "math")`` instead.
.. _sec: .. _sec:
System Error Codes System Error Codes
------------------ ------------------
System Error Codes (SECs) use the error category ``"system"``. They System Error Codes (SECs) represent errors in the actor system or one of its
represent errors in the actor system or one of its modules and are defined as modules and are defined as follows:
follows.
.. literalinclude:: /libcaf_core/caf/sec.hpp .. literalinclude:: /libcaf_core/caf/sec.hpp
:language: C++ :language: C++
:lines: 32-117 :start-after: --(rst-sec-begin)--
:end-before: --(rst-sec-end)--
.. _exit-reason: .. _exit-reason:
...@@ -77,5 +85,5 @@ in the same way. The latter terminates an actor unconditionally when used in ...@@ -77,5 +85,5 @@ in the same way. The latter terminates an actor unconditionally when used in
.. literalinclude:: /libcaf_core/caf/exit_reason.hpp .. literalinclude:: /libcaf_core/caf/exit_reason.hpp
:language: C++ :language: C++
:lines: 29-49 :start-after: --(rst-exit-reason-begin)--
:end-before: --(rst-exit-reason-end)--
...@@ -14,8 +14,8 @@ name, joining, and leaving. ...@@ -14,8 +14,8 @@ name, joining, and leaving.
std::string id = "foo"; std::string id = "foo";
auto expected_grp = system.groups().get(module, id); auto expected_grp = system.groups().get(module, id);
if (! expected_grp) { if (! expected_grp) {
std::cerr << "*** cannot load group: " std::cerr << "*** cannot load group: " << to_string(expected_grp.error())
<< system.render(expected_grp.error()) << std::endl; << std::endl;
return; return;
} }
auto grp = std::move(*expected_grp); auto grp = std::move(*expected_grp);
......
...@@ -45,10 +45,10 @@ Dynamically Typed Actor ...@@ -45,10 +45,10 @@ Dynamically Typed Actor
~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~
A dynamically typed actor accepts any kind of message and dispatches on its A dynamically typed actor accepts any kind of message and dispatches on its
content dynamically at the receiver. This is the "traditional" messaging content dynamically at the receiver. This is the traditional messaging style
style found in implementations like Erlang or Akka. The upside of this approach found in implementations like Erlang or Akka. The upside of this approach is
is (usually) faster prototyping and less code. This comes at the cost of (usually) faster prototyping and less code. This comes at the cost of requiring
requiring excessive testing. excessive testing.
Statically Typed Actor Statically Typed Actor
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
......
...@@ -85,10 +85,9 @@ This policy models split/join or scatter/gather work flows, where a work item ...@@ -85,10 +85,9 @@ This policy models split/join or scatter/gather work flows, where a work item
is split into as many tasks as workers are available and then the individuals is split into as many tasks as workers are available and then the individuals
results are joined together before sending the full result back to the client. results are joined together before sending the full result back to the client.
The join function is responsible for ``glueing'' all result messages together The join function is responsible for ``glueing`` all result messages together to
to create a single result. The function is called with the result object create a single result. The function is called with the result object (initialed
(initialed using ``init``) and the current result messages from a using ``init``) and the current result messages from a worker.
worker.
The first argument of a split function is a mapping from actors (workers) to The first argument of a split function is a mapping from actors (workers) to
tasks (messages). The second argument is the input message. The default split tasks (messages). The second argument is the input message. The default split
......
...@@ -19,7 +19,7 @@ change its behavior when not receiving message after a certain amount of time. ...@@ -19,7 +19,7 @@ change its behavior when not receiving message after a certain amount of time.
message_handler x1{ message_handler x1{
[](int i) { /*...*/ }, [](int i) { /*...*/ },
[](double db) { /*...*/ }, [](double db) { /*...*/ },
[](int a, int b, int c) { /*...*/ } [](int a, int b, int c) { /*...*/ },
}; };
In our first example, ``x1`` models a behavior accepting messages that consist In our first example, ``x1`` models a behavior accepting messages that consist
...@@ -31,7 +31,7 @@ other message is not matched and gets forwarded to the default handler (see ...@@ -31,7 +31,7 @@ other message is not matched and gets forwarded to the default handler (see
message_handler x2{ message_handler x2{
[](double db) { /*...*/ }, [](double db) { /*...*/ },
[](double db) { /* - unreachable - */ } [](double db) { /* - unreachable - */ },
}; };
Our second example illustrates an important characteristic of the matching Our second example illustrates an important characteristic of the matching
...@@ -55,37 +55,54 @@ The handler ``x4`` will consume messages with a single ...@@ -55,37 +55,54 @@ The handler ``x4`` will consume messages with a single
``double`` using the first callback in ``x2``, essentially ``double`` using the first callback in ``x2``, essentially
overriding the second callback in ``x1``. overriding the second callback in ``x1``.
.. code-block:: C++
behavior bhvr{
[](int i) { /*...*/ },
[](double db) { /*...*/ },
[](int a, int b, int c) { /*...*/ },
after(std::chrono::seconds(10)) >> [] { /*..*/ },
};
Similar to ``message_handler``, ``behavior`` stores a set of functions.
Additionally, behaviors accept optional timeouts as *last* argument to the
constructor. In the example above, the behavior calls the timeout handler after
receiving no messages for 10 seconds. This timeout triggers repeatedly, i.e.,
the timeout handler gets called again after 20 seconds when not receiving any
messages, then again after 30 seconds, and so on.
.. _atom: .. _atom:
Atoms Atoms
----- -----
Defining message handlers in terms of callbacks is convenient, but requires a Defining message handlers in terms of callbacks is convenient, but requires a
simple way to annotate messages with meta data. Imagine an actor that provides simple way to annotate messages with meta data. Imagine an actor that provides a
a mathematical service for integers. It receives two integers, performs a mathematical service for integers. It receives two integers, performs a
user-defined operation and returns the result. Without additional context, the user-defined operation and returns the result. Without additional context, the
actor cannot decide whether it should multiply or add the integers. Thus, the actor cannot decide whether it should multiply or add the integers, for example.
operation must be encoded into the message. The Erlang programming language
introduced an approach to use non-numerical constants, so-called Thus, the operation must be encoded into the message. The Erlang programming
*atoms*, which have an unambiguous, special-purpose type and do not have language introduced an approach to use non-numerical constants, so-called
the runtime overhead of string constants. *atoms*, which have an unambiguous, special-purpose type and do not have the
runtime overhead of string constants.
Atoms in CAF are mapped to integer values at compile time. This mapping is Atoms in CAF are mapped to integer values at compile time. This mapping is
guaranteed to be collision-free and invertible, but limits atom literals to ten guaranteed to be collision-free and invertible, but limits atom literals to ten
characters and prohibits special characters. Legal characters are characters and prohibits special characters. Legal characters are ``_0-9A-Za-z``
``_0-9A-Za-z`` and the whitespace character. Atoms are created using and the whitespace character. Atoms are created using the ``constexpr`` function
the ``constexpr`` function ``atom``, as the following example ``atom``, as the following example illustrates.
illustrates.
.. code-block:: C++ .. code-block:: C++
atom_value a1 = atom("add"); atom_value a1 = atom("add");
atom_value a2 = atom("multiply"); atom_value a2 = atom("multiply");
**Warning**: The compiler cannot enforce the restrictions at compile time, .. warning::
except for a length check. The assertion ``atom("!?") != atom("?!")``
is not true, because each invalid character translates to a whitespace The compiler cannot enforce the restrictions at compile time, except for a
character. length check. The assertion ``atom("!?") != atom("?!")`` is not true, because
each invalid character translates to a whitespace character.
While the ``atom_value`` is computed at compile time, it is not While the ``atom_value`` is computed at compile time, it is not
uniquely typed and thus cannot be used in the signature of a callback. To uniquely typed and thus cannot be used in the signature of a callback. To
......
This diff is collapsed.
...@@ -92,12 +92,10 @@ connect to the published actor by calling ``remote_actor``: ...@@ -92,12 +92,10 @@ connect to the published actor by calling ``remote_actor``:
// node B // node B
auto ping = system.middleman().remote_actor("node A", 4242); auto ping = system.middleman().remote_actor("node A", 4242);
if (! ping) { if (!ping)
cerr << "unable to connect to node A: " cerr << "unable to connect to node A: " << to_string(ping.error()) << '\n';
<< system.render(ping.error()) << std::endl; else
} else {
self->send(*ping, ping_atom::value); self->send(*ping, ping_atom::value);
}
There is no difference between server and client after the connection phase. There is no difference between server and client after the connection phase.
Remote actors use the same handle types as local actors and are thus fully Remote actors use the same handle types as local actors and are thus fully
......
...@@ -4,23 +4,32 @@ Overview ...@@ -4,23 +4,32 @@ Overview
Compiling CAF requires CMake and a C++11-compatible compiler. To get and Compiling CAF requires CMake and a C++11-compatible compiler. To get and
compile the sources on UNIX-like systems, type the following in a terminal: compile the sources on UNIX-like systems, type the following in a terminal:
.. :: .. code-block:: none
git clone https://github.com/actor-framework/actor-framework git clone https://github.com/actor-framework/actor-framework
cd actor-framework cd actor-framework
./configure ./configure
make make -C build
make install [as root, optional] make -C build test [optional]
make -C build install [as root, optional]
We recommended to run the unit tests as well: If the output of ``make test`` indicates an error, please submit a bug report
that includes (a) your compiler version, (b) your OS, and (c) the content of the
file ``build/Testing/Temporary/LastTest.log``.
.. :: The configure script provides several build options for advanced configuration.
Please run ``./configure -h`` to see all available options. If you are building
CAF only as a dependency, disabling the unit tests and the examples can safe you
some time during the build.
make test .. note::
If the output indicates an error, please submit a bug report that includes (a) The ``configure`` script provides a convenient way for creating a build
your compiler version, (b) your OS, and (c) the content of the file directory and calling CMake. Users that are familiar with CMake can of course
``build/Testing/Temporary/LastTest.log``. also use CMake directly and avoid using the ``configure`` script entirely.
On Windows, we recomment using the CMake GUI to generate a Visual Studio
project file for CAF.
Features Features
-------- --------
......
...@@ -19,32 +19,28 @@ It is worth mentioning that the registry is not synchronized between connected ...@@ -19,32 +19,28 @@ It is worth mentioning that the registry is not synchronized between connected
actor system. Each actor system has its own, local registry in a distributed actor system. Each actor system has its own, local registry in a distributed
setting. setting.
+-------------------------------------------+-------------------------------------------------+ +---------------------------------------+-------------------------------------------------+
| **Types** | | | **Types** |
+-------------------------------------------+-------------------------------------------------+ +---------------------------------------+-------------------------------------------------+
| ``name_map`` | ``unordered_map<atom_value, strong_actor_ptr>`` | | ``name_map`` | ``unordered_map<atom_value, strong_actor_ptr>`` |
+-------------------------------------------+-------------------------------------------------+ +---------------------------------------+-------------------------------------------------+
| | | | **Observers** |
+-------------------------------------------+-------------------------------------------------+ +---------------------------------------+-------------------------------------------------+
| **Observers** | | | ``strong_actor_ptr get(actor_id)`` | Returns the actor associated to given ID. |
+-------------------------------------------+-------------------------------------------------+ +---------------------------------------+-------------------------------------------------+
| ``strong_actor_ptr get(actor_id)`` | Returns the actor associated to given ID. | | ``strong_actor_ptr get(atom_value)`` | Returns the actor associated to given name. |
+-------------------------------------------+-------------------------------------------------+ +---------------------------------------+-------------------------------------------------+
| ``strong_actor_ptr get(atom_value)`` | Returns the actor associated to given name. | | ``name_map named_actors()`` | Returns all name mappings. |
+-------------------------------------------+-------------------------------------------------+ +---------------------------------------+-------------------------------------------------+
| ``name_map named_actors()`` | Returns all name mappings. | | ``size_t running()`` | Returns the number of currently running actors. |
+-------------------------------------------+-------------------------------------------------+ +---------------------------------------+-------------------------------------------------+
| ``size_t running()`` | Returns the number of currently running actors. | | **Modifiers** |
+-------------------------------------------+-------------------------------------------------+ +---------------------------------------+-------------------------------------------------+
| | | | ``put(actor_id, strong_actor_ptr)`` | Maps an actor to its ID. |
+-------------------------------------------+-------------------------------------------------+ +---------------------------------------+-------------------------------------------------+
| **Modifiers** | | | ``erase(actor_id)`` | Removes an ID mapping from the registry. |
+-------------------------------------------+-------------------------------------------------+ +---------------------------------------+-------------------------------------------------+
| ``void put(actor_id, strong_actor_ptr)`` | Maps an actor to its ID. | | ``put(atom_value, strong_actor_ptr)`` | Maps an actor to a name. |
+-------------------------------------------+-------------------------------------------------+ +---------------------------------------+-------------------------------------------------+
| ``void erase(actor_id)`` | Removes an ID mapping from the registry. | | ``erase(atom_value)`` | Removes a name mapping from the registry. |
+-------------------------------------------+-------------------------------------------------+ +---------------------------------------+-------------------------------------------------+
| ``void put(atom_value, strong_actor_ptr)``| Maps an actor to a name. |
+-------------------------------------------+-------------------------------------------------+
| ``void erase(atom_value)`` | Removes a name mapping from the registry. |
+-------------------------------------------+-------------------------------------------------+
...@@ -10,7 +10,8 @@ named "calculator". ...@@ -10,7 +10,8 @@ named "calculator".
.. literalinclude:: /examples/remoting/remote_spawn.cpp .. literalinclude:: /examples/remoting/remote_spawn.cpp
:language: C++ :language: C++
:lines: 123-137 :start-after: --(rst-client-begin)--
:end-before: --(rst-client-end)--
We first connect to a CAF node with ``middleman().connect(...)``. On success, We first connect to a CAF node with ``middleman().connect(...)``. On success,
``connect`` returns the node ID we need for ``remote_spawn``. This requires the ``connect`` returns the node ID we need for ``remote_spawn``. This requires the
......
...@@ -69,48 +69,50 @@ Defining Sources ...@@ -69,48 +69,50 @@ Defining Sources
.. literalinclude:: /examples/streaming/integer_stream.cpp .. literalinclude:: /examples/streaming/integer_stream.cpp
:language: C++ :language: C++
:lines: 17-48 :start-after: --(rst-source-begin)--
:end-before: --(rst-source-end)--
The simplest way to defining a source is to use the The simplest way to defining a source is to use the ``attach_stream_source``
``attach_stream_source`` function and pass it four arguments: a pointer function and pass it four arguments: a pointer to *self*, *initializer* for the
to *self*, *initializer* for the state, *generator* for state, *generator* for producing values, and *predicate* for signaling the end
producing values, and *predicate* for signaling the end of the stream. of the stream.
Defining Stages Defining Stages
--------------- ---------------
.. literalinclude:: /examples/streaming/integer_stream.cpp .. literalinclude:: /examples/streaming/integer_stream.cpp
:language: C++ :language: C++
:lines: 50-83 :start-after: --(rst-stage-begin)--
:end-before: --(rst-stage-end)--
The function ``make_stage`` also takes three lambdas but additionally The function ``make_stage`` also takes three lambdas but additionally the
the received input stream handshake as first argument. Instead of a predicate, received input stream handshake as first argument. Instead of a predicate,
``make_stage`` only takes a finalizer, since the stage does not produce ``make_stage`` only takes a finalizer, since the stage does not produce data on
data on its own and a stream terminates if no more sources exist. its own and a stream terminates if no more sources exist.
Defining Sinks Defining Sinks
-------------- --------------
.. literalinclude:: /examples/streaming/integer_stream.cpp .. literalinclude:: /examples/streaming/integer_stream.cpp
:language: C++ :language: C++
:lines: 85-114 :start-after: --(rst-sink-begin)--
:end-before: --(rst-sink-end)--
The function ``make_sink`` is similar to ``make_stage``, except The function ``make_sink`` is similar to ``make_stage``, except that is does not
that is does not produce outputs. produce outputs.
Initiating Streams Initiating Streams
------------------ ------------------
.. literalinclude:: /examples/streaming/integer_stream.cpp .. literalinclude:: /examples/streaming/integer_stream.cpp
:language: C++ :language: C++
:lines: 128-132 :start-after: --(rst-main-begin)--
:end-before: --(rst-main-end)--
In our example, we always have a source ``int_source`` and a sink In our example, we always have a source ``int_source`` and a sink ``int_sink``
``int_sink`` with an optional stage ``int_selector``. Sending with an optional stage ``int_selector``. Sending ``open_atom`` to the source
``open_atom`` to the source initiates the stream and the source will initiates the stream and the source will respond with a stream handshake.
respond with a stream handshake.
Using the actor composition in CAF (``snk * src`` reads *sink Using the actor composition in CAF (``snk * src`` reads *sink after source*)
after source*) allows us to redirect the stream handshake we send in allows us to redirect the stream handshake we send in ``caf_main`` to the sink
``caf_main`` to the sink (or to the stage and then from the stage to (or to the stage and then from the stage to the sink).
the sink).
...@@ -99,7 +99,7 @@ as ``CAF_CHECK`` and provide tests rather than implementing a ...@@ -99,7 +99,7 @@ as ``CAF_CHECK`` and provide tests rather than implementing a
}, },
[&](caf::error& err) { [&](caf::error& err) {
// Must not happen, stop test. // Must not happen, stop test.
CAF_FAIL(sys.render(err)); CAF_FAIL(err);
}); });
} }
...@@ -127,18 +127,16 @@ configuration classes. ...@@ -127,18 +127,16 @@ configuration classes.
Using this fixture unlocks three additional macros: Using this fixture unlocks three additional macros:
* ``expect`` checks for a single message. The macro verifies the content types of the message and invokes the necessary member functions on the test coordinator. Optionally, the macro checks the receiver of the message and its content. If the expected message does not exist, the test aborts. - ``expect`` checks for a single message. The macro verifies the content types of the message and invokes the necessary member functions on the test coordinator. Optionally, the macro checks the receiver of the message and its content. If the expected message does not exist, the test aborts.
* ``allow`` is similar to ``expect``, but it does not abort the test if the expected message is missing. This macro returns ``true`` if the allowed message was delivered, ``false`` otherwise. - ``allow`` is similar to ``expect``, but it does not abort the test if the expected message is missing. This macro returns ``true`` if the allowed message was delivered, ``false`` otherwise.
* ``disallow`` aborts the test if a particular message was delivered to an actor. - ``disallow`` aborts the test if a particular message was delivered to an actor.
The following example implements two actors, ``ping`` and The following example implements two actors, ``ping`` and ``pong``, that
``pong``, that exchange a configurable amount of messages. The test exchange a configurable amount of messages. The test *three pings* then checks
*three pings* then checks the contents of each message with the contents of each message with ``expect`` and verifies that no additional
``expect`` and verifies that no additional messages exist using messages exist using ``disallow``.
``disallow``.
.. literalinclude:: /examples/testing/ping_pong.cpp .. literalinclude:: /examples/testing/ping_pong.cpp
:language: C++ :language: C++
:lines: 12-60 :start-after: --(rst-ping-pong-begin)--
:end-before: --(rst-ping-pong-end)--
.. _type-inspection: .. _type-inspection:
Type Inspection (Serialization and String Conversion) Type Inspection
===================================================== ===============
CAF is designed with distributed systems in mind. Hence, all message types must CAF is designed with distributed systems in mind. Hence, all message types must
be serializable and need a platform-neutral, unique name that is configured at be serializable and need a platform-neutral, unique name (see
startup (see :ref:`add-custom-message-type`). Using a message type that is not :ref:`add-custom-message-type`). Using a message type that is not serializable
serializable causes a compiler error (see :ref:`unsafe-message-type`). CAF causes a compiler error (see :ref:`unsafe-message-type`). CAF serializes
serializes individual elements of a message by using the inspection API. This individual elements of a message by using the inspection API. This API allows
API allows users to provide code for serialization as well as string conversion users to provide code for serialization as well as string conversion, usually
with a single free function. The signature for a class ``my_class`` is always as with a single free function. The signature for a class ``my_class`` is always as
follows: follows:
...@@ -21,38 +21,41 @@ follows: ...@@ -21,38 +21,41 @@ follows:
The function ``inspect`` passes meta information and data fields to the The function ``inspect`` passes meta information and data fields to the
variadic call operator of the inspector. The following example illustrates an variadic call operator of the inspector. The following example illustrates an
implementation for ``inspect`` for a simple POD struct. implementation of ``inspect`` for a simple POD called ``foo``.
.. literalinclude:: /examples/custom_type/custom_types_1.cpp .. literalinclude:: /examples/custom_type/custom_types_1.cpp
:language: C++ :language: C++
:lines: 23-33 :start-after: --(rst-foo-begin)--
:end-before: --(rst-foo-end)--
The inspector recursively inspects all data fields and has builtin support for The inspector recursively inspects all data fields and has builtin support for
(1) ``std::tuple``, (2) ``std::pair``, (3) C arrays, (4) any ``std::tuple``, ``std::pair``, arrays, as well as containers that provide the
container type with ``x.size()``, ``x.empty()``, member functions ``size``, ``empty``, ``begin`` and ``end``.
``x.begin()`` and ``x.end()``.
We consciously made the inspect API as generic as possible to allow for We consciously made the inspect API as generic as possible to allow for
extensibility. This allows users to use CAF's types in other contexts, to extensibility. This allows users to use CAF's types in other contexts, to
implement parsers, etc. implement parsers, etc.
.. note::
When converting a user-defined type to a string, CAF calls user-defined
``to_string`` functions and prefers those over ``inspect``.
Inspector Concept Inspector Concept
----------------- -----------------
The following concept class shows the requirements for inspectors. The The following concept class shows the requirements for inspectors. The
placeholder ``T`` represents any user-defined type. For example, placeholder ``T`` represents any user-defined type. Usually ``error`` or
``error`` when performing I/O operations or some integer type when ``error_code``.
implementing a hash function.
.. code-block:: C++ .. code-block:: C++
Inspector { Inspector {
using result_type = T; using result_type = T;
if (inspector only requires read access to the state of T) static constexpr bool reads_state = ...;
static constexpr bool reads_state = true;
else static constexpr bool writes_state = ...;
static constexpr bool writes_state = true;
template <class... Ts> template <class... Ts>
result_type operator()(Ts&&...); result_type operator()(Ts&&...);
...@@ -63,6 +66,13 @@ references. A loading ``Inspector`` must only accept mutable lvalue ...@@ -63,6 +66,13 @@ references. A loading ``Inspector`` must only accept mutable lvalue
references to data fields, but still allow for constant lvalue references and references to data fields, but still allow for constant lvalue references and
rvalue references to annotations. rvalue references to annotations.
Inspectors that only visit data fields (such as a serializer) sets
``reads_state`` to ``true`` and ``writes_state`` to ``false``. Inspectors that
override data fields (such as a deserializer) assign the inverse values.
These compile-time constants can be used in ``if constexpr`` statements or to
select different ``inspect`` overloads with ``enable_if``.
Annotations Annotations
----------- -----------
...@@ -74,27 +84,28 @@ inspector can query whether a type ``T`` is an annotation with ...@@ -74,27 +84,28 @@ inspector can query whether a type ``T`` is an annotation with
call operator of the inspector along with data fields. The following list shows call operator of the inspector along with data fields. The following list shows
all annotations supported by CAF: all annotations supported by CAF:
* ``type_name(n)``: Display type name as ``n`` in human-friendly output (position before data fields). - ``type_name(n)``: Display type name as ``n`` in human-friendly output
* ``hex_formatted()``: Format the following data field in hex format. (position before data fields).
* ``omittable()``: Omit the following data field in human-friendly output. - ``hex_formatted()``: Format the following data field in hex format.
* ``omittable_if_empty()``: Omit the following data field if it is empty in human-friendly output. - ``omittable()``: Omit the following data field in human-friendly output.
* ``omittable_if_none()``: Omit the following data field if it equals ``none`` in human-friendly output. - ``omittable_if_empty()``: Omit the following data field in human-friendly
* ``save_callback(f)``: Call ``f`` when serializing (position after data fields). output if it is empty.
* ``load_callback(f)``: Call ``f`` after deserializing all data fields (position after data fields). - ``omittable_if_none()``: Omit the following data field in human-friendly
output if it equals ``none``.
- ``save_callback(f)``: Call ``f`` if ``reads_state == true``. Pass this
callback after the data fields.
- ``load_callback(f)``: Call ``f`` ``writes_state == true``. Pass this callback
after the data fields.
Backwards and Third-party Compatibility Backwards and Third-party Compatibility
--------------------------------------- ---------------------------------------
CAF evaluates common free function other than ``inspect`` in order to CAF evaluates common free function other than ``inspect`` in order to simplify
simplify users to integrate CAF into existing code bases. users to integrate CAF into existing code bases.
Serializers and deserializers call user-defined ``serialize`` Serializers and deserializers call user-defined ``serialize`` functions. Both
functions. Both types support ``operator&`` as well as types support ``operator&`` as well as ``operator()`` for individual data
``operator()`` for individual data fields. A ``serialize`` fields. A ``serialize`` function has priority over ``inspect``.
function has priority over ``inspect``.
When converting a user-defined type to a string, CAF calls user-defined
``to_string`` functions and prefers those over ``inspect``.
.. _unsafe-message-type: .. _unsafe-message-type:
...@@ -102,32 +113,57 @@ Whitelisting Unsafe Message Types ...@@ -102,32 +113,57 @@ Whitelisting Unsafe Message Types
--------------------------------- ---------------------------------
Message types that are not serializable cause compile time errors when used in Message types that are not serializable cause compile time errors when used in
actor communication. When using CAF for concurrency only, this errors can be actor communication. For messages that never cross the network, this errors can
suppressed by whitelisting types with be suppressed by whitelisting types with ``CAF_ALLOW_UNSAFE_MESSAGE_TYPE``. The
``CAF_ALLOW_UNSAFE_MESSAGE_TYPE``. The macro is defined as follows. macro is defined as follows.
.. literalinclude:: /libcaf_core/caf/allowed_unsafe_message_type.hpp
:language: C++
:start-after: --(rst-macro-begin)--
:end-before: --(rst-macro-end)--
Keep in mind that ``unsafe`` means that your program runs into undefined
behavior (or segfaults) when you break your promise and try to serialize
messages that contain unsafe message types.
Splitting Save and Load Operations Saving and Loading with Getters and Setters
---------------------------------- -------------------------------------------
If loading and storing cannot be implemented in a single function, users can Many classes shield their member variables with getter and setter functions.
query whether the inspector is loading or storing. For example, consider the This can be addressed by declaring the ``inspect`` function as ``friend``, but
following class ``foo`` with getter and setter functions and no public only when not dealing with 3rd party library types. For example, consider the
access to its members. following class ``foo`` with getter and setter functions and no public access to
its members.
.. literalinclude:: /examples/custom_type/custom_types_3.cpp .. literalinclude:: /examples/custom_type/custom_types_3.cpp
:language: C++ :language: C++
:lines: 20-49 :start-after: --(rst-foo-begin)--
:end-before: --(rst-foo-end)--
Since there is no access to the data fields ``a_`` and ``b_`` Since there is no access to the data fields ``a_`` and ``b_`` (and assuming no
(and assuming no changes to ``foo`` are possible), we need to split our changes to ``foo`` are possible), we can serialize or deserialize from/to local
implementation of ``inspect`` as shown below. variables and use a load callback to write back to the object when loading
state.
.. literalinclude:: /examples/custom_type/custom_types_3.cpp .. literalinclude:: /examples/custom_type/custom_types_3.cpp
:language: C++ :language: C++
:lines: 76-103 :start-after: --(rst-inspect-begin)--
:end-before: --(rst-inspect-end)--
The purpose of the scope guard in the example above is to write the content of For more complicated cases, we can also split the inspect overload as follows:
the temporaries back to ``foo`` at scope exit automatically. Storing
the result of ``f(...)`` in a temporary first and then writing the .. code-block:: C++
changes to ``foo`` is not possible, because ``f(...)`` can
return ``void``. template <class Inspector>
typename std::enable_if<Inspector::reads_state,
typename Inspector::result_type>::type
inspect(Inspector& f, my_class& x) {
// ... serializing ...
}
template <class Inspector>
typename std::enable_if<Inspector::writes_state,
typename Inspector::result_type>::type
inspect(Inspector& f, my_class& x) {
// ... deserializing ...
}
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