September 29, 2006
Bill Baxter wrote:
> 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.

I agree, and thanks for letting me know about the string matching. That'll become possible in D later when it gets more introspection abilities.
September 29, 2006
Bill Baxter skrev:
<snip>
> 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.
> 
Loose coupling also means that you can easily make a GUI in say some kind of XML-file. In this file the interface is defined, along with it's connections. A object schema if you like. But then you would need to be able to pass around classes, like in Object Pascal:
SomeClass createAndInit(SomeClass& aClass) {
  SomeClass foo = new aClass();
  foo.doComplexStuff();
  return foo;
}

Heaven sent for tools. Having an UI tool that manipulates a XML-file is way better than an UI tool that creates and modifies actual code. Especially when the user comes and modifies this code by hand later. And having localization in retargetable text files is just genius.

Hmm... writing a new UI framework, is that a smart idea? There are already dozens.


// Fredrik Olsson
September 29, 2006
Walter Bright skrev:
> Fredrik Olsson wrote:
>> I like what I see. But there is a problem, a signal is hereby identified by it's types only. In a real world scenario many signals will have the same types. Bith a keyUp and a keyDown signal will probably want to send  a key code of the same type.
> 
> I don't understand the problem.
Lets say you have a UI control that can emit two signals; Click and DoubleClick, both send the mouse button as argument.

enum MouseButton { LEFT = 0, RIGHT = 1, MIDDLE = 3 };

class MyControl {
  mixin Signal!(MouseButton);

  void myActualClick(MouseButton mb) {
    ...
    emit(mb);
  }

  void myActualDoubleClick(MouseButton mb) {
    ...
    emit(mb);
  }

}


For the signal targets it will be impossible to tell a click from a double click. Unless you pass a more arguments, but then you kind of loose the simple idea of connecting to listen to a single event signal.

But I think this is more easily solved using "informal interfaces" that can have optional methods, and object delegates listening for the events instead of complex S&S.


enum MouseButton { LEFT = 0, RIGHT = 1, MIDDLE = 3 };

interface MyControlDelegate {
  optional void click(MyControl, MouseButton);
  optional void doubleClick(MyControl, MouseButton);
  optional bool shouldEnable(MyControl) = true;
}

class MyControl {
  MyControlDelage delegate;

  ...

  void myActualClick(MouseButton mb) {
    ...
    delegate.click(this, mb);
  }

  void myActualDoubleClick(MouseButton mb) {
    ...
    delegate.doubleClick(this, mb);
  }

  void myActualTestForEnabled() {
    this.enabled = delegate.shouldEnable(this);
  }

}

class MyActualDelegate : MyControlDelegate {
  bool shouldEnable(MyControl) {
    return today() is TUESDAY;
  }
}

MyControl cnt = new MyControl();
cnt.delegate = new MyActualDelegate(); // Add "automagic" enabling.

The deleagtes will probably not be such specific objects, but rather some larger business logic objects.

So an "informal interface" is a interface of methods that could be implemented, not an interface of methods that must be implemented. The methods are virtual, so testing for implementation should be as easy as comparing for NULL in the VMT.



// Fredrik Olsson
September 29, 2006
Walter Bright wrote:
> Frank Benoit wrote:
>> Inverting the lsb will not, because it is also a valid ptr to the object.
>> Inverting the msb should always work. Well it should work in the way,
>> that it does not prevent the object from being deleted.
>> But how can it be tested, that the ptr is callable?
> 
> I don't think msb will work, either, as there's no guarantee the gc pool won't straddle the boundary.

If you consider that you probably don't want your 'hidden' pointers to be valid for objects they /didn't/ point to either, this gets harder...
I don't think such a simple scheme (XORing with something) can be guaranteed to work in the general case, unless you assume the GC pool spans at most half the address space. [1]
Once it gets to be over 2GB (on x86) I think there's basically no way to make this work.

[1]: If you *do* assume that, (void* p){ return 2 * start_of_gc_pool - cast(size_t)p; } should provide unique values guaranteed not to point to the GC pool as long as the original one did. And feeding the returned value back to it will return the original.


Maybe you could try splitting the pointer up in two parts, stored separately? (i.e. use more than size_t.sizeof bytes to store it)

This could be literally, storing the upper half and lower half of the address in different ints.

Another option is also two ints: one pseudo-random, the other pointer XOR the first.

Yet another one (I like this one, it's pretty much guaranteed to work):
Find some (ptr_bits/2)-bit address range that's guaranteed to not contain valid pointers. IIRC, both Windows and Linux use the upper GB or so for kernel address space, so the GC pool should never be located there on these OSs. Other OSs probably have something similar, if perhaps in a different location.
Then just store the upper and lower halves in separate ints, whose upper half ensure the total value is guaranteed to be int the OS-reserved part of the address space. (e.g. set the upper 16 bits to 1s, the lower 16 to the parts of the pointer stored)

Another variant of the "kernel-reserved address space" I just thought of: If you know that the OS the program is compiled on reserves the top 1 GB of address space for itself, store the top two bits of the pointer  set those to one in your stored pointer, and restore them before returning. Simpler and only uses 34 bits on a 32-bit computer. Of course, memory allocation granularity means you'll likely still allocate at least 5 or 8 bytes and thus still "waste" some bytes.

Or you could "just" implement introspection and update the GC to ignore non-pointers. Then cast the pointer to a size_t for storage so the GC ignores it :). This one will probably be the most work, but will also gives some side-benefits[2]. (I believe it's a long-standing feature request...)

[2]: Or is it the other way around and is this a side-benefit of implementing introspection? Not sure :).
September 29, 2006
Fredrik Olsson skrev:
> Walter Bright skrev:
>> Fredrik Olsson wrote:
<snip>
> So an "informal interface" is a interface of methods that could be implemented, not an interface of methods that must be implemented. The methods are virtual, so testing for implementation should be as easy as comparing for NULL in the VMT.
> 


A more complete example of how "informal interfaces" could work in reality:


/*
 *  An informal interface for delegates to Something
 *  If not implemented shouldDoStuff returns true.
 */
interface SomethingDelagete {

  optional bool shouldDoStuff(Somethin) = true;
  optional void willDoStuff(Something);
  optional void didDoStuff(Something);

}

/*
 *  The Something class.
 *  That will ask a delegate for permission, and in
 *  general keep it informed when doing stuff.
 *  If one is set that is.
 */
class Something {

  SomethingDelegate delegate;

  void doStuff() {
    if (delegate.shouldDoStuff(this)) {
      delegate.willDoStuff(this);
      writefln("Doing stuff");
      delegate.didDoStuff(this);
    }
  }

  char[] toString() { return "Foo"; }
}

/*
 *  An actual delegate that keeps track of Somethings doing.
 *  But ignores telling it what to do.
 */
class MyDelegate {
  void willDoStuff(Something s) {
    writefln(s.toString ~ " will do stuff");
  }
  void didDoStuff(Something s) {
    writefln(s.toString ~ " did do stuff");
  }
}

/*
 *  This will output:
 *    Foo will do stuff
 *    Doing stuff
 *    Foo did do stuff
 */
void main() {
  Something something = new Something();
  something.delegate = new MyDelegate();

  something.doStuff();
}


// Fredrik Olsson
September 29, 2006
The problem in general with hiding pointers is that it'll break with a moving garbage collector. You could work around this by 'pinning' the objects, but pinning objects long term is a bad idea.
September 29, 2006
Fredrik Olsson wrote:
> Walter Bright skrev:
>> Fredrik Olsson wrote:
>>> I like what I see. But there is a problem, a signal is hereby identified by it's types only. In a real world scenario many signals will have the same types. Bith a keyUp and a keyDown signal will probably want to send  a key code of the same type.
>>
>> I don't understand the problem.
> Lets say you have a UI control that can emit two signals; Click and DoubleClick, both send the mouse button as argument.
> 
> enum MouseButton { LEFT = 0, RIGHT = 1, MIDDLE = 3 };
> 
> class MyControl {
>   mixin Signal!(MouseButton);
> 
>   void myActualClick(MouseButton mb) {
>     ...
>     emit(mb);
>   }
> 
>   void myActualDoubleClick(MouseButton mb) {
>     ...
>     emit(mb);
>   }
> 
> }
> 
> 
> For the signal targets it will be impossible to tell a click from a double click. Unless you pass a more arguments, but then you kind of loose the simple idea of connecting to listen to a single event signal.

Ok, I see. The solution is straightforward:

class MyControl {
   mixin Signal!(MouseButton) Click;
   mixin Signal!(MouseButton) DoubleClick;

   void myActualClick(MouseButton mb) {
     ...
     Click.emit(mb);
   }

   void myActualDoubleClick(MouseButton mb) {
     ...
     DoubleClick.emit(mb);
   }
 }

You can have any number of signals in a class.
September 29, 2006
Walter Bright skrev:
<snip>
>> For the signal targets it will be impossible to tell a click from a double click. Unless you pass a more arguments, but then you kind of loose the simple idea of connecting to listen to a single event signal.
> 
> Ok, I see. The solution is straightforward:
> 
> class MyControl {
>    mixin Signal!(MouseButton) Click;
>    mixin Signal!(MouseButton) DoubleClick;
> 
>    void myActualClick(MouseButton mb) {
>      ...
>      Click.emit(mb);
>    }
> 
>    void myActualDoubleClick(MouseButton mb) {
>      ...
>      DoubleClick.emit(mb);
>    }
>  }
> 
> You can have any number of signals in a class.

I just must say I love the beauty of this. Simple, no magic added, and high readability. What more can anyone ask for?


// Fredrik Olsson
September 29, 2006
Walter Bright wrote:
> The problem in general with hiding pointers is that it'll break with a moving garbage collector. You could work around this by 'pinning' the objects, but pinning objects long term is a bad idea.

That's true of course. It's also through though that the current GC *doesn't* move, so it'll work for now. I think as long as a WeakReference class (or struct) that works with the current GC is provided in the same library as that GC itself it'll be fine.
In the general case, such a class *will* have to be tailored to the GC or the other way around.
I believe the latter is what Java does, it has a WeakReference (IIRC) class that the GC recognizes. A moving GC could still modify the pointer contained in such a class, while not considering it for reachability of the pointed-to object (and setting it to null when that object is collected).
I think that should also be implementable in Phobos, actually...
September 29, 2006
Chad J > wrote:
> Couldn't you also do weak pointers by XORing them with 0xFFFFFFFF (or, better yet, const size_t weakxor = -1), then XORing again before and after you need to operate on them?
> 
> Just thought I'd toss that out there.

What happens the day we're halfway up the virtual memory space?