Guards are a new feature in libcppa. More precisely, it's an adaption of Erlangs guard sequences for patterns. A guard is an optional expression that evaluates to either true or false for a given input (message). Guards can be used to constrain a given match statement by using placeholders (_x1 - _x9) as shown in the example below.
using namespace cppa; using namespace cppa::placeholders; receive_loop( on<int>().when(_x1 % 2 == 0) >> []() { // int is even }, on<int>() >> []() { // int is odd } );Guard expressions are a lazy evaluation technique (somewhat like boost lambdas). The placeholder _x1 is substituted with the first value of an incoming message. You can use all binary comparison and arithmetic operators as well as "&&" and "||". In addition, there are two functions designed to be used in guard expressions: gref and gcall. The function gref creates a reference wrapper. It's similar to std::ref but it is always const and 'lazy'. A few examples to illustrate some pitfalls:
int ival = 42; receive( on<int>().when(ival == _x1) // (1) ok, matches if _x1 == 42 on<int>().when(gref(ival) == _x1) // (2) ok, matches if _x1 == ival on<int>().when(std::ref(ival) == _x1) // (3) ok, because of placeholder on<anything>().when(gref(ival) == 42) // (4) ok, matches everything as long as ival == 42 on<anything>().when(std::ref(ival) == 42) // (5) compiler error );The statement std::ref(ival) == 42 is evaluated immediately and returns a boolean, whereas gref(ival) == 42 creates a guard expression. Thus, you should always use gref instead of std::ref to avoid subtle errors.
By the way, gref is a great way to reduce verbosity of receive loops:
bool done = false; do_receive( // ... on<atom("shutdown")>() >> [&]() { done = true; } ) .until(gref(done)); //equal to: .until([&]() { return done; })
The second function, gcall, encapsulates a function call. It's usage is similar to std::bind, but there is also a short version for unary functions: gcall(..., _x1) is equal to _x1(...).
auto vec_sorted = [](std::vector<int> const& vec) { return std::is_sorted(vec.begin(), vec.end()); }; receive( on<std::vector<int>>().when(gcall(vec_sorted, _x1)) // equal to: on<std::vector<int>>().when(_x1(vec_sorted))) // ... );Placeholders provide some more convenience member functions besides operator(). A few code snippets:
_x1.starts_with("hello") _x1.size() > 10 _x1.in({"abc", "def"}) _x1.not_in({0, 10, 100}) _x1.empty() _x1.not_empty() _x1.front() == 42A final note: you don't have to check if a container is empty before calling _x1.front(), because front() returns an option for a reference to the first element.
Have fun!
No comments:
Post a Comment