Generic Property Implementation

Simen Kjærås simen.kjaras at gmail.com
Mon Mar 12 09:54:20 UTC 2018


On Monday, 12 March 2018 at 08:59:49 UTC, Alex wrote:
> An incomplete type is perfectly ok, so there should be no 
> problem with a pointer of the same type inside a struct.
> If accidentally the pointer refers "this", then it must have 
> been set after construction. As before construction the value 
> of "this"-pointer does not exist and after the construction the 
> pointer to "this" has the default value of null.
>
> Therefore, you are aware of this circumstance and appropriate 
> movings of the pointer value are also up to you.

But I don't have a hook to update the pointer when the struct is 
moved. The GC may move my struct without informing me in any way. 
In fact, even just a copy construction will break this:

struct S {
     S* ptr;
     this(int dummy) {
         ptr = &this;
     }
     ~this() {
         // This assert will fail.
         assert(ptr == &this);
     }
}

unittest {
     S s1 = S(0);
     S s2 = S(0);

     assert(&s1 == s1.ptr);
     assert(&s2 == s2.ptr);

     s1 = s2;
}

The reason is copy construction makes a copy[1] of the lhs, then 
performs the copy construction[2]. If the copy construction[2] 
succeeds, the copy[1] is destroyed. If not, the copy[1] is 
blitted back over the original, to give the impression that 
nothing ever happened.

When the destructor is called on the copy[1], &this returns a 
different address from what it did before, since it's a copy and 
logically resides at a different address.


> struct SomeType(alias fn) {}
>
> is (or has to be) lowered to something like
>
> struct SomeType
> {
>   typeof(fn)* fn;
> }
>
> Even if fn contains a frame pointer to S it is perfectly legal 
> to have such a type. SomeType would contain a delegate then.

Indeed. But stack frames aren't copied or moved the way structs 
are.


> However, I think, the syntax
>
>>> struct S {
>>>     int n, m;
>>>     SomeType!(() => n + m) a;
>>> }
>
> is still invalid and
>
>>> struct S {
>>>     int n, m;
>>>     auto a() { return SomeType!(() => n + m)(); }
>>> }
>
> has another semantics.
>
> The latter closures above the current values inside of S.
> The former has to be constructible without the context, as it 
> is perfectly legal to write
>
> struct Outer
> {
> 	struct Inner{}
> }
>
> void main()
> {
>   auto i = Outer.Inner();
> }
>
> but would be impossible with this syntax.

I'm not using any inner structs in my examples. SomeType is 
assumed to be a separate type that knows nothing about S. If 
you're saying this should work:

unittest {
     auto tmp = typeof(S.a)();
}

It wouldn't, and such code is not possible in D today:

struct S {
     int n;
     auto a() { return SomeType!(() => n)(); }
}

struct SomeType(alias fn) {
     int get() { return fn(); }
}

unittest {
    // cannot access frame pointer of foo.S.a.SomeType!(delegate 
() => this.n).SomeType
    auto tmp = typeof(S.a())();
}

--
   Simen


More information about the Digitalmars-d-learn mailing list