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

Starting

In my opinion, the best way of getting started with a new library is by examining example code. 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; }
00001 #include <iostream> 00002 #include <sstream> 00003 00004 #include <unistd.h> 00005 #include <signal.h> 00006 00007 #include <gtkmm.h> 00008 #include <sigc++/class_slot.h> 00009 #include <sigc++/bind_return.h> 00010 #include <sigcx/thread_tunnel.h> 00011 #include <sigcx/gtk_dispatch.h> 00012 00013 using namespace std; 00014 using namespace SigC; 00015 using namespace SigCX; 00016 using namespace SigCX::Threads; 00017 00018 // Incrementor. 00019 // 00020 // Simply call a slot with an increasing value. In a real 00021 // application, you'd do some real work in this class. We want to run 00022 // in our own thread, so we derive from StandardDispatcher. 00023 // 00024 class Incrementor : public StandardDispatcher 00025 { 00026 public: 00027 Incrementor(Slot1<void, int> s) : count_(0), slot_(s) { } 00028 private: 00029 void do_increment() { 00030 slot_(count_++); 00031 // we have ourselves called again in 250 milliseconds 00032 add_timeout_handler_msec(slot(*this, &Incrementor::do_increment), 250); 00033 } 00034 // Run method, overrides StandardDispatcher::run 00035 virtual bool run(bool infinite = true) { 00036 // We start with do_increment(), since that registers our timeout 00037 // handler 00038 do_increment(); 00039 return StandardDispatcher::run(infinite); 00040 } 00041 int count_; 00042 Slot1<void, int> slot_; 00043 }; 00044 00045 // 00046 // Application window 00047 // 00048 class MyWindow: public Gtk::Window 00049 { 00050 Gtk::Label label_[2]; 00051 // We need a GtkDispatcher here, since we run on the GTK+ main 00052 // loop. 00053 GtkDispatcher disp_; 00054 ThreadTunnel my_tunnel_; 00055 ThreadTunnel *thread_tunnel_[2]; 00056 Incrementor *incrementor_[2]; 00057 public: 00058 MyWindow(); 00059 virtual ~MyWindow(); 00060 protected: 00061 void set_label(int i, int label_idx); 00062 void launch_threads(); 00063 void sig_handler(int sig); 00064 bool on_delete(GdkEventAny*); 00065 }; 00066 00067 00068 MyWindow::MyWindow() 00069 : 00070 // We need a tunnel to our dispatcher, so the threads can talk to 00071 // us 00072 my_tunnel_(disp_, ThreadTunnel::CurrentThread) 00073 { 00074 // one-shot idle handler to start the threads 00075 Glib::signal_idle().connect( 00076 bind_return(slot(*this, &MyWindow::launch_threads), false)); 00077 // connect to SIGINT signal, so we can do a clean shutdown 00078 disp_.add_signal_handler(bind(slot(*this, &MyWindow::sig_handler), SIGINT), 00079 SIGINT); 00080 // we want to react on window-delete, too 00081 signal_delete_event().connect(slot(*this, &MyWindow::on_delete), false); 00082 00083 // Create our 2 incrementors, both operating on a tunneled slot to 00084 // our set_label() method. One slot is synchronous, the other 00085 // asynchronous (last argument to open_tunnel). 00086 for (int i = 0; i < 2; i++) 00087 { 00088 Slot1<void, int> tslot = open_tunnel( 00089 &my_tunnel_, 00090 bind(slot(*this, &MyWindow::set_label), i), 00091 (i % 2 == 0) ? true : false); 00092 incrementor_[i] = new Incrementor(tslot); 00093 thread_tunnel_[i] = 0; 00094 } 00095 00096 // Construct our GUI elements: Two labels - very funky ;-) 00097 Gtk::HBox* hbox = manage(new Gtk::HBox(true, 10)); 00098 hbox->set_border_width(5); 00099 hbox->pack_start(label_[0]); 00100 hbox->pack_start(label_[1]); 00101 add(*hbox); 00102 show_all_children(); 00103 } 00104 00105 MyWindow::~MyWindow() 00106 { 00107 // We drain the tunnel to ourselves, so any messages pending and 00108 // sent now are thrown away. 00109 my_tunnel_.drain(); 00110 00111 // We send exit messages to our incrementors (synchronously, so we 00112 // know their dispatcher is exiting when we return (and cease to 00113 // exist, along with our tunnel) 00114 for (int i = 0; i < 2; i++) 00115 { 00116 tunnel(slot(dynamic_cast<Dispatcher&>(*incrementor_[i]), 00117 &Dispatcher::exit), 00118 thread_tunnel_[i], true); 00119 if (thread_tunnel_[i]) 00120 delete thread_tunnel_[i]; 00121 delete incrementor_[i]; 00122 } 00123 } 00124 00125 bool MyWindow::on_delete(GdkEventAny*) 00126 { 00127 // make the dispatcher exit (this ends the GTK+ event loop) 00128 disp_.exit(); 00129 return true; 00130 } 00131 00132 void MyWindow::set_label(int i, int label_idx) 00133 { 00134 ostringstream os; 00135 os << i; 00136 label_[label_idx].set_text(os.str()); 00137 } 00138 00139 void MyWindow::launch_threads() 00140 { 00141 for (int i = 0; i < 2; i++) 00142 thread_tunnel_[i] = new ThreadTunnel(*incrementor_[i]); 00143 } 00144 00145 void MyWindow::sig_handler(int sig) 00146 { 00147 // we need to take precausions to not exit more than once, since on 00148 // Linux, all threads receive a signal 00149 static volatile sig_atomic_t did_exit_ = 0; 00150 00151 if (sig == SIGINT && !did_exit_) 00152 { 00153 did_exit_ = 1; 00154 cout << "SIGINT caught - exiting" << endl; 00155 disp_.exit(); 00156 } 00157 } 00158 00159 int main(int argc, char* argv[]) 00160 { 00161 Gtk::Main main(argc, argv); 00162 00163 MyWindow window; 00164 window.show(); 00165 main.run(); 00166 00167 return 0; 00168 }

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


Generated on Wed Jul 28 08:27:30 2004 for SigCX - SigC++ Extras by doxygen 1.3.7