Monday, June 6, 2011

Atoms

I wrote about the problem with atoms a while ago. It's an important topic for an actor library because there are basically two ways to add semantics to messages (other than "this is an integer!"): always send "plain" tuples between actors which are tagged by atoms (or something similar) or create a tuple class per "message type". This is what the Scala-guys do with their case classes. And this is what I did in acedia. But the acedia implementation (or should I say emulation?) depended on massive use of the C preprocessor (which is almost ever a good indicator for a bad design).

Case classes are nice if they're part of your language and type system, but not if your host language is C++. So I'm happy to finally have a satisfying atom implementation for libcppa (although it still has a limitation).

Let me once again start with the (Erlang-) printer example:

Printer = spawn(...),
Printer ! { do_print, "hello world" },
...

This is the libcppa equivalent with the new atoms-implementation:

auto printer = spawn(printer_loop);
printer << make_tuple(atom("do_print"), "hello world");
The implementation of printer_loop might look like this:
void printer_loop()
{
    auto cb = on<atom("do_print"),std::string>() >> [](const std::string& str){
        cout << str << endl;
    };
    for (;;) receive(cb);
}
atom() is a consexpr function that maps the given string to an integer value. This value can be used as template parameter and only this value is used to compare two atoms (no string comparison at runtime!). This mapping is bijective (because the values must not collide), but it allows only a string length of 10 or less (which is the mentioned limitation). If you're interested in the "dirty details" checkout the sources and read cppa/detail/atom_val.hpp. :)

Of course, the atom() function will be replaced by a user-defined string literal as soon as GCC supports them.