Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

JVM Languages

Java-like Messaging System for C++ Classes


Some time ago I tried to learn Java. I have read a couple of good books on the subject — I think everyone has his or her own favorite Java book. Java provides programmers with some fine idioms. Some idioms result from the language itself, and some are part of the standard Java library. One of these idioms is the event publishing scheme taken by the Swing library to enable event-driven GUI programming.

In Swing, when you need a button on your window, you create it, add the button object to the "content pane" (the logical part of the window that manages its elements), and you are done. Almost. You are not usually interested in buttons that do nothing, so you add the "event handling" to it. Swing associates an action with every element of the window (for example, the action for the button is "press") and you can bind your own code with that action. This is done in two steps:

1. Write a class that implements an ActionListener interface. To do that, you need to implement its method, actionPerformed. Put all the code that needs to be executed in this method.

2. Create an object of that class and pass it to the button object. In other words, register it.

Listing One presents a very simple Java program that creates a window on the screen with one button. The event-handler for the "press" action of the button prints a message on the console window. The "press" event handler is not the only event handler, though. The second event handler is the handler for an event associated with the window being closed that causes the whole application to terminate. The whole program can be written in much more compact form (thanks to anonymous inner classes [1]), but I wanted to expose the idea of registering event consumers to event producers. This idiom is not restricted to Java (nor to Swing for that matter). Java uses this idiom quite heavily, and the code presented in Listing One uses only a fraction of the possibilities.

Listing One

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class MyJavaApp
{
    public static void main(String[] args)
    {
        // create a button
        JButton button = new JButton("Press me!");

        // register the event handler for pressing the button
        button.addActionListener(new MyButtonListener());

        // organize the layout of the window
        JPanel pane = new JPanel();
        pane.add(button);
        JFrame frame = new JFrame("My Application");
        frame.getContentPane().add(pane, BorderLayout.CENTER);

        // register the event handler for closing the window
        frame.addWindowListener(new MyWindowListener());

        frame.pack();
        frame.setVisible(true);
    }
}

// this class implements the event handler for a button
class MyButtonListener implements ActionListener
{
    public void actionPerformed(ActionEvent e)
    {
        System.out.println("I was pressed!!!");
    }
}

// this class implements the event handler for
// closing the window
class MyWindowListener extends WindowAdapter
{
    public void windowClosing(WindowEvent e)
    {
        System.exit(0);
    }
}

If this idiom is not restricted to any particular language, it would be nice to implement it in C++.

Brute-Force Approach

The first try to implement the concept of event listeners may look like the one presented in Listing Two.

Listing Two

<pre>
// brute-force approach to the
// event producer-consumer idiom


#include <iostream>
#include <ostream>
#include <string>
#include <vector>

using namespace std;

// interface of the listener
class ActionListener
{
public:
    virtual void actionPerformed() = 0;
};

// some class that can generate events
class SomeClass
{
public:
    // registration function
    void addListener(ActionListener *p)
    {
        listeners_.push_back(p);
    }

    // some other functions
    // ...

    void foo();

private:
    // collection of pointers to listeners
    typedef vector<ActionListener*> ListenerBag;
    ListenerBag listeners_;

    // some other members
    // ...
};

// some function that can produce an event
void SomeClass::foo()
{
    // ...

    // produce an event and notify
    // all listeners that are registered
    ListenerBag::iterator it;
    ListenerBag::iterator itend = listeners_.end();

    for (it = listeners_.begin(); it != itend; ++it)
    {
         (*it)->actionPerformed();
    }
}

// some listener class
class MyListener : public ActionListener
{
public:
    MyListener(const string &name) : name_(name) {}
    void actionPerformed()
    {
        cout << name_
            << " : Action performed, sir!" << endl;
    }

private:
    string name_;
};

int main()
{
    MyListener l1("listener 1");
    MyListener l2("listener 2");

    SomeClass someobject;

    someobject.addListener(&l1);
    someobject.addListener(&l2);

    // ...

    someobject.foo();

    // ...

    return 0;
}

This approach has a couple of inherent problems that come with its design (these are only the problems I can find — there may be others):

1. It is not generic with respect to the interface. There is only one interface, ActionListener. In a non-trivial project, there can be a need for more than just one listener interface.

2. It is not generic with respect to the information being passed with each event. Here, the actionPerformed method does not accept any parameters, so it can be used only to notify of an event taking place. In practice, different events need to carry different amounts of information. For example, the information associated with an event may be the coordinates of a point on the screen where the mouse clicked, or it may be temperature data, such as the data produced by sensors in the core of a nuclear power station. There is no way of fixing the type of this event information.

3. It is not generic with respect to the interface vocabulary. In Listing One, the ActionListener interface has only one function signature. It is quite constraining for clients that are interested in different events, for example, not only in left-clicks but also in cursor movement. In the GUI, the window can produce many different events, like close clicked, minimalize, maximalize, and so on.

4. It does not scale up. This problem can be seen when one producer can be a source of an event defined by different listener interfaces. The brute-force approach imposes the need to implement as many collections in the event producer class as there are listener interfaces. Clearly, this approach does not promote code reuse.

5. It is not thread-safe. The STL collections are not thread-safe by their definition and SomeClass does nothing to change it. When two listener objects try to register themselves with the instance of the SomeClass class, the undefined behavior is waiting for them because the push_back method cannot be executed concurrently.

Is it THAT bad? Not quite. There is one big advantage of this brute-force strategy: it works. This should be the clue that it does not deserve to be thrown away at this stage. This strategy includes a couple good ideas. One good idea is the structure of event generator (event source). In Listing Two, it manages the collection of listeners (by pointers) that will be notified when the event takes place. Each listener can register himself with the event source by one of its public members (addListener). For the interface to be complete, the event source also needs a reverse function like removeListener so that listeners can notify it that they are no longer interested in its events.

This is the core of what is needed for implementing this Java-like communication idiom.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.