Main Page | Modules | Class Hierarchy | Alphabetical List | Class List | Directories | Class Members | Related Pages

Starting

So in this section, you will see a simple, short "Hello-World!"-style example on how to use SigCX.

The examples assume you are familiar with the basic concepts of the SigC++ library.

A Simple Example

First we include the headers for the threading API and the cross-thread tunnels:
#include <iostream>

#include <sigcx/thread.h>
#include <sigcx/thread_tunnel.h>
Then we import the relevant namespaces:
using namespace std;
using namespace SigC;
using namespace SigCX;
using namespace SigCX::Threads;
We can then derive a class from the StandardDispatcher class. This class will run the dispatcher in a seperate thread, so we can create a tunnel to this thread (A tunnel is used to transfer callbacks to the thread and execute them there).
class HelloWorld : public StandardDispatcher
{
  public:
In our class we implement a greet() that just displays a message.
    void greet(const string& msg) {
      cout << "Hello World! " << msg << endl;
    }
};
Now the main function:
int main()
{
Here we create a HelloWorld object, and a ThreadTunnel to it, which runs the dispatcher of hello in a new thread.
  HelloWorld hello;
  ThreadTunnel tunn(hello);
Then we send invoke the HelloWorld::greet() method by calling the tunnel() function. The template arguments specify the return type and argument type. You only need them when the compiler cannot instantiate the tunnel() function on its own.
  
  tunnel<void, const string&>(slot(hello, &HelloWorld::greet), 
                              "Today is a nice day.", &tunn);

  Slot1<void, const string&> tunneled_slot = 
    open_tunnel(&tunn, slot(hello, &HelloWorld::greet));
Note that the tunnel() invocation returns immediatly, as the passed slot will be invoked asynchronously.

As an alternative, you can create a proxy tunnel with open_tunnel() and invoke that:

  
  tunneled_slot("I say goodbye now.");
The tunneled_slot is also asynchronous, so you cannot use its return value (wich is void in this case, anyway).

Now we are done with our HelloWorld thread, so we send an exit message to it's dispatcher. Note the last argument to the tunnel() call. It is by default initialized to false, but if you pass true, the method waits until the slot has finished execution. So we are sure that after this call all previously sent messages have been processed.

  
  tunnel(slot(dynamic_cast<Dispatcher&>(hello), &Dispatcher::exit), 
         &tunn, true);
}

A GUI Example

As libSigCX is quite useful for GUI programming (using multithreading to provide a non-blocking GUI) and integrates very well with gtkmm, a gtkmm example comes with libSigCX since version 0.6.4. The source code is commented quite well (feel free to suggest improvements, so there is no line-by-line walkthrough.

#include <iostream>
#include <sstream>

#include <unistd.h>
#include <signal.h>

#include <gtkmm.h>
#include <sigc++/class_slot.h>
#include <sigc++/bind_return.h>
#include <sigcx/thread_tunnel.h>
#include <sigcx/gtk_dispatch.h>

using namespace std;
using namespace SigC;
using namespace SigCX;
using namespace SigCX::Threads;

// Incrementor. 
//
// Simply call a slot with an increasing value. In a real
// application, you'd do some real work in this class. We want to run
// in our own thread, so we derive from StandardDispatcher.
//
class Incrementor : public StandardDispatcher
{
  public:
    Incrementor(Slot1<void, int> s) : count_(0), slot_(s) { }
  private:
    void do_increment() {
      slot_(count_++);
      // we have ourselves called again in 250 milliseconds
      add_timeout_handler_msec(slot(*this, &Incrementor::do_increment), 250);
    }
    // Run method, overrides StandardDispatcher::run
    virtual bool run(bool infinite = true) {
      // We start with do_increment(), since that registers our timeout
      // handler
      do_increment();
      return StandardDispatcher::run(infinite);
    }
    int count_;
    Slot1<void, int> slot_;
};

//
// Application window
//
class MyWindow: public Gtk::Window
{
    Gtk::Label label_[2];
    // We need a GtkDispatcher here, since we run on the GTK+ main
    // loop.
    GtkDispatcher disp_;
    ThreadTunnel my_tunnel_;
    ThreadTunnel *thread_tunnel_[2];
    Incrementor *incrementor_[2];
  public:
    MyWindow();
    virtual ~MyWindow();
  protected:
    void set_label(int i, int label_idx);
    void launch_threads();
    void sig_handler(int sig);
    bool on_delete(GdkEventAny*);
};


MyWindow::MyWindow()
    : 
    // We need a tunnel to our dispatcher, so the threads can talk to
    // us
    my_tunnel_(disp_, ThreadTunnel::CurrentThread)
{
  // one-shot idle handler to start the threads
  Glib::signal_idle().connect(
          bind_return(slot(*this, &MyWindow::launch_threads), false));
  // connect to SIGINT signal, so we can do a clean shutdown
  disp_.add_signal_handler(bind(slot(*this, &MyWindow::sig_handler), SIGINT), 
                           SIGINT);
  // we want to react on window-delete, too
  signal_delete_event().connect(slot(*this, &MyWindow::on_delete), false);
  
  // Create our 2 incrementors, both operating on a tunneled slot to
  // our set_label() method. One slot is synchronous, the other
  // asynchronous (last argument to open_tunnel).
  for (int i = 0; i < 2; i++)
  {
    Slot1<void, int> tslot = open_tunnel(
            &my_tunnel_, 
            bind(slot(*this, &MyWindow::set_label), i), 
            (i % 2 == 0) ? true : false);
    incrementor_[i] = new Incrementor(tslot);
    thread_tunnel_[i] = 0;
  }
  
  // Construct our GUI elements: Two labels - very funky ;-)
  Gtk::HBox* hbox = manage(new Gtk::HBox(true, 10));
  hbox->set_border_width(5);
  hbox->pack_start(label_[0]);
  hbox->pack_start(label_[1]);
  add(*hbox);
  show_all_children();
}

MyWindow::~MyWindow()
{
  // We drain the tunnel to ourselves, so any messages pending and
  // sent now are thrown away.
  my_tunnel_.drain();
  
  // We send exit messages to our incrementors (synchronously, so we
  // know their dispatcher is exiting when we return (and cease to
  // exist, along with our tunnel)
  for (int i = 0; i < 2; i++)
  {
    tunnel(slot(dynamic_cast<Dispatcher&>(*incrementor_[i]), 
                &Dispatcher::exit),
           thread_tunnel_[i], true);
    if (thread_tunnel_[i])
      delete thread_tunnel_[i];
    delete incrementor_[i];
  }
}

bool MyWindow::on_delete(GdkEventAny*)
{
  // make the dispatcher exit (this ends the GTK+ event loop)
  disp_.exit();
  return true;
}

void MyWindow::set_label(int i, int label_idx)
{
  ostringstream os;
  os << i;
  label_[label_idx].set_text(os.str());
}

void MyWindow::launch_threads()
{
  for (int i = 0; i < 2; i++)
    thread_tunnel_[i] = new ThreadTunnel(*incrementor_[i]);
}

void MyWindow::sig_handler(int sig)
{
  // we need to take precausions to not exit more than once, since on
  // Linux, all threads receive a signal
  static volatile sig_atomic_t did_exit_ = 0;
  
  if (sig == SIGINT && !did_exit_)
  {
    did_exit_ = 1;
    cout << "SIGINT caught - exiting" << endl;
    disp_.exit();
  }
}

int main(int argc, char* argv[])
{
  Gtk::Main main(argc, argv);
  
  MyWindow window;
  window.show();
  main.run();
  
  return 0;
}

Go to the next section, to the previous section or return to the index.


Generated on Sun Apr 10 18:35:43 2005 for SigCX - SigC++ Extras by  doxygen 1.4.2