www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Safely moving structs in D

reply bitwise <bitwise.pvt gmail.com> writes:
Is it ok to memcpy/memmove a struct in D?

Quote from here:
https://dlang.org/spec/garbage.html

"Do not have pointers in a struct instance that point back to the 
same instance. The trouble with this is if the instance gets 
moved in memory, the pointer will point back to where it came 
from, with likely disastrous results."

This seems to suggests it's ok to move structs around in memory 
without calling their postblit...but if this is the case, why 
does postblit even exist, if it's not strictly guaranteed to be 
called after the struct has been blitted?
Jan 23 2017
next sibling parent reply bitwise <bitwise.pvt gmail.com> writes:
I'm confused about what the rules would be here.

It would make sense to call the postblit if present, but 
std.Array currently does not:
https://github.com/dlang/phobos/blob/04cca5c85ddf2be25381fc63c3e941498b17541b/std/container/array.d#L884
Jan 23 2017
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 01/23/2017 02:58 PM, bitwise wrote:
 I'm confused about what the rules would be here.

 It would make sense to call the postblit if present, but std.Array
 currently does not:
 https://github.com/dlang/phobos/blob/04cca5c85ddf2be25381fc63c3e941498b17541b/std/container/array.d#L884
Post-blit is for copying though. Moving should not call post-blit. You may want to look at the implementation of std.algorithm.move to see how it plays with post-blit: https://dlang.org/phobos/std_algorithm_mutation.html#.move Ali
Jan 23 2017
parent bitwise <bitwise.pvt gmail.com> writes:
On Monday, 23 January 2017 at 23:04:45 UTC, Ali Çehreli wrote:
 On 01/23/2017 02:58 PM, bitwise wrote:
 I'm confused about what the rules would be here.

 It would make sense to call the postblit if present, but 
 std.Array
 currently does not:
 https://github.com/dlang/phobos/blob/04cca5c85ddf2be25381fc63c3e941498b17541b/std/container/array.d#L884
Post-blit is for copying though. Moving should not call post-blit. You may want to look at the implementation of std.algorithm.move to see how it plays with post-blit: https://dlang.org/phobos/std_algorithm_mutation.html#.move Ali
That's a good point. It didn't click at first, but checking for postblit is done with 'hasElaborateCopyConstructor(T)'. I had thought that what memmove was doing would be considered "blitting", and hence require a postblit afterwards. I did look at std.move, but was mistaken about which code path was being taken. It seemed like structs that defined only a postblit would have been moved by assignment: https://github.com/dlang/phobos/blob/366f6e4e66abe96bca9fd69d03042e08f787d040/std/algorithm/mutation.d#L1310 But in actuality, the memcpy branch fires because hasElaborateAssign(T) returns true for structs with a postblit - which was unexpected. I don't really understand why, but this makes things clearer. Thanks
Jan 23 2017
prev sibling next sibling parent sarn <sarn theartofmachinery.com> writes:
On Monday, 23 January 2017 at 22:26:58 UTC, bitwise wrote:
 Is it ok to memcpy/memmove a struct in D?

 Quote from here:
 https://dlang.org/spec/garbage.html

 "Do not have pointers in a struct instance that point back to 
 the same instance. The trouble with this is if the instance 
 gets moved in memory, the pointer will point back to where it 
 came from, with likely disastrous results."

 This seems to suggests it's ok to move structs around in memory 
 without calling their postblit...but if this is the case, why 
 does postblit even exist, if it's not strictly guaranteed to be 
 called after the struct has been blitted?
You may need the postblit for a *copying* blit. For example, if a struct does reference counting, the postblit will need to increment the count for the new copy. It's a slightly different solution to what C++ solves with copy constructors, assignment operators, etc. Compared to C++, the D approach is a bit simpler and trades off a little flexibility for more opportunities for the compiler to generate efficient code. Here's the situation in C++, BTW: http://en.cppreference.com/w/cpp/language/rule_of_three
Jan 23 2017
prev sibling parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Monday, January 23, 2017 22:26:58 bitwise via Digitalmars-d-learn wrote:
 Is it ok to memcpy/memmove a struct in D?

 Quote from here:
 https://dlang.org/spec/garbage.html

 "Do not have pointers in a struct instance that point back to the
 same instance. The trouble with this is if the instance gets
 moved in memory, the pointer will point back to where it came
 from, with likely disastrous results."

 This seems to suggests it's ok to move structs around in memory
 without calling their postblit...but if this is the case, why
 does postblit even exist, if it's not strictly guaranteed to be
 called after the struct has been blitted?
Moving structs is fine. The postblit constructor is for when they're copied. A copy is unnecessary if the original isn't around anymore - e.g. passing an rvalue to a function can move the value; it doesn't need to copy it. Even passing an lvalue doesn't need to result in a copy if the lvalue is not referenced at any point after that function call. However, if you're going to end up with two distinct copies, then they need to actually be copies, and a postblit constructor will be called. Types that would need postblit constructors would include anything doing reference counting as well as anything that needs to do a deep copy of something on the heap (though such structs aren't a great idea, since usually copying is assumed to be cheap, and having the postblit constructor allocate on the heap isn't exactly cheap). In reality, I don't think that many structs typically have postblit constructors, but there are definitely use cases where they're needed. The bit about structs not pointing to themselves is to make it legal to move structs in cases where the compiler knows that only one copy is required, whereas in C++, because pointing to yourself is perfectly legal, the compiler has to do a lot more copying, and they had to introduce move constructors to get around the problem. - Jonathan M Davis
Jan 24 2017
parent bitwise <bitwise.pvt gmail.com> writes:
On Tuesday, 24 January 2017 at 11:46:47 UTC, Jonathan M Davis 
wrote:
 On Monday, January 23, 2017 22:26:58 bitwise via 
 Digitalmars-d-learn wrote:
 [...]
Moving structs is fine. The postblit constructor is for when they're copied. A copy is unnecessary if the original isn't around anymore - e.g. passing an rvalue to a function can move the value; it doesn't need to copy it. Even passing an lvalue doesn't need to result in a copy if the lvalue is not referenced at any point after that function call. However, if you're going to end up with two distinct copies, then they need to actually be copies, and a postblit constructor will be called. [...]
Awesome, thanks - this makes sense.
Jan 25 2017