Showing posts with label API. Show all posts
Showing posts with label API. Show all posts
Wednesday, September 24, 2014
Version 0.11 released!
Version 0.11 of CAF has just been released. The core components mainly received optimizations and bugfixes this time around. However, CAF now includes the first alpha versions of a runtime inspection & debugging toolkit. Read more on GitHub.
Monday, May 5, 2014
Version 0.9 released!
Version 0.9 of libcppa has just been released. It features an all-new work-stealing scheduler. Expect new benchmark results in the near future as the last evaluation is heavily outdated by now.
Aside from performance performance tweaks, version 0.9 also features an improved broker interface covered in a new manual section as well as a new example.
Aside from performance performance tweaks, version 0.9 also features an improved broker interface covered in a new manual section as well as a new example.
Saturday, November 9, 2013
Moving Forward: Type Safety & Actors
Type safety is a topic that comes up very frequently when talking to C++ developers about libcppa - and actors in general. Indeed, the major concern about libcppa at the C++Now 2013 conference was that it does not provide a type-safe messaging interface. I'm not going to discuss pros and cons of dynamic and static typing. Both approaches do have their benefits. In libcppa, you can use atoms to "create" new message types on the fly and then pattern match on the receiver side. In this way, it is very lightweight to introduce new message types to a system, as you don't have to maintain header files for your message types. On the flip side, "wrong" message types are detected at runtime. When sending arguments in the wrong order, your pattern on the receiver side will not match. Naturally, most C++ developers expect the compiler to detect those kind of bugs.
Version 0.8 introduced strongly-typed actors to give developers a choice. Unfortunately, libcppa is now a two-class society. Even worse, it is indeed rather easy to get an untyped reference to a strongly-typed actor. But let's step back and take a look at the issues individually.
Version 0.8 introduced strongly-typed actors to give developers a choice. Unfortunately, libcppa is now a two-class society. Even worse, it is indeed rather easy to get an untyped reference to a strongly-typed actor. But let's step back and take a look at the issues individually.
- Second-class Typed Actors: Typed actors are not allowed to use guard expressions. This in itself is not a problem. The problem is how libcppa signals system events. For example, when monitoring an actor, you'll receive a "down" message consisting of the atom 'DOWN' and the exit reason as uint32. Since typed actors are not allowed to use guards, the only thing they could possibly do is to define a handler for all messages with an atom as first element followed by an uint32. There are 1064 possible atoms. As a consequence, libcppa needs to introduce message types for system messages. This will break code and I would prefer not to, but it's better to break some code that's easy to repair than to have a broken design.
- Separated APIs: To spawn a typed actor, we use functions that return the behavior. For untyped actors, we can use void functions that call 'become' internally. Even worse, 'become' now can throw - when called from a typed actor. It's probably for the best to move 'become' from the namespace cppa to the class event_based_actor and always use function that return a behavior.
- Type-safety Not Enforced: As you might know if you've ever used libcppa: An actor can get a handle to itself by using 'self' and a handle to any actor that sent a message to it by calling 'self->last_sender()'. This handle is of course untyped. So, not only can a typed actor get an untyped handle to itself, each actor it is communicating with has an untyped handle to it. The only way to enforce type safety is to either remove 'self' completely from the API or to change its type to something not convertible to actor_ptr, so that you can only access member functions like 'trap_exits'. The member function 'last_sender()' then also would have to go or to return only a logical address that you can use for monitoring and linking, but not for message passing.
- Missing Features: This is not a design issue, but libcppa 0.9 will of course implement proper remote communication for typed actors.
Labels:
API,
erlang,
pattern matching,
version 0.9
Tuesday, October 15, 2013
Version 0.8 released!
Version 0.8 of libcppa has just been released. The biggest change for existing code: The function reply is deprecated. Actors now do automatically reply to a message by returning a value from the message handler. This change does not only make your code cleaner, but this is also the only way a new kind of actors - strongly typed actors - are allowed to reply to incoming messages. Typed actors is the most frequently requested feature for libcppa - and have finally arrived. You can create typed actors using the function spawn_typed:
As you can see, the argument to spawn_typed is a match expression rather than a function. This is because a typed actor is not allowed to change its behavior. The messaging interface is burnt into its type. In the example above, the type of p0 is typed_actor_ptr<replies_to<int, int>::with<double>,replies_to<double, double>::with<double, double>>. In this way, the compiler is now able to type-check your messages:
Typed actors are not "feature complete" yet, i.e., typed actors cannot be published and it is not possible to use priorities when sending a message to a typed actor. However, this is just a matter of time. To learn more about typed actors, visit Section 15 of the 0.8 manual.
As if typed actors were not enough, version 0.8 includes yet another new kind of actors: brokers. A broker connects your actor system to any other network protocol. The new release includes an example featuring Google Protobuf: examples/remote_actors/protobuf_broker.cpp.
A small addition that is worth mentioning is the new exit reason exit_reason::user_shutdown. This reason can be used whenever you force actors to quit as part of application shutdown or for shutting down parts of your system that are no longer necessary.
auto p0 = spawn_typed( on_arg_match >> [](int a, int b) { return static_cast<double>(a) * b; }, on_arg_match >> [](double a, double b) { return make_cow_tuple(a * b, a / b); } );
As you can see, the argument to spawn_typed is a match expression rather than a function. This is because a typed actor is not allowed to change its behavior. The messaging interface is burnt into its type. In the example above, the type of p0 is typed_actor_ptr<replies_to<int, int>::with<double>,replies_to<double, double>::with<double, double>>. In this way, the compiler is now able to type-check your messages:
send(p0, 42); // <- compiler error send(p0, 42, 24); // <- ok sync_send(p0, 1, 2, 3).then ... // <- compiler error sync_send(p0, 1, 2).then( [](float) { ... } // <- compiler error: expected double ); sync_send(p0, 1, 2).then( [](double d) { ... } // <- ok );
Typed actors are not "feature complete" yet, i.e., typed actors cannot be published and it is not possible to use priorities when sending a message to a typed actor. However, this is just a matter of time. To learn more about typed actors, visit Section 15 of the 0.8 manual.
As if typed actors were not enough, version 0.8 includes yet another new kind of actors: brokers. A broker connects your actor system to any other network protocol. The new release includes an example featuring Google Protobuf: examples/remote_actors/protobuf_broker.cpp.
A small addition that is worth mentioning is the new exit reason exit_reason::user_shutdown. This reason can be used whenever you force actors to quit as part of application shutdown or for shutting down parts of your system that are no longer necessary.
Tuesday, May 21, 2013
Version 0.7.1 released
Version 0.7.1 of libcppa has just been released. This release fixes some bugs and improves compatibility with GCC 4.8.
Monday, May 13, 2013
Version 0.7 released
Version 0.7 of libcppa has just been released. For the first time in an official release, this version breaks code compatibility with earlier versions (this was not an easy step and caused some discussion). When calling spawn with a function, the new default implementation is event-based. Hence, your actor should set its behavior using become and not use functions like receive. However, it is pretty easy to port your existing code. To opt-in to the "old" behavior, use spawn<blocking_api>(fun_name).
Among improvements and optimizations, this version includes a few new functions as well:
Please note that the manual has received a huge update due to the changed default behavior of spawn and has not caught up to the new features yet.
Among improvements and optimizations, this version includes a few new functions as well:
- Priority-aware messaging (opt-in feature)
- OpenCL-based actors (must be enabled using '--with-opencl')
Please note that the manual has received a huge update due to the changed default behavior of spawn and has not caught up to the new features yet.
Friday, February 22, 2013
Version 0.6 released
Version 0.6 of libcppa has just been released. This release brings several improvements to the synchronous messaging API. Please read more about features like functor-only usage and continuations in the revamped Section 7 of the manual.
Among several smaller improvements, this release also includes:
Among several smaller improvements, this release also includes:
- manual skipping of messages
- Boost 1.53 support thanks to GitHub user abigagli
- a new quit_actor function to send 'EXIT' messages to actors
Monday, January 7, 2013
Version 0.5.5 released
Version 0.5.5 of libcppa has just been released. This release brings several bugfixes and performance improvements as well as aout – A Thread-Safe Wrapper for std::cout.
Friday, October 26, 2012
Version 0.5 released
Version 0.5 of libcppa has just been released. This release brings Log4j-like logfiles to libcppa developers (must be enabled at compile time), support for user-defined communication protocols, and actor companions.
Each class using the actor_companion_mixin has such an actor companion, which provides an easy way for active non-actor objects, i.e., objects with an own thread or event loop, to send messages to/as actor. The default use case for this feature is to treat GUI elements - widgets - as actors. A more specific mixin for Qt widgets is on its way.
By the way, libcppa now has more than a thousand commits on GitHub! :-)
Each class using the actor_companion_mixin has such an actor companion, which provides an easy way for active non-actor objects, i.e., objects with an own thread or event loop, to send messages to/as actor. The default use case for this feature is to treat GUI elements - widgets - as actors. A more specific mixin for Qt widgets is on its way.
By the way, libcppa now has more than a thousand commits on GitHub! :-)
Wednesday, August 22, 2012
Version 0.4 released
The main feature of version 0.4 is a streamlined and bugfixed network layer. Of course, there are some new features as well:
- Support for User-Defined Network Layer
The functions publish and remote_actor were overloaded to allow user-defined network layers such as OpenSSL; see this mailing list discussion and the doxygen documentation of util::input_stream, util::output_stream and util::acceptor for more details. - Shutdown Function
The new shutdown() function closes all network connections, stops the scheduler and deletes all oflibcppa's singletons. It is strongly recommended to call this function before returning from main(), especially if you are connected to remote actors. - Syntactic Sugar for Synchronous Messaging
Synchronous message handling using futures is flexible but sometimes too verbose.auto future = sync_send(...); handle_response(future, ...); // event-based API receive_response(future, ...); // blocking API
Version 0.4 provides some syntactic sugar to make your code more compact. Whenever you send a message and immediately wait for the response, you can write the following instead.sync_send(...).then(...); // event-based API sync_send(...).await(...); // blocking API
Furthermore, there is a feature request for a continuation-passing style API to enable developers to easily encode "send X then receive Y then send Z" message flows (see Issue 58 on GitHub). - Local Groups & Remote Actors
Local groups, as returned by calling group::get("local", ...); or group::anonymous(), are now not-so-local. It is possible to send a local group to a remote actor and let the remote actor join the group. Whenever an actor sends a message to the group, the message is send back to the owning process if needed and forwarded to all subscribers from there, including remote actors. This approach certainly does not scale for largely distributed groups, since it is N-times unicast*. However, it paves to path for more use cases of "local" groups and we are working on scalable group communication as well.
* The N in this case is the number of hosts and not the number of remote actors.
Friday, August 10, 2012
New Functions in 0.3.3
Among some bugfixes, version 0.3.3 also includes a few new functions to add 'missing' features:
- Forwarding of Messages
libcppa lacked an easy and transparent way to forward messages. The new function forward_to finally adds this functionality. Furthermore, forwarding a synchronous messages is not possible without this function. Read more about forwarding in Section 5.4 of the manual. - Messaging with Tuples
Matthias Vallentin pointed out that the API was somewhat inconsistent, since it did not provide functions to use a tuple as response message. We have added the following functions in version 0.3.3 to treat tuples as first-class citizen: send_tuple, sync_send_tuple, reply_tuple, delayed_send_tuple and delayed_reply_tuple. - Manual
The manual is now included to the source distribution as manual.pdf and states the libcppa version. - Doxygen
CMake checks whether doxygen is available on your system and adds an optional "doc" target to the Makefile. You can create your own local version of the doxygen documentation by running "make doc". Open the file html/index.html afterwards.
Wednesday, July 25, 2012
Version 0.3 Released
I'm glad to announce libcppa 0.3. This release has two major improvements.
Synchronous Messages
Actors can send synchronous response messages by using the function sync_send. This function returns a future to the response that can be received by using either the blocking receive_response or the event-based handle_response. Please read the section about synchronous communication in the manual for further details.
Configure Script
Thanks to Matthias Vallentin (a.k.a. mavam), libcppa has a much simpler build process now. The new configure script hides all CMake details behind a nice and clean interface.
Minor Improvements
- Context-switching can be disabled by using the --disable-context-switching configure option for platforms without Boost.Context support (Issue 24).
- The function tuple_cast does no longer require an additional header include (Issue 38).
- The new "cppa_fwd.hpp" header provides forward declarations for heavily used data structures, such as actor_ptr (Issue 36).
- become() no longer accepts pointers to avoid potential misuses and resulting memory leaks.
Friday, June 29, 2012
We Have a Beta! Read the Manual!
After months of development, I've just merged the development branch into the master branch. Get the V0.2.0 tag from Github or update your local working copy.
What's new?
* I know libcppa had a quite volatile API in the past. However, this is the first official release and libcppa is no longer released as experimental version. You will seldom see code-breaking changes from now on and in major updates only! This is the perfect time to get started with libcppa. :)
That said, version 0.2 had a few changes. Here is a list of "quick-fixes" to get your code running again:
I will update all blog posts in the near future, so that all code examples will compile and run with version 0.2.
Have fun!
What's new?
- Improved performance! (new benchark results will follow)
- CMake instead of Automake
- A user manual (PDF)
- A stable API*
* I know libcppa had a quite volatile API in the past. However, this is the first official release and libcppa is no longer released as experimental version. You will seldom see code-breaking changes from now on and in major updates only! This is the perfect time to get started with libcppa. :)
That said, version 0.2 had a few changes. Here is a list of "quick-fixes" to get your code running again:
- become_void() => quit()
- future_send() => delayed_send()
- stacked_event_based_actor is dead and gone; just use event_based_actor and the new policy-based API of become/unbecome (please read the manual for further details)
- fsm_actor => sb_actor (read: "State-Based Actor"); because "real", transition-oriented FSM actors are something I am thinking about as a possible feature in a future release
- spawn(new T(...)) => spawn<T>(...)
I will update all blog posts in the near future, so that all code examples will compile and run with version 0.2.
Have fun!
Monday, April 16, 2012
Guards! Guards!
(no, this post is not about the Discworld novel)
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.
By the way, gref is a great way to reduce verbosity of receive loops:
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(...).
Have fun!
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!
Tuesday, March 6, 2012
RIP invoke_rules
The class invoke_rules was among the first classes of libcppa. In fact, it received a lot of refactoring even before the first commit on github. However, it's finally gone. If your code fails to compile with the current version, this is how to fix your code:
invoke_rules → partial_function timed_invoke_rules → behaviorThe class invoke_rules had too much changes in the past and its name isn't very well chosen. In fact, the implemented behavior of it already was identical to a partial function. But it had a brother called timed_invoke_rules that was a partial function with a timeout. That's pretty much the definition of an actor's behavior, isn't it? It's an old remains from the time I've implemented on() and after(). The new partial_function/behavior interface is straightforward and much more intuitive.
Tuesday, January 24, 2012
Dining Philosophers
The Dining Philosophers Problem is a well-known exercise in computer science for concurrent systems. Recently, I've found an implementation for Akka that's an adaption of this algorithm of Dale Schumacher. I think it's a pretty nice example program to introduce libcppa's event-based actor implementation. The following source code is an adaption of the Akka example implementation.
Let's start with the straightforward chopstick implementation.
Btw: you should always use [=] in lambda expressions for event-based actors. This copies the this pointer into the lambda and you have access to all members. You should never use references, because become always immediately returns. If your lambda finally gets called, everything that was previously on the stack is gone! All your references are guaranteed to cause undefined behavior.
The implementation of philosopher is a little bit longer. Basically, we implement the following state diagram.
Have fun!
Let's start with the straightforward chopstick implementation.
#include "cppa/cppa.hpp" using std::chrono::seconds; using namespace cppa; // either taken by a philosopher or available struct chopstick : sb_actor<chopstick> { behavior& init_state; // a reference to available behavior available; behavior taken_by(const actor_ptr& philos) { // create a behavior new on-the-fly return ( on<atom("take"), actor_ptr>() >> [=](actor_ptr other) { send(other, atom("busy"), this); }, on(atom("put"), philos) >> [=]() { become(&available); } ); } chopstick() : init_state(available) { available = ( on<atom("take"), actor_ptr>() >> [=](actor_ptr philos) { send(philos, atom("taken"), this); become(taken_by(philos)); } ); } };The class fsm_actor uses the Curiously Recurring Template Pattern to initialize the actor with the behavior stored in the member init_state. We don't really have an initial state. Thus, init_state is a reference to the behavior available. You also could inherit from event_based_actor and override the member function init() by hand. But using an fsm_actor is more convenient. You change the state/behavior of an actor by calling become. It takes either a pointer to a member or an rvalue. The member function taken_by creates a behavior "on-the-fly". You could achieve the same by using a member storing the 'owning' philosopher. But why use member variables if you don't have to? This solution is easier to understand than manipulating some internal state.
Btw: you should always use [=] in lambda expressions for event-based actors. This copies the this pointer into the lambda and you have access to all members. You should never use references, because become always immediately returns. If your lambda finally gets called, everything that was previously on the stack is gone! All your references are guaranteed to cause undefined behavior.
The implementation of philosopher is a little bit longer. Basically, we implement the following state diagram.
/* * +-------------+ {(busy|taken), Y} * /-------->| thinking |<------------------\ * | +-------------+ | * | | | * | | {eat} | * | | | * | V | * | +-------------+ {busy, X} +-------------+ * | | hungry |----------->| denied | * | +-------------+ +-------------+ * | | * | | {taken, X} * | | * | V * | +-------------+ * | | wait_for(Y) | * | +-------------+ * | | | * | {busy, Y} | | {taken, Y} * \-----------/ | * | V * | {think} +-------------+ * \---------| eating | * +-------------+ * * * [ X = left => Y = right ] * [ X = right => Y = left ] */This is a simplification of the original diagram. A philosopher becomes hungry after receiving an atom("eat") and then tries to obtain its left and right chopstick. A philosopher eats if it obtained both chopsticks, otherwise it will think again.
struct philosopher : sb_actor<philosopher> { std::string name; // the name of this philosopher actor_ptr left; // left chopstick actor_ptr right; // right chopstick // note: we have to define all behaviors in the constructor because // non-static member initialization are not (yet) implemented in GCC behavior thinking; behavior hungry; behavior denied; behavior eating; behavior init_state;A philosopher has a name, a left and a right chopstick and a bunch of possible behaviors. Hopefully, GCC has non-static member initialization in the next version. For now, we have to define all behavior in the constructor, except for wait_for, which is realized as a function similar to taken_by of chopstick.
// wait for second chopstick behavior waiting_for(const actor_ptr& what) { return ( on(atom("taken"), what) >> [=]() { // create message in memory to avoid interleaved // messages on the terminal std::ostringstream oss; oss << name << " has picked up chopsticks with IDs " << left->id() << " and " << right->id() << " and starts to eat\n"; cout << oss.str(); // eat some time delayed_send(this, seconds(5), atom("think")); become(&eating); }, on(atom("busy"), what) >> [=]() { send((what == left) ? right : left, atom("put"), this); send(this, atom("eat")); become(&thinking); } ); }Our constructor defines all behaviors as well as the three member variables storing name and left and right and chopstick.
philosopher(const std::string& n, const actor_ptr& l, const actor_ptr& r) : name(n), left(l), right(r) { // a philosopher that receives {eat} stops thinking and becomes hungry thinking = ( on(atom("eat")) >> [=]() { become(&hungry); send(left, atom("take"), this); send(right, atom("take"), this); } ); // wait for the first answer of a chopstick hungry = ( on(atom("taken"), left) >> [=]() { become(waiting_for(right)); }, on(atom("taken"), right) >> [=]() { become(waiting_for(left)); }, on<atom("busy"), actor_ptr>() >> [=]() { become(&denied); } ); // philosopher was not able to obtain the first chopstick denied = ( on<atom("taken"), actor_ptr>() >> [=](actor_ptr& ptr) { send(ptr, atom("put"), this); send(this, atom("eat")); become(&thinking); }, on<atom("busy"), actor_ptr>() >> [=]() { send(this, atom("eat")); become(&thinking); } ); // philosopher obtained both chopstick and eats (for five seconds) eating = ( on(atom("think")) >> [=]() { send(left, atom("put"), this); send(right, atom("put"), this); delayed_send(this, seconds(5), atom("eat")); cout << ( name + " puts down his chopsticks and starts to think\n"); become(&thinking); } ); // philosophers start to think after receiving {think} init_state = ( on(atom("think")) >> [=]() { cout << (name + " starts to think\n"); delayed_send(this, seconds(5), atom("eat")); become(&thinking); } ); } };The source code is a straightforward implementation of the state diagram. Our main starts five chopsticks and five philosophers. We use an anonymous group to send the initial {think} message to all philosophers at once. You could send five single messages as well.
int main(int, char**) { // create five chopsticks cout << "chopstick ids:"; std::vector<actor_ptr> chopsticks; for (size_t i = 0; i < 5; ++i) { chopsticks.push_back(spawn<chopstick>()); cout << " " << chopsticks.back()->id(); } cout << endl; // a group to address all philosophers auto dinner_club = group::anonymous(); // spawn five philosopher, each joining the Dinner Club std::vector<std::string> names = { "Plato", "Hume", "Kant", "Nietzsche", "Descartes" }; for (size_t i = 0; i < 5; ++i) { spawn_in_group<philosopher>(dinner_club, names[i], chopsticks[i], chopsticks[(i+1) % chopsticks.size()]); } // tell philosophers to start thinking send(dinner_club, atom("think")); // real philosophers are never done await_all_others_done(); return 0; }The full source code can be found in the examples folder on github.
Have fun!
Sunday, January 22, 2012
Minor API Changes
I've removed some function from the cppa namespace in the latest version. If your code fails to compile after git pull, follow this instructions:
self() → self trap_exit(...) → self->trap_exit(...) last_received() → self->last_dequeued() link(other) → self->link_to(other) quit(reason) → self->quit(reason)
Wednesday, January 18, 2012
Opt for option!
In a perfect world, each function could safely assume all arguments are valid. But this is the real world and things go wrong all the time. Especially whenever a function needs to parse a user-defined input. A good example for such a function is X to_int(string const& str). What is the correct type of X?
Well, some might argue "int of course and the function should throw an exception on error!". I would not recommend it. Throwing an exception is really the very last thing you should do. An exception is a gigantic hammer that smashes your stack to smithereens. If your user didn't read the documentation and uses your function without a try block, the exception will kill the whole program. Furthermore, exceptions are slow. All major compilers are optimized to have few overhead for entering a try block. Throwing an exception, stack unwinding and catching are expensive. You should not throw an exception unless there is nothing else you can do.
Use a bool pointer. Some libraries, e.g., the Qt library, use a bool pointer as function argument. Honestly, I don't like this approach. It forces you do declare additional variables and always returns an object, even if the function shouldn't. It's ok for integers, but what if your function returns a vector or string? Creating empty objects is a waste of time.
Return a pair. Some STL functions return a pair with a boolean and the result. The boolean indicates whether the function was successful. Again, creating empty objects is a waste of time. Thus, this approach isn't very efficient. But that's not the real issue here:
Return a pointer. Safety issues aside, you should use the stack for doing work and the heap for dynamically growing containers. Your stack is your friend. It is fast, automatically destroys variables as they go out of scope, and did I mention fast? Allocating small objects on the heap has a significant performance impact.
Return an option. If you're familiar with Haskell or Scala, you'll know Maybe or Option. In short, a function doesn't return a value. It returns maybe a value. If your string actually is an integer, the function returns an integer. Otherwise, it returns nothing. libcppa does have an option class. I guess you'll know what this code is supposed to do:
This are some performance results for cppa::option compared to returning an int, returning a pair, and returning a boost::optional.
The boost implementation clearly falls short. This is because the boost implementation doesn't use the stack. That's not because the boost developers don't know how to write efficient code. It's because unrestricted unions are a C++11 feature. cppa::option has a slight overhead compared to a pair for returning values but is even faster than a pair for returning empties, because the memory is uninitialized in this case. If you're already a user of libcppa: use option. If you're not (yet) a user: copy & paste the source code and use it. :)
Of course, it's C++11 only. Honestly, I really don't know why this isn't part of the STL. It should! It's general, fast, safe and really improves the readability of your source code.
Well, some might argue "int of course and the function should throw an exception on error!". I would not recommend it. Throwing an exception is really the very last thing you should do. An exception is a gigantic hammer that smashes your stack to smithereens. If your user didn't read the documentation and uses your function without a try block, the exception will kill the whole program. Furthermore, exceptions are slow. All major compilers are optimized to have few overhead for entering a try block. Throwing an exception, stack unwinding and catching are expensive. You should not throw an exception unless there is nothing else you can do.
Use a bool pointer. Some libraries, e.g., the Qt library, use a bool pointer as function argument. Honestly, I don't like this approach. It forces you do declare additional variables and always returns an object, even if the function shouldn't. It's ok for integers, but what if your function returns a vector or string? Creating empty objects is a waste of time.
Return a pair. Some STL functions return a pair with a boolean and the result. The boolean indicates whether the function was successful. Again, creating empty objects is a waste of time. Thus, this approach isn't very efficient. But that's not the real issue here:
auto x = to_int("try again"); if (x.first) do_something(x.second);Is this code correct? The answer is "I don't know". Remember, you can assign a bool to an int and you can use integers in if-statements. You have to read the documentation of to_int to see if x.first is the bool or the int.
Return a pointer. Safety issues aside, you should use the stack for doing work and the heap for dynamically growing containers. Your stack is your friend. It is fast, automatically destroys variables as they go out of scope, and did I mention fast? Allocating small objects on the heap has a significant performance impact.
Return an option. If you're familiar with Haskell or Scala, you'll know Maybe or Option. In short, a function doesn't return a value. It returns maybe a value. If your string actually is an integer, the function returns an integer. Otherwise, it returns nothing. libcppa does have an option class. I guess you'll know what this code is supposed to do:
auto x = to_int("try again"); if (x) do_something(*x);So, what is the type of x here? It is "option<int>". You can write if (x.valid()) do_something(x.get()); instead if you prefer a more verbose style. Option supports default values: do_something(x.get_or_else(0));. If you're a user of the boost library, maybe you'll know boost::optional. In general, I would recommend boost to everyone, but boost::optional is slow. Just have a look at the implementation. cppa::option uses a union to store the value. If the option is empty, the object in the union won't get constructed. You'll have a slight overhead of returning "empty memory" but you don't pay for creating empty objects.
This are some performance results for cppa::option compared to returning an int, returning a pair, and returning a boost::optional.
return type | 100,000,000 values | 100,000,000 empties |
---|---|---|
int | 0.488s | - |
std::pair<bool, int> | 2.418s | 2.304s |
cppa::option<int> | 2.776s | 1.598s |
boost::optional<int> | 7.419s | 2.987s |
Of course, it's C++11 only. Honestly, I really don't know why this isn't part of the STL. It should! It's general, fast, safe and really improves the readability of your source code.
Wednesday, December 7, 2011
Documentation
Finally! The section about message handling is done and the doxygen documentation is now online at github pages: http://neverlord.github.com/libcppa/.
Send me a mail or leave a comment if you miss something important.
Have fun!
Send me a mail or leave a comment if you miss something important.
Have fun!
Wednesday, September 21, 2011
delayed_send
delayed_send and its brother delayed_reply are great if you need to poll a resource every x (milli)seconds while staying responsive to other messages or if you want to implement a "timed loop". Both accept std::chrono durations in seconds, milliseconds, microseconds and minutes. However, the accuracy depends on your scheduler, operating system, workload, etc., so you shouldn't rely on a microseconds-timing.
A good example for such a loop is an animation. In message oriented programming (and this is was libcppa is about), the most idiomatic way to implement such a loop is to send a message to yourself that is delayed by a predefined amount of time. If you receive that message, you do the next animation step and send another delayed message to yourself, and so on, until the animation is done.
Today's example is a terminal-animation with a dancing Kirby. Have fun!
A good example for such a loop is an animation. In message oriented programming (and this is was libcppa is about), the most idiomatic way to implement such a loop is to send a message to yourself that is delayed by a predefined amount of time. If you receive that message, you do the next animation step and send another delayed message to yourself, and so on, until the animation is done.
Today's example is a terminal-animation with a dancing Kirby. Have fun!
#include <chrono> #include <iostream> #include <algorithm> #include "cppa/cppa.hpp" using std::cout; using std::endl; using namespace cppa; // ASCII art figures constexpr const char* figures[] = { "<(^.^<)", "<(^.^)>", "(>^.^)>" }; // array of {figure, offset} pairs constexpr size_t animation_steps[][2] = { {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}, {1, 9}, {2, 10}, {2, 11}, {2, 12}, {2, 13}, {1, 13}, {0, 13}, {0, 12}, {0, 11}, {0, 10}, {0, 9}, {0, 8}, {0, 7}, {1, 7} }; constexpr size_t animation_width = 20; // "draws" an animation step: {offset_whitespaces}{figure}{padding} void draw_kirby(size_t const (&animation)[2]) { cout.width(animation_width); cout << '\r'; std::fill_n(std::ostream_iterator<char>{cout}, animation[1], ' '); cout << figures[animation[0]]; cout.fill(' '); cout.flush(); } void dancing_kirby() { // let's get it started send(self, atom("Step")); // iterate over animation_steps auto i = std::begin(animation_steps); receive_for(i, std::end(animation_steps)) ( on<atom("Step")>() >> [&]() { draw_kirby(*i); // animate next step in 150ms delayed_send(self, std::chrono::milliseconds(150), atom("Step")); } ); } int main() { cout << endl; dancing_kirby(); cout << endl; }
Subscribe to:
Posts (Atom)