digitalmars.D.learn - Potential strategy for avoiding problems with copy of a struct
- james.p.leblanc (30/30) Aug 22 2021 Hello,
- Mathias LANG (20/23) Aug 22 2021 ```
- jfondren (65/83) Aug 22 2021 Sorry, I don't follow this at all.
- james.p.leblanc (47/53) Aug 22 2021 Mattias, jfondren,
- jfondren (63/76) Aug 22 2021 I still really don't get what you're trying to do, to the point
- james.p.leblanc (35/42) Aug 22 2021 Dear jfondren,
- jfondren (3/23) Aug 22 2021 OK, rather than roll your own solution, Unique!Foo might do what
- =?UTF-8?Q?Ali_=c3=87ehreli?= (5/13) Aug 22 2021 As Matthias Lang mentioned, keeping a reference to itself makes a struct...
- james.p.leblanc (15/29) Aug 22 2021 Ali,
Hello, Question about a possible strategy to avoid problems with undesired/unintended copying of a structure: 1) We have a struct, call this **Foo**. 2) We instantiate it with, **x = Foo(a,b,c);** a. our constructor will initialize a field: **this.myadd = &this** b. this capture the address of our original "x" in x.myadd. 3) We wish to allow, any function calls using x, so we cannot disable Foo's this(this). 4) Our goal is to avoid problems with any accidental copying of x ... say by doing: **auto y=x;** 5) the copy of step 4 **does not use the constructor**, thus y.myadd would contain the address of x (and not y) 6) We can exploit that y.myadd does NOT contain its own address (but instead contains the address of x). This requires adding logic checks in any Foo opAssign, and other overloads. For example, we can disallow any such overloads by checking: if( &this != this.myadd ){ ... } 7) Needing to operate on x with other functions implies that private or const is not a solution. (I believe.) Some initial experiments lead me to believe this may acheive part of what I would like. But, it feels very "hackish" and ugly. Is there a better way? Best Regards, James
Aug 22 2021
On Sunday, 22 August 2021 at 07:58:12 UTC, james.p.leblanc wrote:Is there a better way? Best Regards, James``` public mixin template NonMovableOrCopyable () { disable this (); disable this (this); disable ref typeof (this) opAssign () (auto ref typeof(this) rhs); } ``` This will catch most mistakes at CT. However the language is technically free to copy / move structs at will (and interior pointers are forbidden). A recent enough version of LDC (>= v1.20.0 IIRC, might be v1.22.0) will do a very good job at not needlessly moving things around. See for example the discussion here: https://forum.dlang.org/thread/miuevyfxbujwrhghmiuw forum.dlang.org DMD on the other hand is much more likely to not perform NRVO / move things, so be wary of compiler differences.
Aug 22 2021
On Sunday, 22 August 2021 at 07:58:12 UTC, james.p.leblanc wrote:Hello, Question about a possible strategy to avoid problems with undesired/unintended copying of a structure: 1) We have a struct, call this **Foo**. 2) We instantiate it with, **x = Foo(a,b,c);** a. our constructor will initialize a field: **this.myadd = &this** b. this capture the address of our original "x" in x.myadd.Sorry, I don't follow this at all. Consider: ```d struct Foo { int a, b, c; Foo* myadd; this(int a, int b, int c) { myadd = &this; this.a = a; this.b = b; this.c = c; } } void main() { import std.stdio : writeln; Foo x; // initialized with (0, 0, 0, null) writeln(&x); // a memory location x = Foo(1, 2, 3); // (1, 2, 3, &x) writeln(&x); // the exact same memory location writeln(x.myadd.a); // 1, rather than 0, because &x==x.myadd } ``` There's no saving the original "x" here, it's overwritten by the assignment. Are you sure you don't want classes instead, to get reference semantics?4) Our goal is to avoid problems with any accidental copying of x ... say by doing: **auto y=x;** 5) the copy of step 4 **does not use the constructor**, thus y.myadd would contain the address of x (and not y)`auto y=x;` actually does call object lifetime functions. ```d struct CountCopies { int copies; this(this) { // postblit copies++; } } unittest { CountCopies x; auto y = x; auto z = y; assert(x.copies == 0); assert(y.copies == 1); assert(z.copies == 2); } ``` or with a copy constructor, ```d struct CountCopies { int copies; this(ref return scope const CountCopies rhs) { // copy constructor copies = rhs.copies + 1; } disable this(this); // helps pre-copy-constructor compilers reject this code } unittest { CountCopies x; auto y = x; auto z = y; assert(x.copies == 0); assert(y.copies == 1); assert(z.copies == 2); } ```Some initial experiments lead me to believe this may acheive part of what I would like. But, it feels very "hackish" and ugly. Is there a better way?If you don't get an answer that you like, I suggesting posting functional code and then stating your dissastisfactions with it.
Aug 22 2021
On Sunday, 22 August 2021 at 11:10:33 UTC, jfondren wrote:On Sunday, 22 August 2021 at 07:58:12 UTC, james.p.leblanc wrote:Mattias, jfondren, Thanks both for your replies, I always learn something from them. I've trimmed my code to a minimal example to give a better idea of my thinking on this. You will notice that to ensure that I "seed" x with its real address at initialization, I must add disable this() to my struct. import std.stdio; struct Foo { int a, b, c; Foo* myadd; this(int a, int b, int c) { this.a = a; this.b = b; this.c = c; this.myadd = &this; } disable this(); } void main() { // x is original, we wish to protect auto x = Foo(1, 2, 3); // y is the "bad copy", do not want operation on y to pollute original x auto y=x; writeln("&x: ",&x,", x.myadd: ",x.myadd,", the 2 agree, we have original!"); writeln("&y: ",&y,", y.myadd: ",y.myadd,", these 2 disagree (must be a bad copy!"); } Produces output: &x: **7FFC65C02CC8**, x.myadd: **7FFC65C02CC8**, the 2 agree, we have original! &y: 7FFC65C02CE8, y.myadd: **7FFC65C02CC8**, these 2 disagree (must be a bad copy! So, as stated, in my struct overloading I can check if my two values agree or not (exposing whether or not I have the original, or a copy), and react appropriately. I understand that the language allows circumvention of this method... but I just want to catch minor mistakes in programming. Again, all comments and thoughts are welcome. Best Regards, JamesHello,If you don't get an answer that you like, I suggesting posting functional code and then stating your dissastisfactions with it.
Aug 22 2021
On Sunday, 22 August 2021 at 13:03:20 UTC, james.p.leblanc wrote:On Sunday, 22 August 2021 at 11:10:33 UTC, jfondren wrote:I still really don't get what you're trying to do, to the point of wanting to know: are you making 'myadd' a field for this struct only so that you can check it against the address of the variable? Or is it a real example of data you want to protect from accidental copies that just, coincidentally, can also be used to check if the struct has been copied? And, still, are you sure that you want a struct and not a class? The idea of not polluting the original of a struct requires some reference types as members of the struct, or a pointer, as otherwise everything is copied and the original is perfectly 'protected' anyway. Here's a struct that 1. has an int* as an example of data that shouldn't be copied, for example because it would result in a double free on destruction 2. protects against bad copies, not by forbidding copies, but by nulling the int* with a postblit 3. can then report that it's an original vs. a copy by seeing if the int* is null ```d import std.stdio; import core.memory : pureMalloc, pureFree; struct Foo { int a, b, c; int* unique; this(int a, int b, int c) { this.a = a; this.b = b; this.c = c; unique = cast(int*) pureMalloc(int.sizeof); *unique = a * b * c; } void report() const { if (unique) writeln("I'm the original: ", *unique); else writeln("I am an incomplete copy :("); } this(this) { unique = null; } ~this() { pureFree(unique); } } void alwaysGetsACopy(Foo f) { assert(f.unique is null); } void reportIfOriginal(ref Foo f) { if (f.unique !is null) f.report; } void main() { auto x = Foo(1, 2, 3); auto y = x; x.report; // I'm an original y.report; // I'm a copy x.alwaysGetsACopy; // assertion succeeds x.reportIfOriginal; // I'm an original y.reportIfOriginal; } ```On Sunday, 22 August 2021 at 07:58:12 UTC, james.p.leblanc wrote:Mattias, jfondren, Thanks both for your replies, I always learn something from them. I've trimmed my code to a minimal example to give a better idea of my thinking on this.Hello,If you don't get an answer that you like, I suggesting posting functional code and then stating your dissastisfactions with it.
Aug 22 2021
On Sunday, 22 August 2021 at 13:37:50 UTC, jfondren wrote:this(this) { unique = null; } ~this() { pureFree(unique); } }Dear jfondren, I truly appreciate you taking the time to help me with my question! **This bit of magic with the postblit may hold the key to my issue.** (I have done some experiments based on your example code, am learning much, and it looks VERY encouraging! It is a bit scary how you guessed very closely what I am trying to do. I have a AVX aligned pointers (obtained from fftw_malloc), that I want to protect. To be a bit more specific. The code that reads/writes from to/from fftw routines is all pointer based. But, to allow use of standard dlang things such as "foreach" and friends, I've been cobbling together a (naive) wrapper. This allows me to use standard dlang syntax and operator overloads. I call my struct "fakeArray" ... since it is meant to behave either as a static array, or a dynamic array as needed. It **seems** to work pretty well ... but protecting the arrays from accidental programming mistakes (such as the "auto y=x") has been eluding me. In fact, I **can** do a legitimate data copy via "y = x", as long as y is already instantiated as a "fakeArray". I must steady more the example you kindly provided (learn more about the postblit, and pureFree, the const report function, etc...) Best Regards, James
Aug 22 2021
On Sunday, 22 August 2021 at 14:40:29 UTC, james.p.leblanc wrote:It is a bit scary how you guessed very closely what I am trying to do. I have a AVX aligned pointers (obtained from fftw_malloc), that I want to protect. To be a bit more specific. The code that reads/writes from to/from fftw routines is all pointer based. But, to allow use of standard dlang things such as "foreach" and friends, I've been cobbling together a (naive) wrapper. This allows me to use standard dlang syntax and operator overloads. I call my struct "fakeArray" ... since it is meant to behave either as a static array, or a dynamic array as needed. It **seems** to work pretty well ... but protecting the arrays from accidental programming mistakes (such as the "auto y=x") has been eluding me.OK, rather than roll your own solution, Unique!Foo might do what you want, from https://dlang.org/phobos/std_typecons.html#Unique
Aug 22 2021
On 8/22/21 6:03 AM, james.p.leblanc wrote:struct Foo { int a, b, c; Foo* myadd; this(int a, int b, int c) { this.a = a; this.b = b; this.c = c; this.myadd = &this;As Matthias Lang mentioned, keeping a reference to itself makes a struct object illegal in D. D sees structs as value types: It is supposed that any copy can be used in place of another copy. Ali
Aug 22 2021
On Sunday, 22 August 2021 at 14:35:48 UTC, Ali Çehreli wrote:On 8/22/21 6:03 AM, james.p.leblanc wrote:Ali, You highlight am important point that I have not been completely aware of. Indeed, I did have a few "warning bells" going off in my brain when I began typing "this.myadd = &this;" into my program ... but, it compiled, so I continued (into the potential quicksand, admittedly!) So, thanks for illuminating this issue for me. Also, the "postblit" hint recently posted in this thread, may allow me to procede along a safer path. Kind Regards, Jamesstruct Foo { int a, b, c; Foo* myadd; this(int a, int b, int c) { this.a = a; this.b = b; this.c = c; this.myadd = &this;As Matthias Lang mentioned, keeping a reference to itself makes a struct object illegal in D. D sees structs as value types: It is supposed that any copy can be used in place of another copy. Ali
Aug 22 2021