January 31, 2013
On Thursday, 31 January 2013 at 13:58:44 UTC, Zach the Mystic wrote:
> I hope this isn't the final word on the topic.

I found this article which I think explains the reasoning behind non-zero sized structs even better:

http://bytes.com/topic/c/insights/660463-sizeof-empty-class-structure-1-a
January 31, 2013
On Thursday, 31 January 2013 at 12:58:13 UTC, TommiT wrote:
> [..] So, it might be just as well to just disallow copying of 'properties' except when it happens as a part of copying the enclosing object.

Or... maybe not, because disallowing making a copy of a property variable makes it illegal to pass it by value as a templated argument:

struct S
{
    int n;

    property Prop
    {
        @property int get() { return outer.n; }
        alias this = get;
    }
    Prop prop;
}

void foo(int v) {}

void bar(T)(T t)
    if (isImplicitlyConvertible!(T,int))
{
    int v = t;
}

...

S s;

foo(s.prop); // ok: calling foo(s.prop.get)
bar(s.prop); // error: cannot make a copy of a
             // property variable of type S.Prop
January 31, 2013
On Thursday, 31 January 2013 at 14:47:17 UTC, TommiT wrote:
> [..]

Although, it's just not very healthy to be passing those property variables around, because it enables writing all kinds of bugs:

struct S
{
    int n;

    property Prop
    {
        @property int get() { return outer.n; }
        alias this = get;

        Prop opAssign(string S : "+")(int v)
        {
            outer.n += v;
            return this;
        }
    }
    Prop prop;
}

void foo(T)(T t)
    if (isImplicitlyConvertible!(T,int))
{
    auto v = t;
    v += 1; // calls t.prop.opAssign!"+"(1)
            // given: is(T == S.Prop)
}

...

S s;

foo(s.prop); // increments S.n, not good

So, I guess properties need to be magic, they're not imaginable as a some kind of restricted + augmented structs. The crux of the matter is the alias this which is needed for implementing the getter (accessor) of the property. The property variable should *not* be convertible to getter, but the property variable itself should be the getter.
January 31, 2013
On Thursday, 31 January 2013 at 15:18:36 UTC, TommiT wrote:
> [..] So, I guess properties need to be magic, [..]

So, going back to the concept of property as a crossbreed between a namespace and a variable. Here's my new take on it:

* Property looks like a namespace when it is defined, and a variable when it is used.

* Property definition creates its own lexical scope.

* Properties can't hold any variables, don't take up any memory, and you can't take the address of a property.

* Properties may have a special opGet operator that will be called eagerly whenever a naked property is used, i.e. a property is used without applying any such operator or member function that is defined for that particular property.

I don't know if properties really need to have member functions, but I wouldn't want to rule them out right off the bat.

struct T
{
    private int value;

    bool isCorrect() const
    {
        return false;
    }

    property myProp
    {
        // the special "eager casting operator" of properties
        int opGet() const
        {
            return value;
        }

        void opAssign(int v)
        {
            value = v;
            // doesn't have to return anything
        }

        void opAssign(string s : "+")(int v)
        {
            // 'this' refers to the enclosing object
            this.value += v;
        }

        // function attribute 'const' refers to the
        // constness of the enclosing object
        bool isCorrect() const
        {
            return value == 42;
        }

        ref T sayWhat()
        {
            // isCorrect refers to: this.myProp.isCorrect
            // ...and not to:       this.isCorrect
            assert(isCorrect());

            // 'this' refers to the enclosing object
            return this;
        }

        int data; // error: properties can't hold any variables
    }
}

void foo(T : int)(T t);

int fn(T.myProp p); // error: T.myProp is a property, not a type

...

T t;
foo(t.myProp);     // foo(t.myProp.opGet());
auto v = t.myProp; // int v = t.myProp.opGet();
t.myProp = 41;     // t.myProp.opAssign(41);
t.myProp += 1;     // t.myProp.opAssign!"+"(41);
assert(t.myProp.isCorrect());

immutable T t2;
t2.myProp.sayWhat(); // error: cannot call a non-const property
                     // method of an immmutable variable t2
January 31, 2013
On Thursday, 31 January 2013 at 16:44:03 UTC, TommiT wrote:
> T t;
> foo(t.myProp);     // foo(t.myProp.opGet());
> auto v = t.myProp; // int v = t.myProp.opGet();
> t.myProp = 41;     // t.myProp.opAssign(41);
> t.myProp += 1;     // t.myProp.opAssign!"+"(41);
> assert(t.myProp.isCorrect());
>
> immutable T t2;
> t2.myProp.sayWhat(); // error: cannot call a non-const property
>                      // method of an immmutable variable t2

(CONT'D)

t.myProp *= 2; // error: can't assign to an rvalue int

It tries to call:

t.myProp.opGet() *= 2;

...because T.myProp doesn't define a *= operator. So, it becomes one of those "naked" uses of a property.
January 31, 2013
On Wednesday, 30 January 2013 at 18:36:17 UTC, Dmitry Olshansky wrote:
> I have one key problem - the hidden pointer detail. In other words how should it find the instance of the outer struct to to access it?
>
> struct A{
> 	int a;
> 	struct B{
> 		void foo(){ a = 42; }
> 	}	
> 	B b;
> }
>
> A a;
> a.b.foo(); //how that b is supposed to know it's outer struct without the hidden pointer?
>
> auto x = a.b;
> x.foo();// and now what?

 Been thinking about that. I can only think that inner (non-static) struts cannot be returned outside of their parent. Passing them to something is fine, but they in turn can't return them anywhere either.

 Actually they likely can't be set anywhere outside the ownership of the struct period; That would allow flexibility where we don't have it.

  //above struct example

  class C {
    A.B b; //Error, context pointer goes to type of A
           //need root struct.
  }

  A.B func();           //Error!
  ref A.B func();       //Error!
  A.B* func();          //@system only, advanced programmers
  void func(A.B b);     //acceptable
  void func(ref A.B b); //acceptable
  A func(A a);          //allowed

 Now if one inner struct passed to another of the same struct (but not instance of), the context pointer can only point to the one that now owns it. For POD and other stuff this is acceptable.

  struct S {
    struct Inner{}
    Inner[] x;
  }

  S s1;
  S s2;

  s2.x = s1.x.dup; //allowed, same structure but
                   //s2 owns it, pointers updated

  Although it seems limiting, it actually would be quite useful. I have a sorting algorithmn I'm testing with that would benefit from not being forced to use static. Seeing as the inner struct never leaves the outer struct...

  struct MCS {
    Range[] ranges;
    static struct Range{
      MCS *parent; //manually managed. More error prone.
    }
  }
February 01, 2013
On Thursday, 31 January 2013 at 16:44:03 UTC, TommiT wrote:
> So, going back to the concept of property as a crossbreed between a namespace and a variable. Here's my new take on it:
>
> * Property looks like a namespace when it is defined, and a variable when it is used.
>
> * Property definition creates its own lexical scope.
>
> * Properties can't hold any variables, don't take up any memory, and you can't take the address of a property.
>
> * Properties may have a special opGet operator that will be called eagerly whenever a naked property is used, i.e. a property is used without applying any such operator or member function that is defined for that particular property.
>
> I don't know if properties really need to have member functions, but I wouldn't want to rule them out right off the bat.
>
> struct T
> {
>     private int value;
>
>     bool isCorrect() const
>     {
>         return false;
>     }
>
>     property myProp
>     {
>         // the special "eager casting operator" of properties
>         int opGet() const
>         {
>             return value;
>         }
>
>         void opAssign(int v)
>         {
>             value = v;
>             // doesn't have to return anything
>         }
>
>         void opAssign(string s : "+")(int v)
>         {
>             // 'this' refers to the enclosing object
>             this.value += v;
>         }
>
>         // function attribute 'const' refers to the
>         // constness of the enclosing object
>         bool isCorrect() const
>         {
>             return value == 42;
>         }
>
>         ref T sayWhat()
>         {
>             // isCorrect refers to: this.myProp.isCorrect
>             // ...and not to:       this.isCorrect
>             assert(isCorrect());
>
>             // 'this' refers to the enclosing object
>             return this;
>         }
>
>         int data; // error: properties can't hold any variables
>     }
> }
>
> void foo(T : int)(T t);
>
> int fn(T.myProp p); // error: T.myProp is a property, not a type
>
> ...
>
> T t;
> foo(t.myProp);     // foo(t.myProp.opGet());
> auto v = t.myProp; // int v = t.myProp.opGet();
> t.myProp = 41;     // t.myProp.opAssign(41);
> t.myProp += 1;     // t.myProp.opAssign!"+"(41);
> assert(t.myProp.isCorrect());
>
> immutable T t2;
> t2.myProp.sayWhat(); // error: cannot call a non-const property
>                      // method of an immmutable variable t2

Here's some more elaboration of the interplay between 1) "naked" property use, 2) property methods/operators and 3) the methods/operators of the type which "naked" property use returns through the opGet operator:

struct Speed
{
    int _value;

    void opOpAssign(string op)(int rhs)
        if (op == "+")
    {
        _value += rhs;
        assert(1 <= _value && _value <= 4);
    }
}

struct Sneak
{
    int _value;

    void opOpAssign(string op)(int rhs)
        if (op == "+")
    {
        _value += rhs;
        assert(1 <= _value && _value <= 4);
    }
}

struct Character
{
    private Speed _speed;
    private Sneak _sneak;

    property speed
    {
        ref Speed opGet()
        {
            return _speed;
        }

        void opOpAssign(string op)(int rhs)
            if (op == "+")
        {
            _speed += rhs;
            assert(_speed._value + _sneak._value <= 5);
        }
    }

    property sneak
    {
        ref Sneak opGet()
        {
            return _sneak;
        }
    }
}

void main()
{
    Character c;

    c.speed += 1; // calls: c.speed.opOpAssign!"+"(1);
    c.sneak += 1; // calls: c.sneak.opGet() += 1;
                  // i.e.   c._sneak.opOpAssign!"+"(1);
}

As far as I know, the obove kind of thing isn't possible with the current state of affairs; with @property attribute, you cannot provide the extra level of encapsulation that checks that the sum of 'speed' and 'sneak' isn't above 5.
February 01, 2013
On Friday, 1 February 2013 at 14:49:48 UTC, TommiT wrote:
> As far as I know, the obove kind of thing isn't possible with the current state of affairs; with @property attribute, you cannot provide the extra level of encapsulation that checks that the sum of 'speed' and 'sneak' isn't above 5.

...and if the specification is changed so that:

struct S
{
    T _prop;

    @property T prop() const
    {
        return _prop;
    }

    @property void prop(T t)
    {
        assert(t <= 5);
        _prop = t;
    }
}
...
S s;
s.prop += 3;

...gets lowered to s.prop = (s.prop + 3); Then that sucks because now T is forced to provide the binary + operator.
February 01, 2013
On Friday, February 01, 2013 16:03:41 TommiT wrote:
> On Friday, 1 February 2013 at 14:49:48 UTC, TommiT wrote:
> > As far as I know, the obove kind of thing isn't possible with the current state of affairs; with @property attribute, you cannot provide the extra level of encapsulation that checks that the sum of 'speed' and 'sneak' isn't above 5.
> 
> ...and if the specification is changed so that:
> 
> struct S
> {
>      T _prop;
> 
>      @property T prop() const
>      {
>          return _prop;
>      }
> 
>      @property void prop(T t)
>      {
>          assert(t <= 5);
>          _prop = t;
>      }
> }
> ...
> S s;
> s.prop += 3;
> 
> ...gets lowered to s.prop = (s.prop + 3); Then that sucks because now T is forced to provide the binary + operator.

And why is that a problem? If you expect s.pop += 3 to work, how could you not expect + to work on the type of prop? You're generally doing something wrong if your type defines += and not + anyway, so I really don't see the problem here.

- Jonathan M Davis
February 01, 2013
On Friday, 1 February 2013 at 15:14:55 UTC, Jonathan M Davis wrote:
> [..] You're generally doing something wrong
> if your type defines += and not + anyway, so I really don't see the problem
> here.

Okay, I suppose you're right. But that still leaves the possible performance overhead with s.prop = (s.prop + 3). For example, if prop is an array of values, there's no point in making a copy of it, when you could more effectively just add 3 to each element of the array.