libcaf  0.15.5
Blocking API

Blocking functions to receive messages. More...

Blocking functions to receive messages.

The blocking API of CAF is intended to be used for migrating previously threaded applications. When writing new code, you should consider the nonblocking API based on become and unbecome first.

Sending Messages

The function send can be used to send a message to an actor. The first argument is the receiver of the message followed by any number of values:

~~ // spawn some actors auto a1 = spawn(...); auto a2 = spawn(...); auto a3 = spawn(...);

// send a message to a1 send(a1, atom("hello"), "hello a1!");

// send a message to a1, a2, and a3 auto msg = make_message(atom("compute"), 1, 2, 3); send(a1, msg); send(a2, msg); send(a3, msg); ~~

Receive messages

The function receive takes a behavior as argument. The behavior is a list of { pattern >> callback } rules.

~~ receive ( on(atom("hello"), arg_match) >> [](const std::string& msg) { cout << "received hello message: " << msg << endl; }, on(atom("compute"), arg_match) >> [](int i0, int i1, int i2) { // send our result back to the sender of this messages return make_message(atom("result"), i0 + i1 + i2); } ); ~~

Please read the manual for further details about pattern matching.

Atoms

Atoms are a nice way to add semantic informations to a message. Assuming an actor wants to provide a "math sevice" for integers. It could provide operations such as addition, subtraction, etc. This operations all have two operands. Thus, the actor does not know what operation the sender of a message wanted by receiving just two integers.

Example actor: ~~ void math_actor() { receive_loop ( on(atom("plus"), arg_match) >> [](int a, int b) { return make_message(atom("result"), a + b); }, on(atom("minus"), arg_match) >> [](int a, int b) { return make_message(atom("result"), a - b); } ); } ~~

Receive Loops

Previous examples using receive create behaviors on-the-fly. This is inefficient in a loop since the argument passed to receive is created in each iteration again. It's possible to store the behavior in a variable and pass that variable to receive. This fixes the issue of re-creation each iteration but rips apart definition and usage.

There are four convenience functions implementing receive loops to declare behavior where it belongs without unnecessary copies: receive_loop, receive_while, receive_for and do_receive.

receive_loop is analogous to receive and loops "forever" (until the actor finishes execution).

receive_while creates a functor evaluating a lambda expression. The loop continues until the given lambda returns false. A simple example:

~~ // receive two integers vector<int> received_values; receive_while([&]() { return received_values.size() < 2; }) ( on<int>() >> [](int value) { received_values.push_back(value); } ); // ... ~~

receive_for is a simple ranged-based loop:

~~ std::vector<int> vec {1, 2, 3, 4}; auto i = vec.begin(); receive_for(i, vec.end()) ( on(atom("get")) >> [&]() -> message { return {atom("result"), *i}; } ); ~~

do_receive returns a functor providing the function until that takes a lambda expression. The loop continues until the given lambda returns true. Example:

~~ // receive ints until zero was received vector<int> received_values; do_receive ( on<int>() >> [](int value) { received_values.push_back(value); } ) .until([&]() { return received_values.back() == 0 }); // ... ~~

Sending Delayed Messages

The function delayed_send provides a simple way to delay a message. This is particularly useful for recurring events, e.g., periodical polling. Usage example:

~~ delayed_send(self, std::chrono::seconds(1), poll_atom::value); receive_loop ( // ... [](poll_atom) { // ... poll something ... // and do it again after 1sec delayed_send(self, std::chrono::seconds(1), poll_atom::value); } ); ~~

See also the dancing kirby example.