Wednesday, November 14, 2012

Using Actors with Qt

Actor programming is nice and makes our lives easier, but at some point, we have to display the output of our program to the user. Not everything is a command line tool. Hence, we have to use some sort of GUI library, which raises the question how we pass messages from actors - safely! - to the GUI at runtime. Well, what if we could send an ordinary message to the GUI as if it's an actor? What if the GUI can send us ordinary messages whenever the user pushes some buttons? Briefly speaking, can we treat GUI elements as actors? Luckily, the answer is yes! We have to write some gluecode, but in fact, you can treat everything as an actor with libcppa.

Getting Started

In this article, we will focus on the Qt library, as it is the open source GUI library of choice for C++ developers. In the next article, we will have a look at the general concept of actor companions. An actor companion is an actor that co-exists with another object. In our case, this object is a QWidget-base class. The companion is used to receive and send messages, but it does not have any behavior itself. All it takes to enable a class to have such a companion is to use the actor_widget_mixin.

The following class implements a simple group-based chat widget with a QTextEdit for text output and a QLineEdit for user input (chat messages). The full source code can be found in the examples folder in the Git repository.
#include <QWidget>
#include "cppa/qtsupport/actor_widget_mixin.hpp"

class ChatWidget : public cppa::actor_widget_mixin<QWidget> {

  Q_OBJECT

  typedef cppa::actor_widget_mixin<QWidget> super;

  // ...

 public:

  ChatWidget(QWidget* parent = nullptr, Qt::WindowFlags f = 0);

  // ...

 private:

  std::string m_name;
  cppa::group_ptr m_chatroom;

};
The mixin adds the following member functions to ChatWidget:
  • actor_ptr as_actor()
    returns the companion of this widget
  • set_message_handler
    sets the partial function for message processing

Handle Messages to the Widget

In our example, the widget should handle 'join', 'setName', and 'quit' messages as well as display chat messages (received as std::string). We encode our message handling directly into the constructor of ChatWidget:
ChatWidget::ChatWidget(QWidget* parent, Qt::WindowFlags f) : super(parent, f) {
  set_message_handler (
    on(atom("join"), arg_match) >> [=](const group_ptr& what) {
      if (m_chatroom) {
        send(m_chatroom, m_name + " has left the chatroom");
        self->leave(m_chatroom);
      }
      self->join(what);
      print(("*** joined " + to_string(what)).c_str());
      m_chatroom = what;
      send(what, m_name + " has entered the chatroom");
    },
    on(atom("setName"), arg_match) >> [=](string& name) {
      send(m_chatroom, m_name + " is now known as " + name);
      m_name = std::move(name);
      print("*** changed name to "
            + QString::fromUtf8(m_name.c_str()));
    },
    on(atom("quit")) >> [=] {
      close(); // close widget
    },
    on<string>() >> [=](const string& txt) {
      // don't print own messages
      if (self != self->last_sender()) {
        print(QString::fromUtf8(txt.c_str()));
      }
    }
  );
}

Pitfalls & Things to Know

The code is pretty straight forward if you are already familiar with libcppa, but there is one important thing to note: Unhandled messages are lost. Although the widget is able to handle libcppa messages now, it does not have a mailbox. Messages are delivered to the widget as QEvent objects that are disposed afterwards.

The second thing to note is that you should never use self outside the message handler. This includes using functions such as send, because self is internally used to determine the sender of a message. The reason to this limitation is that libcppa's self "pointer" is thread-local. Using self would therefore convert the Qt thread your widget runs in to an actor, but it wouldn't address your widget's companion.

To send a message from outside of your handler, you have to tell libcppa who is the sender of the message by hand:
send_as(as_actor(), m_chatroom, "hello world!");


Have fun!

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! :-)

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 of libcppa'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.

Thursday, July 26, 2012

libcppa on Mountain Lion

I have just upgraded my Mac and I have good news for all Apple users out there!

If you have switched to Mountain Lion or have plans to do so, you will find clang++ in version 4.0 after installing XCode. Hence, there is finally no need to get a recent compiler from an external source. libcppa 0.3 compiles and runs like a charm after installing CMake and Boost from MacPorts.

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?

  • 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!