On Jun 26, 2006, at 8:11 PM, Joe Van Dyk wrote:
On 6/26/06, Michael Ekstrand <michael elehack net> wrote:On Sun, Jun 25, 2006 at 10:12:13AM -0700, Joe Van Dyk wrote:Say a user clicks a checkbox in a menu that affects three other parts of the application. Would a signal be the best way to communicate that change to the rest of the application? How would that work? Would I create a custom signal or slot? How would the other parts of the application listen to that signal? Would I create the slot and then pass that slot to the other parts of the application (i.e. in a constructor)?What exactly you do to accomplish depends on what you're doing,obviously. However, signals and slots are frequently a decent way to do this kind of thing. For example, in my application, I have a singletonclass that manages application settings. My preferences dialog sets configuration items in this config class when its checkboxes, etc. aremanipulated. Other parts of the application can then connect to signalsthe configuration engine fires when config keys are changed. So when you check the "always show tabs" checkbox, it sets the "tabalways"configuration key to True in the config engine. The config engine thenfires the signal connected to the "tabalways" key, so any listeningapplication component knows that the "tabalways" option has changed, andcan adjust itself appropriately. It provides a very flexible, and somewhat centralized, wah of disseminating such events.How do the various application components listen to the signal?
The key is that the configuration object is a singleton. It is managed by an App object (also a singleton). And my App object (to which a pointer is globally available - a getApp() function would also work) has a get_config() method, which returns a reference to the Config object. So a window can do
app->get_config().signal_changed("mainwindow", "tabalways").connect(some_listener)
The config object stores signals in a map, and signal_changed returns the existing signal for a particular config key if one exists, or creates a new signal and stores it in the map if one doesn't.
I've gone through the tutorial for libsigc++ and it's still terribly confusing to me. http://libsigc.sourceforge.net/libsigc2/docs/manual/html/ch02s02.html says "Handily it derives from sigc::trackable already". I have no idea why that's handy. First time sigc::trackable is referenced in the document.
Actually, sigc::trackable is referenced in the introduction, where it says that a slot can use a pointer-to-a-member-function and that its object should inherit from sigc::trackable.
Why it's handy is that sigc::trackable provides the necessary mechanism so that signals don't try to call member functions bound to deleted objects. It provides a virtual destructor that disconnects all signal connections with members of the now-deleted object as slots, so that you don't have strange segfaults. So any time you use sigc::mem_fun to build a slot pointing to a method, the object containing the method should inherit from sigc::trackable. That should be cleared up though.
Any chance someone would like to update that tutorial to make it a bit more clear? And possibly also introduce functors in it?
A functor has the same definition as it does in the STL - to quote SGI's excellent STL documentation, "any object that can be called as if it is a function."
So the problem is probably that the tutorial doesn't document prerequisite knowledge... it's probably best read with an STL reference in hand. Perhaps a note should be added to the introduction that the tutorial assumes familiarity with STL concepts and terminology. Which, I've learned, is about necessary in order to do much with templates.
I would work on enhancing the tutorial, but I'm already behind enough without adding more tasks to my summer... Maybe if I get good and sick sometime...
- Michael