Thread overview
Dissecting the SS
Sep 28, 2006
Georg Wrede
Sep 28, 2006
J Duncan
Sep 29, 2006
Bradley Smith
Sep 29, 2006
Lutger
Sep 29, 2006
J Duncan
Sep 29, 2006
J Duncan
Sep 29, 2006
Lutger
September 28, 2006
Kyle Furlong wrote:
> Walter Bright wrote:
>> J Duncan wrote:
>>> also as a big fan of QT, Id like to request a S&S mechanism; or some
>>> sort of messaging pattern in the language. I think this would take D
>>> "over the top!"
>>
>> While I appreciate and enjoy the enthusiasm, this is deja vu all over
>> again. My entire career in compilers (C, C++, D, Javascript, etc.)
>> I've heard people say that "if only you implemented X, it will open
>> the floodgates!" It never does, but what does work is to work with
>> people who are *already* D users who are blocked by the lack of
>> something. With S&S, I'd like to see first how far it can be pushed
>> with existing D techniques.
>
> This last paragraph is why D will succeed. Walter, if this isn't the
> best way to evolve a language, I don't know what is.

There are a few excellent implementations already announced around here.

Something I haven't seen discussed is whether we should try to make an all-encompassing implementation, or maybe separate ones for the different situations where they might be used.

A stab at dissecting the field:

The environment where SS might be used might be multithreaded or not, the SS bindings might be entirely known at compile time or be dynamic at runtime, the participating classes may be known at compile time or only at runtime (e.g. plugins?).

That gives potentially eight separate use cases:

000 single thread entirely compile time known bindings and classes
001 multithread
010 bindings known only at runtime
011 multithread & bindings only known at runtime
100 classes only known at runtime
101 multithread & classes only known at runtime
110 both bindings & classes only known at runtime
111 multithread & both bindings & classes only known at runtime

Obviously 111 would be the "top". But even after it is implemented, can there be cases where some of the others would suffice and those cases be popular enough that implementing them too would be warranted (especially for simplicity and performance)?

I've played around a little with QT, but that definitely doesn't make me qualified to answer all of these. :-(

Case 000 would be usable (and sufficient) in round robin simulations.

The cases in between, can it be that some of them simply aren't needed? Or do people come up with use cases for each one?

---

I'm not seriously suggesting that we implement all 8 separately. But after this analysis we might get by with say 3 or 4 that should be popular enough to warrant consideration.

And of course, in any of the cases one can always use 111 instead (just that it would not be optimal in size and speed for that application).

---

One interesting thing is whether they should really use the same invocation syntax? The obvious answer is yes, but if we end up implementing only two or three of them (and especially while they're still library-only implementations), it may not be imperative at all that they'd get invoked alike. For example, if we end up with just 111 and 000 permanently, then a dissimilar syntax would in time make it obvious for the person reading the code which we are using. Knowing this might release us from coercing the syntax to be alike, especially if their implementations strongly suggest a dissimilar syntax.
September 28, 2006
// what QT-style SS would look like in D

class AClass
{
	void aSignal(int i);
}

class BClass
{
	void aSlot(int i) { writefln("signal triggered %s", i); }
}

void main()
{
	AClass a = new AClass;

	BClass b = new BClass;

	connect( a, "aSignal(int)", b, "aSlot(int)" );

	a.aSignal(10);	// -> writes "signal triggered 10"
}


i just wanted to give an example of how qt does it. the qt moc generates the code for the function 'aSignal', this gives it a feeling like its part of the language. the moc also generates code to map signal and slot names to addresses. they also provide basic named properties (named data members with set/get methods) this way. this results in a pretty nice system that allows things like automatic signal slot connections in gui classes based on names, (ex. the 'edit1' widget automatically sends a 'textChanged' signal to a slot on a parent widget called 'edit1_textChanged') i dont really have a point to make, i just wanted to give a simple example of qt.

September 29, 2006
J Duncan wrote:
> // what QT-style SS would look like in D
> 
> class AClass
> {
>     void aSignal(int i);
> }
> 
> class BClass
> {
>     void aSlot(int i) { writefln("signal triggered %s", i); }
> }
> 
> void main()
> {
>     AClass a = new AClass;
> 
>     BClass b = new BClass;
> 
>     connect( a, "aSignal(int)", b, "aSlot(int)" );
> 
>     a.aSignal(10);    // -> writes "signal triggered 10"
> }
> 
> 
> i just wanted to give an example of how qt does it. the qt moc generates the code for the function 'aSignal', this gives it a feeling like its part of the language. the moc also generates code to map signal and slot names to addresses. they also provide basic named properties (named data members with set/get methods) this way. this results in a pretty nice system that allows things like automatic signal slot connections in gui classes based on names, (ex. the 'edit1' widget automatically sends a 'textChanged' signal to a slot on a parent widget called 'edit1_textChanged') i dont really have a point to make, i just wanted to give a simple example of qt.
> 

Is there something more to the QT mechanism? I'm not a QT user, but I figure there must be more too it. Otherwise, why not use the following?

import std.stdio;

class AClass
{
//  void aSignal(int i);
    void delegate(int) aSignal;
}

class BClass
{
    void aSlot(int i) { writefln("signal triggered %s", i); }
}

void main()
{
    AClass a = new AClass;

    BClass b = new BClass;

    //connect( a, "aSignal(int)", b, "aSlot(int)" );
    a.aSignal = &b.aSlot;


    a.aSignal(10);    // -> writes "signal triggered 10"
}


  Bradley


September 29, 2006
Georg Wrede wrote:
<snip>
> A stab at dissecting the field:
> 
> The environment where SS might be used might be multithreaded or not, the SS bindings might be entirely known at compile time or be dynamic at runtime, the participating classes may be known at compile time or only at runtime (e.g. plugins?).
> 
> That gives potentially eight separate use cases:
> 
> 000 single thread entirely compile time known bindings and classes
> 001 multithread
> 010 bindings known only at runtime
> 011 multithread & bindings only known at runtime
> 100 classes only known at runtime
> 101 multithread & classes only known at runtime
> 110 both bindings & classes only known at runtime
> 111 multithread & both bindings & classes only known at runtime
> 
> Obviously 111 would be the "top". But even after it is implemented, can there be cases where some of the others would suffice and those cases be popular enough that implementing them too would be warranted (especially for simplicity and performance)?
<snip>

I'd say yes, but it will depend of how it is actually used. For a lot of cases, gui perhaps, performance is less important that runtime connections, but for some things it is the other way around. After profiling I noticed my non-tracking S&S is 2 to 3 times virtual function call and tracking is about 5 x the cost, a specialized template for non-tracking signals would reduce cost even more making it viable for more use cases.

Syntax matters too, templates can mean it's very clear for the user:

Signal!() signal;
signal ~= { writefln("hello world") };
signal();

For runtime connections, a little more needs to be done I guess.

I would divide the cases in two, each with it's own benefits:

1. Lightweight, compile-time type checked and high-performance. This may be similar to C# events and is basically a fancy container for delegates or (preferably) all callable types.

2. Heavy weight, dynamic, with introspection. Slower, this is the QT design.

I think it is possible perhaps to implement 2 on top of 1 with a minimum of fuss, using D's facilities for compile time string parsing.
September 29, 2006
Bradley Smith wrote:
> 
> Is there something more to the QT mechanism? I'm not a QT user, but I figure there must be more too it. Otherwise, why not use the following?
>

Yes, J Duncan posted is how it could look like in D. In QT you must declare signal and slots capable classes with a macro, then QT uses a custom preprocessor to do all the magic, here is an example from the docs:

class Foo : public QObject
{
    Q_OBJECT
public:
    Foo();
    int value() const { return val; }
public slots:
    void setValue( int );
signals:
    void valueChanged( int );
private:
    int val;
};

void Foo::setValue( int v )
{
    if ( v != val ) {
        val = v;
        emit valueChanged(v);
    }
}

Foo a, b;
connect(&a, SIGNAL(valueChanged(int)), &b, SLOT(setValue(int)));
b.setValue( 11 ); // a == undefined  b == 11
setValue( 79 ); // a == 79         b == 79
b.value();        // returns 79

http://doc.trolltech.com/3.3/signalsandslots.html


September 29, 2006

Bradley Smith wrote:
> J Duncan wrote:
> 
>> // what QT-style SS would look like in D
>>
>> class AClass
>> {
>>     void aSignal(int i);
>> }
>>
>> class BClass
>> {
>>     void aSlot(int i) { writefln("signal triggered %s", i); }
>> }
>>
>> void main()
>> {
>>     AClass a = new AClass;
>>
>>     BClass b = new BClass;
>>
>>     connect( a, "aSignal(int)", b, "aSlot(int)" );
>>
>>     a.aSignal(10);    // -> writes "signal triggered 10"
>> }
>>
>>
>> i just wanted to give an example of how qt does it. the qt moc generates the code for the function 'aSignal', this gives it a feeling like its part of the language. the moc also generates code to map signal and slot names to addresses. they also provide basic named properties (named data members with set/get methods) this way. this results in a pretty nice system that allows things like automatic signal slot connections in gui classes based on names, (ex. the 'edit1' widget automatically sends a 'textChanged' signal to a slot on a parent widget called 'edit1_textChanged') i dont really have a point to make, i just wanted to give a simple example of qt.
>>
> 
> Is there something more to the QT mechanism? I'm not a QT user, but I figure there must be more too it. Otherwise, why not use the following?
> 
> import std.stdio;
> 
> class AClass
> {
> //  void aSignal(int i);
>     void delegate(int) aSignal;
> }
> 
> class BClass
> {
>     void aSlot(int i) { writefln("signal triggered %s", i); }
> }
> 
> void main()
> {
>     AClass a = new AClass;
> 
>     BClass b = new BClass;
> 
>     //connect( a, "aSignal(int)", b, "aSlot(int)" );
>     a.aSignal = &b.aSlot;
> 
> 
>     a.aSignal(10);    // -> writes "signal triggered 10"
> }
> 
> 
>   Bradley
> 
> 

ok for one, in your example you have a 1 to 1 relationship between signals and slots; of course this can be done with built in arrays. but i think the key feature is that A doesnt have to know anything about B, only that its a qtObject - this provides a whole other level of anonymous polymorphism. you no longer care about interfaces or base classes, just if an object has a particular slot. i suppose the key element missing then would be class introspection.

yeah i found S&S rather silly before i used them, but they quickly become powerful tools. it seems especially useful in gui code which is heavily event driven. then you have really slick tool integration. many mundane tasks are eliminated. you end up just connecting various signals to slots in a rich class hierarchy to enable various behaviors.....
September 29, 2006
I also forgot to mention that slots need to disconnect from the signals when an object is deleted


J Duncan wrote:
> 
> 
> Bradley Smith wrote:
> 
>> J Duncan wrote:
>>
>>> // what QT-style SS would look like in D
>>>
>>> class AClass
>>> {
>>>     void aSignal(int i);
>>> }
>>>
>>> class BClass
>>> {
>>>     void aSlot(int i) { writefln("signal triggered %s", i); }
>>> }
>>>
>>> void main()
>>> {
>>>     AClass a = new AClass;
>>>
>>>     BClass b = new BClass;
>>>
>>>     connect( a, "aSignal(int)", b, "aSlot(int)" );
>>>
>>>     a.aSignal(10);    // -> writes "signal triggered 10"
>>> }
>>>
>>>
>>> i just wanted to give an example of how qt does it. the qt moc generates the code for the function 'aSignal', this gives it a feeling like its part of the language. the moc also generates code to map signal and slot names to addresses. they also provide basic named properties (named data members with set/get methods) this way. this results in a pretty nice system that allows things like automatic signal slot connections in gui classes based on names, (ex. the 'edit1' widget automatically sends a 'textChanged' signal to a slot on a parent widget called 'edit1_textChanged') i dont really have a point to make, i just wanted to give a simple example of qt.
>>>
>>
>> Is there something more to the QT mechanism? I'm not a QT user, but I figure there must be more too it. Otherwise, why not use the following?
>>
>> import std.stdio;
>>
>> class AClass
>> {
>> //  void aSignal(int i);
>>     void delegate(int) aSignal;
>> }
>>
>> class BClass
>> {
>>     void aSlot(int i) { writefln("signal triggered %s", i); }
>> }
>>
>> void main()
>> {
>>     AClass a = new AClass;
>>
>>     BClass b = new BClass;
>>
>>     //connect( a, "aSignal(int)", b, "aSlot(int)" );
>>     a.aSignal = &b.aSlot;
>>
>>
>>     a.aSignal(10);    // -> writes "signal triggered 10"
>> }
>>
>>
>>   Bradley
>>
>>
> 
> ok for one, in your example you have a 1 to 1 relationship between signals and slots; of course this can be done with built in arrays. but i think the key feature is that A doesnt have to know anything about B, only that its a qtObject - this provides a whole other level of anonymous polymorphism. you no longer care about interfaces or base classes, just if an object has a particular slot. i suppose the key element missing then would be class introspection.
> 
> yeah i found S&S rather silly before i used them, but they quickly become powerful tools. it seems especially useful in gui code which is heavily event driven. then you have really slick tool integration. many mundane tasks are eliminated. you end up just connecting various signals to slots in a rich class hierarchy to enable various behaviors.....