The examples assume you are familiar with the basic concepts of the SigC++ library.
#include <iostream> #include <sigcx/thread.h> #include <sigcx/thread_tunnel.h>
using namespace std; using namespace SigC; using namespace SigCX; using namespace SigCX::Threads;
class HelloWorld : public StandardDispatcher { public:
void greet(const string& msg) { cout << "Hello World! " << msg << endl; } };
int main()
{
ThreadTunnel
to it, which runs the dispatcher of hello
in a new thread. HelloWorld hello; ThreadTunnel tunn(hello);
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));
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.");
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);
}
#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.