Commit 23182f0d authored by Dominik Charousset's avatar Dominik Charousset

Update documentation and examples

parent c6815ed0
......@@ -45,7 +45,7 @@ add_core_example(custom_type custom_types_3)
# testing DSL
add_example(testing ping_pong)
target_link_libraries(ping_pong PRIVATE CAF::internal CAF::core CAF::test)
add_test(NAME "examples.ping-pong" COMMAND ping_pong -r300 -n -v5)
# -- examples for CAF::io ------------------------------------------------------
......
#include <random>
#include <chrono>
#include <cstdlib>
#include <iostream>
#include <random>
#include "caf/all.hpp"
#include "caf/actor_ostream.hpp"
#include "caf/actor_system.hpp"
#include "caf/caf_main.hpp"
#include "caf/event_based_actor.hpp"
using namespace caf;
using std::endl;
void caf_main(actor_system& system) {
for (int i = 1; i <= 50; ++i) {
system.spawn([i](blocking_actor* self) {
aout(self) << "Hi there! This is actor nr. "
<< i << "!" << endl;
behavior printer(event_based_actor* self, int32_t num, int32_t delay) {
aout(self) << "Hi there! This is actor nr. " << num << "!" << std::endl;
std::chrono::milliseconds timeout{delay};
self->delayed_send(self, timeout, timeout_atom_v);
return {
[=](timeout_atom) {
aout(self) << "Actor nr. " << num << " says goodbye after waiting for "
<< delay << "ms!" << std::endl;
},
};
}
void caf_main(actor_system& sys) {
std::random_device rd;
std::default_random_engine re(rd());
std::chrono::milliseconds tout{re() % 10};
self->delayed_send(self, tout, 42);
self->receive(
[i, self](int) {
aout(self) << "Actor nr. "
<< i << " says goodbye!" << endl;
}
);
});
}
std::minstd_rand re(rd());
std::uniform_int_distribution<int32_t> dis{1, 99};
for (int32_t i = 1; i <= 50; ++i)
sys.spawn(printer, i, dis(re));
}
CAF_MAIN()
......@@ -7,8 +7,6 @@
* - simple_broker -c localhost 4242 *
\******************************************************************************/
// Manual refs: 42-47 (Actors.tex)
#include "caf/config.hpp"
#ifdef CAF_WINDOWS
......@@ -38,12 +36,14 @@ using namespace caf::io;
namespace {
// --(rst-attach-begin)--
// Utility function to print an exit message with custom name.
void print_on_exit(const actor& hdl, const std::string& name) {
hdl->attach_functor([=](const error& reason) {
cout << name << " exited: " << to_string(reason) << endl;
});
}
// --(rst-attach-end)--
enum class op : uint8_t {
ping,
......
// 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 <iostream>
#include <string>
......
#include <string>
#include <iostream>
#include "caf/all.hpp"
using std::endl;
using std::string;
#include "caf/actor_ostream.hpp"
#include "caf/actor_system.hpp"
#include "caf/caf_main.hpp"
#include "caf/event_based_actor.hpp"
using namespace caf;
......@@ -13,32 +13,32 @@ behavior mirror(event_based_actor* self) {
return {
// a handler for messages containing a single string
// that replies with a string
[=](const string& what) -> string {
[=](const std::string& what) -> std::string {
// prints "Hello World!" via aout (thread-safe cout wrapper)
aout(self) << what << endl;
aout(self) << what << std::endl;
// reply "!dlroW olleH"
return string(what.rbegin(), what.rend());
}
return std::string{what.rbegin(), what.rend()};
},
};
}
void hello_world(event_based_actor* self, const actor& buddy) {
// send "Hello World!" to our buddy ...
self->request(buddy, std::chrono::seconds(10), "Hello World!").then(
self->request(buddy, std::chrono::seconds(10), "Hello World!")
.then(
// ... wait up to 10s for a response ...
[=](const string& what) {
[=](const std::string& what) {
// ... and print it
aout(self) << what << endl;
}
);
aout(self) << what << std::endl;
});
}
void caf_main(actor_system& system) {
void caf_main(actor_system& sys) {
// create a new actor that calls 'mirror()'
auto mirror_actor = system.spawn(mirror);
auto mirror_actor = sys.spawn(mirror);
// create another actor that calls 'hello_world(mirror_actor)';
system.spawn(hello_world, mirror_actor);
// system will wait until both actors are destroyed before leaving main
sys.spawn(hello_world, mirror_actor);
// the system will wait until both actors are done before exiting the program
}
// creates a main function for us that calls our caf_main
......
......@@ -3,8 +3,6 @@
* 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 "caf/all.hpp"
......@@ -14,18 +12,22 @@ using namespace caf;
namespace {
// --(rst-calculator-actor-begin)--
using calculator_actor
= typed_actor<result<int32_t>(add_atom, int32_t, int32_t),
result<int32_t>(sub_atom, int32_t, int32_t)>;
// --(rst-calculator-actor-end)--
// prototypes and forward declarations
// --(rst-prototypes-begin)--
behavior calculator_fun(event_based_actor* self);
void blocking_calculator_fun(blocking_actor* self);
calculator_actor::behavior_type typed_calculator_fun();
class calculator;
class blocking_calculator;
class typed_calculator;
// --(rst-prototypes-end)--
// --(rst-function-based-begin)--
// function-based, dynamically typed, event-based API
behavior calculator_fun(event_based_actor*) {
return {
......@@ -55,7 +57,9 @@ calculator_actor::behavior_type typed_calculator_fun() {
[](sub_atom, int32_t a, int32_t b) { return a - b; },
};
}
// --(rst-function-based-end)--
// --(rst-class-based-begin)--
// class-based, dynamically typed, event-based API
class calculator : public event_based_actor {
public:
......@@ -91,6 +95,7 @@ public:
return typed_calculator_fun();
}
};
// --(rst-class-based-end)--
void tester(scoped_actor&) {
// end of recursion
......@@ -120,14 +125,16 @@ void tester(scoped_actor& self, const Handle& hdl, int32_t x, int32_t y,
tester(self, std::forward<Ts>(xs)...);
}
void caf_main(actor_system& system) {
auto a1 = system.spawn(blocking_calculator_fun);
auto a2 = system.spawn(calculator_fun);
auto a3 = system.spawn(typed_calculator_fun);
auto a4 = system.spawn<blocking_calculator>();
auto a5 = system.spawn<calculator>();
auto a6 = system.spawn<typed_calculator>();
scoped_actor self{system};
void caf_main(actor_system& sys) {
// --(rst-spawn-begin)--
auto a1 = sys.spawn(blocking_calculator_fun);
auto a2 = sys.spawn(calculator_fun);
auto a3 = sys.spawn(typed_calculator_fun);
auto a4 = sys.spawn<blocking_calculator>();
auto a5 = sys.spawn<calculator>();
auto a6 = sys.spawn<typed_calculator>();
// --(rst-spawn-end)--
scoped_actor self{sys};
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(a4, exit_reason::user_shutdown);
......
......@@ -40,7 +40,7 @@ behavior unchecked_cell(stateful_actor<cell_state>* self) {
// --(rst-cell-end)--
void caf_main(actor_system& system) {
// --(rst-spawn-cell-end)--
// --(rst-spawn-cell-begin)--
// Create one cell for each implementation.
auto cell1 = system.spawn(type_checked_cell);
auto cell2 = system.spawn(unchecked_cell);
......@@ -50,7 +50,7 @@ void caf_main(actor_system& system) {
f(put_atom_v, 20);
cout << "cell value (after setting to 20): " << f(get_atom_v) << endl;
// Get an unchecked cell and send it some garbage. Triggers an "unexpected
// message" error.
// message" error (and terminates cell2!).
anon_send(cell2, "hello there!");
}
......
......@@ -55,11 +55,14 @@ void draw_kirby(const animation_step& animation) {
cout.flush();
}
// --(rst-delayed-send-begin)--
// uses a message-based loop to iterate over all animation steps
void dancing_kirby(event_based_actor* self) {
behavior dancing_kirby(event_based_actor* self) {
using namespace std::literals::chrono_literals;
// let's get it started
self->send(self, update_atom_v, size_t{0});
self->become([=](update_atom, size_t step) {
return {
[=](update_atom, size_t step) {
if (step == sizeof(animation_step)) {
// we've printed all animation steps (done)
cout << endl;
......@@ -68,11 +71,12 @@ void dancing_kirby(event_based_actor* self) {
}
// print given step
draw_kirby(animation_steps[step]);
// animate next step in 150ms
self->delayed_send(self, std::chrono::milliseconds(150), update_atom_v,
step + 1);
});
// schedule next animation step
self->delayed_send(self, 150ms, update_atom_v, step + 1);
},
};
}
// --(rst-delayed-send-end)--
void caf_main(actor_system& system) {
system.spawn(dancing_kirby);
......
#include <iostream>
#include "caf/all.hpp"
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 15-36 (MessagePassing.tex)
using namespace caf;
// using add_atom = atom_constant<atom("add")>; (defined in atom.hpp)
using calc = typed_actor<result<int32_t>(add_atom, int32_t, int32_t)>;
// --(rst-delegate-begin)--
using adder_actor = typed_actor<result<int32_t>(add_atom, int32_t, int32_t)>;
void actor_a(event_based_actor* self, const calc& worker) {
self->request(worker, std::chrono::seconds(10), add_atom_v, 1, 2)
.then([=](int32_t result) { //
aout(self) << "1 + 2 = " << result << std::endl;
});
adder_actor::behavior_type worker_impl() {
return {
[](add_atom, int32_t x, int32_t y) { return x + y; },
};
}
calc::behavior_type actor_b(calc::pointer self, const calc& worker) {
adder_actor::behavior_type server_impl(adder_actor::pointer self,
adder_actor worker) {
return {
[=](add_atom add, int32_t x, int32_t y) {
return self->delegate(worker, add, x, y);
......@@ -27,14 +19,19 @@ calc::behavior_type actor_b(calc::pointer self, const calc& worker) {
};
}
calc::behavior_type actor_c() {
return {
[](add_atom, int32_t x, int32_t y) { return x + y; },
};
void client_impl(event_based_actor* self, adder_actor adder, int32_t x,
int32_t y) {
using namespace std::literals::chrono_literals;
self->request(adder, 10s, add_atom_v, x, y).then([=](int32_t result) {
aout(self) << x << " + " << y << " = " << result << std::endl;
});
}
void caf_main(actor_system& system) {
system.spawn(actor_a, system.spawn(actor_b, system.spawn(actor_c)));
void caf_main(actor_system& sys) {
auto worker = sys.spawn(worker_impl);
auto server = sys.spawn(server_impl, sys.spawn(worker_impl));
sys.spawn(client_impl, server, 1, 2);
}
// --(rst-delegate-end)--
CAF_MAIN()
......@@ -35,6 +35,7 @@ CAF_END_TYPE_ID_BLOCK(divider)
CAF_ERROR_CODE_ENUM(math_error)
// --(rst-divider-begin)--
using divider = typed_actor<result<double>(div_atom, double, double)>;
divider::behavior_type divider_impl() {
......@@ -46,6 +47,7 @@ divider::behavior_type divider_impl() {
},
};
}
// --(rst-divider-end)--
void caf_main(actor_system& system) {
double x;
......@@ -54,6 +56,7 @@ void caf_main(actor_system& system) {
std::cin >> x;
cout << "y: " << flush;
std::cin >> y;
// --(rst-request-begin)--
auto div = system.spawn(divider_impl);
scoped_actor self{system};
self->request(div, std::chrono::seconds(10), div_atom_v, x, y)
......@@ -63,6 +66,7 @@ void caf_main(actor_system& system) {
aout(self) << "*** cannot compute " << x << " / " << y << " => "
<< to_string(err) << endl;
});
// --(rst-request-end)--
}
CAF_MAIN(id_block::divider)
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 86-98 (MessagePassing.tex)
#include <cassert>
#include <chrono>
#include <iomanip>
......@@ -101,6 +97,7 @@ matrix::behavior_type matrix_actor(matrix::stateful_pointer<matrix_state> self,
[=](error& err) mutable { rp.deliver(std::move(err)); });
return rp;
},
// --(rst-fan-out-begin)--
[=](get_atom get, average_atom, column_atom, int column) {
assert(column < columns);
std::vector<cell> columns;
......@@ -118,6 +115,7 @@ matrix::behavior_type matrix_actor(matrix::stateful_pointer<matrix_state> self,
[=](error& err) mutable { rp.deliver(std::move(err)); });
return rp;
},
// --(rst-fan-out-end)--
};
}
......
/******************************************************************************\
* 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 "caf/all.hpp"
using std::cout;
using std::endl;
using namespace caf;
using adder = typed_actor<result<int32_t>(add_atom, int32_t, int32_t)>;
// --(rst-promise-begin)--
using adder_actor = typed_actor<result<int32_t>(add_atom, int32_t, int32_t)>;
// function-based, statically typed, event-based API
adder::behavior_type worker() {
adder_actor::behavior_type worker_impl() {
return {
[](add_atom, int32_t a, int32_t b) { return a + b; },
[](add_atom, int32_t x, int32_t y) { return x + y; },
};
}
// function-based, statically typed, event-based API
adder::behavior_type calculator_master(adder::pointer self) {
auto w = self->spawn(worker);
adder_actor::behavior_type server_impl(adder_actor::pointer self,
adder_actor worker) {
return {
[=](add_atom x, int32_t y, int32_t z) -> result<int32_t> {
[=](add_atom, int32_t y, int32_t z) {
auto rp = self->make_response_promise<int32_t>();
self->request(w, infinite, x, y, z).then([=](int32_t result) mutable {
rp.deliver(result);
});
self->request(worker, infinite, add_atom_v, y, z)
.then([rp](int32_t result) mutable { rp.deliver(result); });
return rp;
},
};
}
void caf_main(actor_system& system) {
auto f = make_function_view(system.spawn(calculator_master));
cout << "12 + 13 = " << f(add_atom_v, 12, 13) << endl;
void client_impl(event_based_actor* self, adder_actor adder, int32_t x,
int32_t y) {
using namespace std::literals::chrono_literals;
self->request(adder, 10s, add_atom_v, x, y).then([=](int32_t result) {
aout(self) << x << " + " << y << " = " << result << std::endl;
});
}
void caf_main(actor_system& sys) {
auto worker = sys.spawn(worker_impl);
auto server = sys.spawn(server_impl, sys.spawn(worker_impl));
sys.spawn(client_impl, server, 1, 2);
}
// --(rst-promise-end)--
CAF_MAIN()
......@@ -2,10 +2,6 @@
* 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 <cstdint>
#include <iostream>
......@@ -18,22 +14,38 @@ using std::vector;
using std::chrono::seconds;
using namespace caf;
// --(rst-cell-begin)--
using cell
= typed_actor<result<void>(put_atom, int32_t), result<int32_t>(get_atom)>;
= typed_actor<result<void>(put_atom, int32_t), // 'put' writes to the cell
result<int32_t>(get_atom)>; // 'get 'reads from the cell
struct cell_state {
int32_t value = 0;
};
static constexpr inline const char* name = "cell";
cell::pointer self;
int32_t value;
cell_state(cell::pointer ptr, int32_t val) : self(ptr), value(val) {
// nop
}
cell::behavior_type cell_impl(cell::stateful_pointer<cell_state> self,
int32_t x0) {
self->state.value = x0;
cell_state(const cell_state&) = delete;
cell_state& operator=(const cell_state&) = delete;
cell::behavior_type make_behavior() {
return {
[=](put_atom, int32_t val) { self->state.value = val; },
[=](get_atom) { return self->state.value; },
[=](put_atom, int32_t val) { value = val; },
[=](get_atom) { return value; },
};
}
}
};
using cell_impl = cell::stateful_impl<cell_state>;
// --(rst-cell-end)--
// --(rst-testees-begin)--
void waiting_testee(event_based_actor* self, vector<cell> cells) {
for (auto& x : cells)
self->request(x, seconds(1), get_atom_v).await([=](int32_t y) {
......@@ -59,11 +71,13 @@ void blocking_testee(blocking_actor* self, vector<cell> cells) {
aout(self) << "cell #" << x.id() << " -> " << to_string(err) << endl;
});
}
// --(rst-testees-end)--
// --(rst-main-begin)--
void caf_main(actor_system& system) {
vector<cell> cells;
for (auto i = 0; i < 5; ++i)
cells.emplace_back(system.spawn(cell_impl, i * i));
for (int32_t i = 0; i < 5; ++i)
cells.emplace_back(system.spawn<cell_impl>(i * i));
scoped_actor self{system};
aout(self) << "waiting_testee" << endl;
auto x1 = self->spawn(waiting_testee, cells);
......@@ -74,5 +88,6 @@ void caf_main(actor_system& system) {
aout(self) << "blocking_testee" << endl;
system.spawn(blocking_testee, cells);
}
// --(rst-main-end)--
CAF_MAIN()
// Manual refs: lines 12-60 (Testing)
#define CAF_SUITE ping_pong
#include "caf/test/dsl.hpp"
......@@ -9,6 +7,7 @@
using namespace caf;
// --(rst-ping-pong-begin)--
namespace {
behavior ping(event_based_actor* self, actor pong_actor, int n) {
......@@ -58,3 +57,4 @@ CAF_TEST(three pings) {
}
CAF_TEST_FIXTURE_SCOPE_END()
// --(rst-ping-pong-end)--
......@@ -32,6 +32,7 @@
#include "caf/blocking_actor.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/byte_span.hpp"
#include "caf/caf_main.hpp"
#include "caf/config_option.hpp"
#include "caf/config_option_adder.hpp"
#include "caf/config_value.hpp"
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
// For historic reasons, CAF_MAIN is implemented in exec_main.hpp. Eventually
// the implementation of the macro should move here.
#include "caf/exec_main.hpp"
......@@ -19,6 +19,7 @@
namespace caf {
// --(rst-exit-reason-begin)--
/// This error category represents fail conditions for actors.
enum class exit_reason : uint8_t {
/// Indicates that an actor finished execution without error.
......@@ -40,6 +41,7 @@ enum class exit_reason : uint8_t {
/// Indicates that an actor was killed because it became unreachable.
unreachable
};
// --(rst-exit-reason-end)--
/// @relates exit_reason
CAF_CORE_EXPORT std::string to_string(exit_reason);
......
......@@ -19,6 +19,7 @@
namespace caf {
// --(rst-sec-begin)--
/// SEC stands for "System Error Code". This enum contains error codes for
/// ::actor_system and its modules.
enum class sec : uint8_t {
......@@ -159,6 +160,7 @@ enum class sec : uint8_t {
/// A key lookup failed.
no_such_key = 65,
};
// --(rst-sec-end)--
/// @relates sec
CAF_CORE_EXPORT std::string to_string(sec);
......
......@@ -54,6 +54,9 @@ public:
// tell actor_cast which semantic this type uses
static constexpr bool has_weak_ptr_semantics = false;
/// Stores the template parameter pack.
using signatures = detail::type_list<Sigs...>;
/// Creates a new `typed_actor` type by extending this one with `Es...`.
template <class... Es>
using extend = typed_actor<Sigs..., Es...>;
......@@ -68,15 +71,31 @@ public:
/// for their behavior stack.
using behavior_type = typed_behavior<Sigs...>;
/// The default, event-based type for implementing this messaging interface.
using impl = typed_event_based_actor<Sigs...>;
/// Identifies pointers to instances of this kind of actor.
using pointer = typed_event_based_actor<Sigs...>*;
using pointer = impl*;
/// Allows a view to an actor implementing this messaging interface without
/// knowledge of the actual type..
/// A view to an actor that implements this messaging interface without
/// knowledge of the actual type.
using pointer_view = typed_actor_pointer<Sigs...>;
/// Identifies the base class for this kind of actor.
using base = typed_event_based_actor<Sigs...>;
/// A class type suitable as base type class-based implementations.
using base = impl;
/// The default, event-based type for implementing this messaging interface as
/// a stateful actor.
template <class State>
using stateful_impl = stateful_actor<State, impl>;
template <class State>
using stateful_base [[deprecated("use stateful_impl instead")]]
= stateful_actor<State, base>;
/// Convenience alias for `stateful_impl<State>*`.
template <class State>
using stateful_pointer = stateful_impl<State>*;
/// Identifies pointers to brokers implementing this interface.
using broker_pointer = io::typed_broker<Sigs...>*;
......@@ -84,17 +103,6 @@ public:
/// Identifies the base class of brokers implementing this interface.
using broker_base = io::typed_broker<Sigs...>;
/// Stores the template parameter pack.
using signatures = detail::type_list<Sigs...>;
/// Identifies the base class for this kind of actor with actor.
template <class State>
using stateful_base = stateful_actor<State, base>;
/// Identifies the base class for this kind of actor with actor.
template <class State>
using stateful_pointer = stateful_actor<State, base>*;
/// Identifies the broker_base class for this kind of actor with actor.
template <class State>
using stateful_broker_base = stateful_actor<State, broker_base>;
......
......@@ -250,14 +250,15 @@ messaging interface for a simple calculator.
.. literalinclude:: /examples/message_passing/calculator.cpp
:language: C++
:lines: 17-18
:start-after: --(rst-calculator-actor-begin)--
:end-before: --(rst-calculator-actor-end)--
It is not required to create a type alias such as ``calculator_actor``,
but it makes dealing with statically typed actors much easier. Also, a central
alias definition eases refactoring later on.
Interfaces have set semantics. This means the following two type aliases
``i1`` and ``i2`` are equal:
``i1`` and ``i2`` are considered equal by CAF:
.. code-block:: C++
......@@ -300,13 +301,15 @@ parameter. For example, the following functions and classes represent actors.
.. literalinclude:: /examples/message_passing/calculator.cpp
:language: C++
:lines: 21-26
:start-after: --(rst-prototypes-begin)--
:end-before: --(rst-prototypes-end)--
Spawning an actor for each implementation is illustrated below.
.. literalinclude:: /examples/message_passing/calculator.cpp
:language: C++
:lines: 123-128
:start-after: --(rst-spawn-begin)--
:end-before: --(rst-spawn-end)--
Additional arguments to ``spawn`` are passed to the constructor of a
class or used as additional function arguments, respectively. In the example
......@@ -344,7 +347,8 @@ dynamically typed).
.. literalinclude:: /examples/message_passing/calculator.cpp
:language: C++
:lines: 28-56
:start-after: --(rst-function-based-begin)--
:end-before: --(rst-function-based-end)--
.. _class-based:
......@@ -365,6 +369,14 @@ simple management of state via member variables. However, composing states via
inheritance can get quite tedious. For dynamically typed actors, composing
states is particularly hard, because the compiler cannot provide much help.
The following three classes implement the prototypes shown in spawn_ by
delegating to the function-based implementations we have seen before:
.. literalinclude:: /examples/message_passing/calculator.cpp
:language: C++
:start-after: --(rst-class-based-begin)--
:end-before: --(rst-class-based-end)--
.. _stateful-actor:
Stateful Actors
......@@ -405,7 +417,8 @@ printing a custom string on exit.
.. literalinclude:: /examples/broker/simple_broker.cpp
:language: C++
:lines: 42-47
:start-after: --(rst-attach-begin)--
:end-before: --(rst-attach-end)--
It is possible to attach code to remote actors. However, the cleanup code will
run on the local machine.
......@@ -461,7 +474,7 @@ before the optional timeout, as shown in the example below.
[&](const exit_msg& x) {
// ...
},
others >> [](message_view& x) -> result<message> {
others >> [](message& x) -> skippable_result {
// report unexpected message back to client
return sec::unexpected_message;
}
......
......@@ -5,9 +5,7 @@ Errors
Errors in CAF have a code and a category, similar to ``std::error_code`` and
``std::error_condition``. Unlike its counterparts from the C++ standard library,
``error`` is plattform-neutral and serializable. Instead of using category
singletons, CAF stores categories as atoms (see :ref:`atom`). Errors can also
include a message to provide additional context information.
``error`` is plattform-neutral and serializable.
Class Interface
---------------
......@@ -15,19 +13,17 @@ Class Interface
+-----------------------------------------+--------------------------------------------------------------------+
| **Constructors** | |
+-----------------------------------------+--------------------------------------------------------------------+
| ``(Enum x)`` | Construct error by calling ``make_error(x)`` |
| ``(Enum code)`` | Constructs an error with given error code. |
+-----------------------------------------+--------------------------------------------------------------------+
| ``(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`` |
| ``(Enum code, messag context)`` | Constructs an error with given error code and additional context. |
+-----------------------------------------+--------------------------------------------------------------------+
| | |
+-----------------------------------------+--------------------------------------------------------------------+
| **Observers** | |
+-----------------------------------------+--------------------------------------------------------------------+
| ``uint8_t code()`` | Returns the error code |
| ``uint8_t code()`` | Returns the error code as 8-bit integer. |
+-----------------------------------------+--------------------------------------------------------------------+
| ``atom_value category()`` | Returns the error category |
| ``type_id_t category()`` | Returns the type ID of the Enum type used to construct this error. |
+-----------------------------------------+--------------------------------------------------------------------+
| ``message context()`` | Returns additional context information |
+-----------------------------------------+--------------------------------------------------------------------+
......@@ -39,11 +35,19 @@ Class Interface
Add Custom Error Categories
---------------------------
Adding custom error categories requires three steps: (1) declare an enum class
of type ``uint8_t`` with the first value starting at 1, (2) specialize
``error_category`` to give your type a custom ID (value 0-99 are
reserved by CAF), and (3) add your error category to the actor system config.
The following example adds custom error codes for math errors.
Adding custom error categories requires these steps:
* Declare an enum class of type ``uint8_t`` with error codes starting at 1. CAF
always interprets the value 0 as *no error*.
* Assign a type ID to your enum type.
* Specialize ``caf::is_error_code_enum`` for your enum type. For this step, CAF
offers the macro ``CAF_ERROR_CODE_ENUM`` to generate the boilerplate code
necessary.
The following example illustrates all these steps for a custom error code enum
called ``math_error``.
.. literalinclude:: /examples/message_passing/divider.cpp
:language: C++
......@@ -51,31 +55,31 @@ The following example adds custom error codes for math errors.
.. _sec:
System Error Codes
------------------
Default Error Codes
-------------------
System Error Codes (SECs) use the error category ``"system"``. They
represent errors in the actor system or one of its modules and are defined as
follows.
The enum type ``sec`` (for System Error Code) provides many error codes for
common failures in actor systems:
.. literalinclude:: /libcaf_core/caf/sec.hpp
:language: C++
:lines: 32-117
:start-after: --(rst-sec-begin)--
:end-before: --(rst-sec-end)--
.. _exit-reason:
Default Exit Reasons
--------------------
CAF uses the error category ``"exit"`` for default exit reasons. These errors
are usually fail states set by the actor system itself. The two exceptions are
A special kind of error codes are exit reasons of actors. These errors are
usually fail states set by the actor system itself. The two exceptions are
``exit_reason::user_shutdown`` and ``exit_reason::kill``. The former is used in
CAF to signalize orderly, user-requested shutdown and can be used by programmers
in the same way. The latter terminates an actor unconditionally when used in
``send_exit``, even if the default handler for exit messages (see
:ref:`exit-message`) is overridden.
``send_exit``, even for actors that override the default handler (see
:ref:`exit-message`).
.. literalinclude:: /libcaf_core/caf/exit_reason.hpp
:language: C++
:lines: 29-49
:start-after: --(rst-exit-reason-begin)--
:end-before: --(rst-exit-reason-end)--
......@@ -36,19 +36,5 @@ generation, CAF also offers ``message_builder``:
What Debugging Tools Exist?
---------------------------
The ``scripts/`` and ``tools/`` directories contain some useful tools to aid in
development and debugging.
``scripts/atom.py`` converts integer atom values back into strings.
``scripts/demystify.py`` replaces cryptic ``typed_mpi<...>``
templates with ``replies_to<...>::with<...>`` and
``atom_constant<...>`` with a human-readable representation of the
actual atom.
``scripts/caf-prof`` is an R script that generates plots from CAF
profiler output.
``caf-vec`` is a (highly) experimental tool that annotates CAF logs
with vector timestamps. It gives you happens-before relations and a nice
visualization via `ShiViz <https://bestchai.bitbucket.io/shiviz/>`_.
The ``scripts/`` directory contains some useful tools to aid in analyzing CAF
log output.
......@@ -10,10 +10,8 @@ name, joining, and leaving.
.. code-block:: C++
std::string module = "local";
std::string id = "foo";
auto expected_grp = system.groups().get(module, id);
if (! expected_grp) {
auto expected_grp = system.groups().get("local", "foo");
if (!expected_grp) {
std::cerr << "*** cannot load group: " << to_string(expected_grp.error())
<< std::endl;
return;
......
......@@ -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
results are joined together before sending the full result back to the client.
The join function is responsible for ``glueing'' all result messages together
to create a single result. The function is called with the result object
(initialed using ``init``) and the current result messages from a
worker.
The join function is responsible for "glueing" all result messages together to
create a single result. The function is called with the result object (initialed
using ``init``) and the current result messages from a worker.
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
......
......@@ -17,9 +17,9 @@ change its behavior when not receiving message after a certain amount of time.
.. code-block:: C++
message_handler x1{
[](int i) { /*...*/ },
[](int32_t i) { /*...*/ },
[](double db) { /*...*/ },
[](int a, int b, int c) { /*...*/ }
[](int32_t a, int32_t b, int32_t c) { /*...*/ }
};
In our first example, ``x1`` models a behavior accepting messages that consist
......@@ -70,48 +70,33 @@ introduced an approach to use non-numerical constants, so-called
*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
guaranteed to be collision-free and invertible, but limits atom literals to ten
characters and prohibits special characters. Legal characters are
``_0-9A-Za-z`` and the whitespace character. Atoms are created using
the ``constexpr`` function ``atom``, as the following example
illustrates.
Atoms in CAF are tag types, i.e., usually defined as en empty ``struct``. These
types carry no data on their own and only exist to annotate messages. For
example, we could create the two tag types ``add_atom`` and ``multiply_atom``
for implementing a simple math actor like this:
.. code-block:: C++
atom_value a1 = atom("add");
atom_value a2 = atom("multiply");
CAF_BEGIN_TYPE_ID_BLOCK(my_project, caf::first_custom_type_id)
**Warning**: The compiler cannot enforce the restrictions at compile time,
except for a length check. The assertion ``atom("!?") != atom("?!")``
is not true, because each invalid character translates to a whitespace
character.
CAF_ADD_ATOM(my_project, add_atom)
CAF_ADD_ATOM(my_project, multiply_atom)
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
accomplish this, CAF offers compile-time *atom constants*.
.. code-block:: C++
using add_atom = atom_constant<atom("add")>;
using multiply_atom = atom_constant<atom("multiply")>;
Using these constants, we can now define message passing interfaces in a
convenient way:
.. code-block:: C++
CAF_END_TYPE_ID_BLOCK(my_project)
behavior do_math{
[](add_atom, int a, int b) {
[](add_atom, int32_t a, int32_t b) {
return a + b;
},
[](multiply_atom, int a, int b) {
[](multiply_atom, int32_t a, int32_t b) {
return a * b;
}
};
// caller side: send(math_actor, add_atom::value, 1, 2)
Atom constants define a static member ``value``. Please note that this
static ``value`` member does *not* have the type
``atom_value``, unlike ``std::integral_constant`` for example.
// caller side: send(math_actor, add_atom_v, int32_t{1}, int32_t{2})
The macro ``CAF_ADD_ATOM`` defined an empty ``struct`` with the given name as
well as a ``constexpr`` variable for conveniently creating a value of that type
that uses the type name plus a ``_v`` suffix. In the example above,
``atom_value`` is the type name and ``atom_value_v`` is the constant.
This diff is collapsed.
This diff is collapsed.
Overview
========
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:
Compiling CAF requires CMake and a recent C++ compiler. To get and compile the
sources on UNIX-like systems, type the following in a terminal:
.. ::
.. code-block:: bash
git clone https://github.com/actor-framework/actor-framework
cd actor-framework
./configure
make
make install [as root, optional]
make -C build
make -C build install [as root, optional]
We recommended to run the unit tests as well:
.. ::
make test
If the output 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``.
Running ``configure`` is not a mandatory step. The script merely automates the
CMake setup and makes setting build options slightly more convenient. On
Windows, use CMake directly to generate an MSVC project file.
Features
--------
......@@ -32,19 +26,13 @@ Features
* Thread-mapped actors for soft migration of existing applications
* Publish/subscribe group communication
Minimal Compiler Versions
-------------------------
* GCC 4.8
* Clang 3.4
* Visual Studio 2015, Update 3
Supported Operating Systems
---------------------------
* Linux
* Mac OS X
* Windows (static library only)
* Windows
* macOS
* FreeBSD
Hello World Example
-------------------
......
......@@ -139,6 +139,7 @@ The following example implements two actors, ``ping`` and
.. literalinclude:: /examples/testing/ping_pong.cpp
:language: C++
:lines: 12-60
:start-after: --(rst-ping-pong-begin)--
:end-before: --(rst-ping-pong-end)--
......@@ -21,7 +21,6 @@ Contents
ReferenceCounting
Error
ConfiguringActorApplications
Messages
GroupCommunication
ManagingGroupsOfWorkers
Streaming
......
manual/mailbox_element.png

20 KB | W: | H:

manual/mailbox_element.png

39.8 KB | W: | H:

manual/mailbox_element.png
manual/mailbox_element.png
manual/mailbox_element.png
manual/mailbox_element.png
  • 2-up
  • Swipe
  • Onion skin
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