Thread overview
Re: Dealing with property function and operator overloads
Oct 03, 2011
Andrej Mitrovic
Oct 03, 2011
Andrej Mitrovic
Oct 03, 2011
Andrej Mitrovic
Oct 03, 2011
bearophile
Oct 04, 2011
Jacob Carlborg
Oct 04, 2011
Andrej Mitrovic
Oct 05, 2011
Andrej Mitrovic
Oct 03, 2011
Andrej Mitrovic
October 03, 2011
Looks like I can use some D tricks for this:

import std.stdio;

struct Point
{
    int x, y;

    void opOpAssign(string op)(int rhs)
    {
        mixin ("x = x " ~ op ~ " rhs;");
        mixin ("y = y " ~ op ~ " rhs;");
    }
}

struct Wrapped(T)
{
    T payload;
    alias payload this;
    void delegate() dg;

    @property void changed(void delegate() dg)
    {
        this.dg = dg;
    }

    void opOpAssign(string op)(int rhs)
    {
        payload.opOpAssign!op(rhs);
        dg();
    }
}

struct Wrapper
{
    this(int x)
    {
        point.changed = &notifyChanged;
    }

    void notifyChanged()
    {
        writeln("changed!");
    }

    public Wrapped!Point point;
}


void main()
{
    auto wrap = Wrapper(1);
    wrap.point = Point(1, 1);
    assert(wrap.point == Point(1, 1));

    wrap.point += 1;
    assert(wrap.point == Point(2, 2));
}

Pretty cool. I might even be able to write a Wrapped() template that searches for all operator overloads of a type and creates forwarding functions.
October 03, 2011
Forgot to add an opAssign in Wrapped as well:
    void opAssign(T)(T rhs)
    {
        payload = rhs;
        dg();
    }

That takes care of assigning and ops that change the object's state.
October 03, 2011
On Mon, 03 Oct 2011 14:57:33 -0400, Andrej Mitrovic <andrej.mitrovich@gmail.com> wrote:

> Looks like I can use some D tricks for this:
>
> import std.stdio;
>
> struct Point
> {
>     int x, y;
>
>     void opOpAssign(string op)(int rhs)
>     {
>         mixin ("x = x " ~ op ~ " rhs;");
>         mixin ("y = y " ~ op ~ " rhs;");
>     }
> }

Probably slightly off topic, but be very careful with operator overloads without using constraints.

For example, I can do some weird things to your struct:

Point p;

p.opOpAssign!"*x; y+="(5);

I suspect operator overloads are going to be a large hole in the interface design of many objects, but at least they won't be exploitable once compiled.

-Steve
October 03, 2011
On 10/3/11, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
> Probably slightly off topic, but be very careful with operator overloads without using constraints.
>
> For example, I can do some weird things to your struct:
>
> Point p;
>
> p.opOpAssign!"*x; y+="(5);
>
> I suspect operator overloads are going to be a large hole in the interface design of many objects, but at least they won't be exploitable once compiled.
>
> -Steve
>

Generally when I post examples I won't add any constraints, this is to make it easier for other people to read the code. All of the ops have constraints in my code.
October 03, 2011
On Mon, 03 Oct 2011 16:07:19 -0400, Andrej Mitrovic <andrej.mitrovich@gmail.com> wrote:

> On 10/3/11, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
>> Probably slightly off topic, but be very careful with operator overloads
>> without using constraints.
>>
>> For example, I can do some weird things to your struct:
>>
>> Point p;
>>
>> p.opOpAssign!"*x; y+="(5);
>>
>> I suspect operator overloads are going to be a large hole in the interface
>> design of many objects, but at least they won't be exploitable once
>> compiled.
>>
>> -Steve
>>
>
> Generally when I post examples I won't add any constraints, this is to
> make it easier for other people to read the code. All of the ops have
> constraints in my code.

Even so, it's better for people who are not familiar with the language to see correct code vs. code open to exploitation.

I've argued in the past that we need some boilerplate constraints for operators (like isValidOpString).  Can't remember where that stands.

-Steve
October 03, 2011
On 10/3/11, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
> Even so, it's better for people who are not familiar with the language to see correct code vs. code open to exploitation.

I can see your point. I'll make some simple constraints when I post code samples like this from now on.
October 03, 2011
Steven Schveighoffer:

> I suspect operator overloads are going to be a large hole in the interface design of many objects, but at least they won't be exploitable once compiled.

Just after the introduction of the new operator overload syntax I have suggested to introduce strict tests to statically forbid strings that don't contain one of the correct accepted operators. Walter didn't appreciate this idea :-(

Bye,
bearophile
October 04, 2011
On 2011-10-03 20:57, Andrej Mitrovic wrote:
> Looks like I can use some D tricks for this:
>
> import std.stdio;
>
> struct Point
> {
>      int x, y;
>
>      void opOpAssign(string op)(int rhs)
>      {
>          mixin ("x = x " ~ op ~ " rhs;");
>          mixin ("y = y " ~ op ~ " rhs;");
>      }
> }
>
> struct Wrapped(T)
> {
>      T payload;
>      alias payload this;
>      void delegate() dg;
>
>      @property void changed(void delegate() dg)
>      {
>          this.dg = dg;
>      }
>
>      void opOpAssign(string op)(int rhs)
>      {
>          payload.opOpAssign!op(rhs);
>          dg();
>      }
> }
>
> struct Wrapper
> {
>      this(int x)
>      {
>          point.changed =&notifyChanged;
>      }
>
>      void notifyChanged()
>      {
>          writeln("changed!");
>      }
>
>      public Wrapped!Point point;
> }
>
>
> void main()
> {
>      auto wrap = Wrapper(1);
>      wrap.point = Point(1, 1);
>      assert(wrap.point == Point(1, 1));
>
>      wrap.point += 1;
>      assert(wrap.point == Point(2, 2));
> }
>
> Pretty cool. I might even be able to write a Wrapped() template that
> searches for all operator overloads of a type and creates forwarding
> functions.


No "alias this" in the wrapper?

-- 
/Jacob Carlborg
October 04, 2011
On 10/4/11, Jacob Carlborg <doob@me.com> wrote:
> No "alias this" in the wrapper?

I shouldn't have named it Wrapper, you could better think of it as a Widget with a position field, if that position is changed (and not just read from) then some kind of repaint mechanism would be invoked.

However I shouldn't invoke such a mechanism if the value of the payload hasn't actually changed, so I should have a check inside of opAssign/opOpAssign. Additionally, "payload" might have its own functions which change its internal state. So to work around this, I would have to introduce opDispatch as well. Here's what I have so far:

http://codepad.org/uSEMcD0g

In the sample the Point structure is missing operator overloads, but if it had them they would be trapped by TrackChanges.
October 05, 2011
Also I can use a forward function for toString, I didn't realize this until now:
string toString() { return to!string(payload); }