www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Dissecting the SS

reply Georg Wrede <georg.wrede nospam.org> writes:
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.
Sep 28 2006
next sibling parent reply J Duncan <me nospam.com> writes:
// 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.
Sep 28 2006
parent reply Bradley Smith <user domain.invalid> writes:
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
Sep 28 2006
next sibling parent Lutger <lutger.blijdestijn gmail.com> writes:
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
Sep 28 2006
prev sibling parent reply J Duncan <me nospam.com> writes:
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.....
Sep 28 2006
parent J Duncan <me nospam.com> writes:
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.....

Sep 28 2006
prev sibling parent Lutger <lutger.blijdestijn gmail.com> writes:
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)?

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.
Sep 28 2006