Thread overview | |||||
---|---|---|---|---|---|
|
June 03, 2013 Re: Structs should not contain pointers to internal data | ||||
---|---|---|---|---|
| ||||
Attachments:
| Thank you @Ali and @Jonothan! So essentially since I will be storing a pointer, Telemetry!(T) is NOT safe to use only with structs in general. If I have something like: struct UsefulStruct2 { this(this) @disable; this(UsefulStruct2) @disable; this(ref const(UsefulStruct2)) @disable; ref UsefulStruct2 opAssign(UsefulStruct2) @disable; ref UsefulStruct2 opAssign(ref const(UsefulStruct2)) @disable; int importantValue; auto tel1 = Telemetry!int(importantValue); } Does that ensure that UsefulStruct2 is not relocateable and thus I can safely store a pointer to importantValue? If not, what constraints do I need to add to my classes to ensure that I don't run into subtle bugs when structs relocate? Regards, Saurabh Das |
June 03, 2013 Re: Structs should not contain pointers to internal data | ||||
---|---|---|---|---|
| ||||
Posted in reply to Saurabh Das | On 06/03/2013 05:26 AM, Saurabh Das wrote: > Thank you @Ali and @Jonothan! > > So essentially since I will be storing a pointer, Telemetry!(T) is NOT safe > to use only with structs in general. > > If I have something like: > > struct UsefulStruct2 > { > this(this) @disable; > this(UsefulStruct2) @disable; > this(ref const(UsefulStruct2)) @disable; > ref UsefulStruct2 opAssign(UsefulStruct2) @disable; > ref UsefulStruct2 opAssign(ref const(UsefulStruct2)) @disable; > > int importantValue; > auto tel1 = Telemetry!int(importantValue); > } > > Does that ensure that UsefulStruct2 is not relocateable and thus I can > safely store a pointer to importantValue? No. The compiler can still move a struct by blit (bit level transfer). Blit is based on good old memcpy. For a "copy", post-blit is for making corrections after the fact. On the other hand, the programmer cannot interfere if it is a "move". For example, rvalues are moved, e.g. to an array element as in the following example: import std.stdio; import std.array; struct UsefulStruct2 { this(this) @disable; this(UsefulStruct2) @disable; this(ref const(UsefulStruct2)) @disable; ref UsefulStruct2 opAssign(UsefulStruct2) @disable; ref UsefulStruct2 opAssign(ref const(UsefulStruct2)) @disable; int importantValue; int * p; } UsefulStruct2 makeObject(int i) { UsefulStruct2 u; u.importantValue = i; u.p = &u.importantValue; // <-- self-referencing return u; } void main() { auto arr = [ makeObject(1) ]; assert(arr.front.p != &arr.front.importantValue); // PASSES! } > If not, what constraints do I need to add to my classes to ensure that I > don't run into subtle bugs when structs relocate? As you see, @disable is cripling and not a solution for this. As far as I know, the only option is to observe this rule. I agree with you that a struct may become self-referencing, unknowingly and indirectly through members of other types. Ali |
June 03, 2013 Re: Structs should not contain pointers to internal data | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ali Çehreli | On Monday, 3 June 2013 at 16:00:58 UTC, Ali Çehreli wrote:
> On 06/03/2013 05:26 AM, Saurabh Das wrote:
>
> > Thank you @Ali and @Jonothan!
> >
> > So essentially since I will be storing a pointer,
> Telemetry!(T) is NOT safe
> > to use only with structs in general.
> >
> > If I have something like:
> >
> > struct UsefulStruct2
> > {
> > this(this) @disable;
> > this(UsefulStruct2) @disable;
> > this(ref const(UsefulStruct2)) @disable;
> > ref UsefulStruct2 opAssign(UsefulStruct2) @disable;
> > ref UsefulStruct2 opAssign(ref const(UsefulStruct2))
> @disable;
> >
> > int importantValue;
> > auto tel1 = Telemetry!int(importantValue);
> > }
> >
> > Does that ensure that UsefulStruct2 is not relocateable and
> thus I can
> > safely store a pointer to importantValue?
>
> No. The compiler can still move a struct by blit (bit level transfer). Blit is based on good old memcpy. For a "copy", post-blit is for making corrections after the fact. On the other hand, the programmer cannot interfere if it is a "move".
>
> For example, rvalues are moved, e.g. to an array element as in the following example:
>
> import std.stdio;
> import std.array;
>
> struct UsefulStruct2
> {
> this(this) @disable;
> this(UsefulStruct2) @disable;
> this(ref const(UsefulStruct2)) @disable;
> ref UsefulStruct2 opAssign(UsefulStruct2) @disable;
> ref UsefulStruct2 opAssign(ref const(UsefulStruct2)) @disable;
>
> int importantValue;
> int * p;
> }
>
> UsefulStruct2 makeObject(int i)
> {
> UsefulStruct2 u;
> u.importantValue = i;
> u.p = &u.importantValue; // <-- self-referencing
> return u;
> }
>
> void main()
> {
> auto arr = [ makeObject(1) ];
> assert(arr.front.p != &arr.front.importantValue); // PASSES!
> }
>
> > If not, what constraints do I need to add to my classes to
> ensure that I
> > don't run into subtle bugs when structs relocate?
>
> As you see, @disable is cripling and not a solution for this. As far as I know, the only option is to observe this rule.
>
> I agree with you that a struct may become self-referencing, unknowingly and indirectly through members of other types.
>
> Ali
You can get around this limitation by making a wrapper struct which uses special values to represent pointers which point within the containing struct, and does the conversion automatically when you dereference it.
|
Copyright © 1999-2021 by the D Language Foundation