Signals and Slots in D
Bill Baxter
dnewsgroup at billbaxter.com
Fri Sep 29 00:40:09 PDT 2006
Walter Bright wrote:
> Ok, I admit I don't understand S&S. But let's try starting with Qt's
> canonical example from http://doc.trolltech.com/3.3/signalsandslots.html:
>
> class Foo : public QObject
> {
> Q_OBJECT
> public:
> Foo();
> int value() const { return val; }
> public slots:
> void setValue( int );
> {
> if ( v != val ) {
> val = v;
> emit valueChanged(v);
> }
> }
> signals:
> void valueChanged( int );
> private:
> int val;
> };
>
> Foo a, b;
> connect(&a, SIGNAL(valueChanged(int)), &b, SLOT(setValue(int)));
> b.setValue( 11 ); // a == undefined b == 11
> a.setValue( 79 ); // a == 79 b == 79
> b.value(); // returns
>
> It seems that I can do this in D with:
>
> class Foo
> {
> this();
> int value() { return val; }
>
> void setValue( int );
> {
> if ( v != val ) {
> val = v;
> valueChanged(v);
> }
> }
>
> void valueChanged( int i )
> {
> foreach (dg; slots)
> dg(i);
> }
>
> void connect( void delegate(int i) dg)
> {
> slots ~= dg;
> }
>
> private:
> void delegate(int i)[] slots;
>
> int val;
> };
>
> Foo a = new Foo;
> Foo b = new Foo;
> a.connect(&b.setValue);
> b.setValue( 11 ); // a == undefined b == 11
> a.setValue( 79 ); // a == 79 b == 79
> b.value(); // returns 79
>
> There's no casting, it's statically typesafe. Some of the boilerplate
> can be eliminated with a mixin. Is that all there is to it, or have I
> completely missed the boat?
It's close, but check out the signature of trolltech's connect method:
bool connect (
const QObject * sender, const char * signal,
const QObject * receiver, const char * method,
Qt::ConnectionType type = Qt::AutoCompatConnection
);
The key difference is that the target method is specified by a *string*.
That's the main difference between what Qt has and the S&S
implementations people generally come up with for C++ (or D).
Every QObject subclass has a QMetaObject member.
http://doc.trolltech.com/4.1/qmetaobject.html
QMetaObject has interesting methods like
int indexOfMethod ( const char * method ) const
int indexOfProperty ( const char * name ) const
int methodCount () const
QMetaMethod method ( int index ) const
For looking up parts of the class by name and dynamic introspection.
That's the part that requires the running of their "moc" tool, the
Meta-Object compiler. It scans through headers and picks out that sort
of information.
Ok, you're probably now saying, "yeh, but that's not statically
typesafe, and my implementation is!". You're right, sometimes you do
want static type-safety. But sometimes you'd rather have loose dynamic
coupling and runtime type-safety.
Here's where I get a little hand-wavy, but this dynamic binding is very
useful for writing GUIs (and generally any component system that needs
loose coupling). QtDesigner is Trolltech's GUI builder:
http://www.trolltech.com/products/qt/features/designer
It takes advantage of all the introspection capabilities offered by the
QMetaObject that lives in every component. You can point it to a gui
widget you wrote, and it immediately can show all that widget's
properties, signals, and signalable methods (slots), and you can add
that widget to your GUI and start hooking methods together.
Also it means that at run-time, you can safely try to connect to slots
that may or may not be there. If the target doesn't have that slot, no
harm done. And you don't need to know anything about the object at
compile time other than it's a QObject. Loose coupling.
I think you can get similar results in pure C++ with a lot of templates
plus the requirement that users call some sort of method for every
function or property they want to have dynamically callable:
registerSlot(foo, "foo(int,int)")
I think the CEGUI library (www.cegui.org.uk) is now using something like
that approach. But obviously it requires a lot less maintenance if that
is handled for you automatically, because in C++ the place you call the
registerSlot() method always ends up being separated from the place
where you actual declare the foo method. Qt's "slot:" decorator keyword
basically lets you "register" the method at the place of declaration by
tagging it with one word.
All this is not to say that Qt S&S is the best way. Qt's design is
constrained ultimately by having to work with C++. Hence the separate
"moc" compiler. In the end Qt's QMetaObject provides a certain, fairly
limited amount of dynamic functionality. But as pointed out in the
other thread, something like Objective-C provides a much more general
messaging mechanism. From that you can easily build Qt-like S&S or a
dozen other loose coupling solutions.
I think railroading Qt's S&S into a language is the wrong approach.
What goes into the language should be a more general mechanism on top of
which schemes like dynamic S&S can be easily built.
--bb
More information about the Digitalmars-d
mailing list