March 12, 2018 Re: Generic Property Implementation | ||||
---|---|---|---|---|
| ||||
Posted in reply to Simen Kjærås | On Monday, 12 March 2018 at 09:54:20 UTC, Simen Kjærås wrote: > > 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. > Sure, you have. https://dlang.org/spec/struct.html#assign-overload Point #4. In this case, ref S opAssign(ref S rhs) { return this; } Another point is, that I hope, that pointers don't move anywhere, as in C, by definition. > >> 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. > Yes. This is how the structs are meant to be, I thought :) > >> 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: > Ah... I was unclear, I think... > unittest { > auto tmp = typeof(S.a)(); > } I thought, this shouldn't be possible (at least in my mind) > > 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(); } > } > But this is clearly valid. > unittest { > // cannot access frame pointer of foo.S.a.SomeType!(delegate () => this.n).SomeType > auto tmp = typeof(S.a())(); > } > For sure, tmp cannot be defined without an instance of S. So the correct unittest in my eyes would be: unittest { S s; auto res = s.a; assert(res.get == S.init.n); } > -- > Simen |
March 12, 2018 Re: Generic Property Implementation | ||||
---|---|---|---|---|
| ||||
Posted in reply to Alex | On Monday, 12 March 2018 at 10:37:00 UTC, Alex wrote: > Sure, you have. > https://dlang.org/spec/struct.html#assign-overload > Point #4. > In this case, > > ref S opAssign(ref S rhs) > { > return this; > } True. Can you fix these, too? struct S { S* ptr; this(int dummy) { ptr = &this; } ~this() { assert(ptr == &this); } } void test(S s) {} unittest { test(S(0)); } S test2() { return S(0); } unittest { S s = test2(); } > Another point is, that I hope, that pointers don't move anywhere, as in C, by definition. And why not? D allows for moving garbage collectors. >> unittest { >> auto tmp = typeof(S.a)(); >> } > > I thought, this shouldn't be possible (at least in my mind) > >> 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(); } >> } >> > But this is clearly valid. Yes, it's an example of code that works in D today, with similar semantics to those implied in the other example. It's meant to show that just like `auto tmp = typeof(S.a())()` doesn't work today, it shouldn't work with the types used in my other example. >> unittest { >> // cannot access frame pointer of foo.S.a.SomeType!(delegate () => this.n).SomeType >> auto tmp = typeof(S.a())(); >> } >> > For sure, tmp cannot be defined without an instance of S. So the correct unittest in my eyes would be: > unittest { > S s; > auto res = s.a; > assert(res.get == S.init.n); > } I think we may be speaking past one another. Yes, your unittest would be expected to pass. If we go back to the example code: struct S1 { int n, m; SomeType!(() => n + m) a; } vs. struct S2 { int n, m; auto a() { return SomeType!(() => n + m)(); } } I would expect typeof(S1.a) and typeof(S2.a()) to be equivalent. I don't see a good reason why I should be able to construct an instance of typeof(S1.a), since I can't construct a typeof(S2.a()). I see your point that "The latter closures above the current values inside of [S2]". I'm saying I want the former to do the same. I understand that it currently doesn't, and I argue that having it do the same would be a much more useful behavior. Apart from the fact it's impossible, I don't see any good reason not to make it work. :p -- Simen |
March 12, 2018 Re: Generic Property Implementation | ||||
---|---|---|---|---|
| ||||
Posted in reply to Simen Kjærås | On Monday, 12 March 2018 at 13:04:54 UTC, Simen Kjærås wrote: > On Monday, 12 March 2018 at 10:37:00 UTC, Alex wrote: >> Sure, you have. >> https://dlang.org/spec/struct.html#assign-overload >> Point #4. >> In this case, >> >> ref S opAssign(ref S rhs) >> { >> return this; >> } > > True. Can you fix these, too? > > struct S { > S* ptr; > this(int dummy) { > ptr = &this; > } > ~this() { > assert(ptr == &this); > } > } > > void test(S s) {} > > unittest { > test(S(0)); > } > > S test2() { > return S(0); > } > > unittest { > S s = test2(); > } > ok... this is a little bit more tricky, as there is no assignment now. :) But in this two cases I assume, you want to have explicit pass by reference, no? struct S { S* ptr; this(int dummy) { ptr = &this; } ~this() { assert(ptr == &this); } } void test(ref S s){} unittest { auto s = S(0); test(s); /* if I call test(S(0)) with void test(S s) as in your example, neither the postblit nor opAssign is called... hm... no idea why... */ } auto test2() { return new S(0); } unittest { auto s = test2(); /* This example differs more from the post before :) test2 is a true factory now, it has to grant the right setting of all members... */ } >> Another point is, that I hope, that pointers don't move anywhere, as in C, by definition. > > And why not? D allows for moving garbage collectors. If it were allowed, then "contiguous memory allocation" for arrays would be senseless. > >>> unittest { >>> auto tmp = typeof(S.a)(); >>> } >> >> I thought, this shouldn't be possible (at least in my mind) >> >>> 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(); } >>> } >>> >> But this is clearly valid. > > Yes, it's an example of code that works in D today, with similar semantics to those implied in the other example. It's meant to show that just like `auto tmp = typeof(S.a())()` doesn't work today, it shouldn't work with the types used in my other example. > > > >>> unittest { >>> // cannot access frame pointer of foo.S.a.SomeType!(delegate () => this.n).SomeType >>> auto tmp = typeof(S.a())(); >>> } >>> >> For sure, tmp cannot be defined without an instance of S. So the correct unittest in my eyes would be: >> unittest { >> S s; >> auto res = s.a; >> assert(res.get == S.init.n); >> } > > I think we may be speaking past one another. Yes, your unittest would be expected to pass. > > If we go back to the example code: > > struct S1 { > int n, m; > SomeType!(() => n + m) a; > } > > vs. > > struct S2 { > int n, m; > auto a() { return SomeType!(() => n + m)(); } > } > > I would expect typeof(S1.a) and typeof(S2.a()) to be equivalent. I don't see a good reason why I should be able to construct an instance of typeof(S1.a), since I can't construct a typeof(S2.a()). > > I see your point that "The latter closures above the current values inside of [S2]". I'm saying I want the former to do the same. I understand that it currently doesn't, and I argue that having it do the same would be a much more useful behavior. Apart from the fact it's impossible, I don't see any good reason not to make it work. :p I see your point too :) But the latter form just grants the existence of an instance of S, whereas the first form doesn't. By the way, this would work: struct S1 { static int n, m; // added a static here. SomeType!(() => n + m) a; } struct SomeType(alias fn){} > -- > Simen |
Copyright © 1999-2021 by the D Language Foundation