www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Using non-copyable types

reply Yuxuan Shui <yshuiv7 gmail.com> writes:
When I try to use non-copyable types, I just get all sorts of 
resistance from the standard library.

For example, most of the std.range functions assume the range is 
copyable. std.typecons.Tuple assume all of its types are copyable.

I tried to use the new copy constructor feature to make a wrapper 
that moves by default. But apparently copy constructors are 
ignored when postblit is disabled.

How can we make using non-copyable types easier?
May 23 2019
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, May 23, 2019 at 06:05:41PM +0000, Yuxuan Shui via Digitalmars-d wrote:
 When I try to use non-copyable types, I just get all sorts of
 resistance from the standard library.
 
 For example, most of the std.range functions assume the range is
 copyable.  std.typecons.Tuple assume all of its types are copyable.
 
 I tried to use the new copy constructor feature to make a wrapper that
 moves by default. But apparently copy constructors are ignored when
 postblit is disabled.
 
 How can we make using non-copyable types easier?
Wrap them in a reference. I.e., instead of passing the range itself, pass a pointer to the range. Or use a suitable wrapper struct with reference semantics. Thanks to D unifying member invocation via object and via pointer to object, this should be mostly transparent to Phobos algorithms. This gets around the complications with postblits / copy ctors and Phobos assumptions about copyability. NonCopyableRange r = ...; auto rp = &r; result = rp.map!(...).filter!(...).joiner; // etc. T -- When solving a problem, take care that you do not become part of the problem.
May 23 2019
next sibling parent reply Yuxuan Shui <yshuiv7 gmail.com> writes:
On Thursday, 23 May 2019 at 18:18:02 UTC, H. S. Teoh wrote:
 On Thu, May 23, 2019 at 06:05:41PM +0000, Yuxuan Shui via 
 Digitalmars-d wrote:
 [...]
Wrap them in a reference. I.e., instead of passing the range itself, pass a pointer to the range. Or use a suitable wrapper struct with reference semantics. Thanks to D unifying member invocation via object and via pointer to object, this should be mostly transparent to Phobos algorithms. This gets around the complications with postblits / copy ctors and Phobos assumptions about copyability. NonCopyableRange r = ...; auto rp = &r; result = rp.map!(...).filter!(...).joiner; // etc. T
What about Tuple? These are probably not the best examples, I just sense there is a general lack of consideration of non-copyable types in phobos.
May 23 2019
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, May 23, 2019 at 06:33:04PM +0000, Yuxuan Shui via Digitalmars-d wrote:
 On Thursday, 23 May 2019 at 18:18:02 UTC, H. S. Teoh wrote:
 On Thu, May 23, 2019 at 06:05:41PM +0000, Yuxuan Shui via Digitalmars-d
 wrote:
 [...]
Wrap them in a reference. I.e., instead of passing the range itself, pass a pointer to the range. Or use a suitable wrapper struct with reference semantics. Thanks to D unifying member invocation via object and via pointer to object, this should be mostly transparent to Phobos algorithms. This gets around the complications with postblits / copy ctors and Phobos assumptions about copyability. NonCopyableRange r = ...; auto rp = &r; result = rp.map!(...).filter!(...).joiner; // etc.
[...]
 What about Tuple?
Probably have to create a new Tuple that references the uncopyable object in the original.
 These are probably not the best examples, I just sense there is a
 general lack of consideration of non-copyable types in phobos.
Certainly, because non-copyable types were added to the language after the fact. Like so many additions that people clamor for, they add many complications and unexpected interactions with existing language features, and also break assumptions Phobos was originally predicated upon, thereby introducing subtle bugs and/or corner cases that didn't exist when the code was written. Basically, Phobos was written assuming that ranges are mutable and assignable, and that .init exists and is valid. When one or more of these is false, you'll get a lot of unexpected behaviours. The .init stuff may have been fixed by now, but I wouldn't be surprised if there are still places where this assumption is still being made. The assignable part may have been mostly fixed, but there are still a lot of places where this is assumed. This is probably part of the reason W&A have very high standards when it comes to adding new features. Most of the time the people clamoring for the new feature fail to realize the full implications of adding it to the language. Breaking Phobos assumptions is one of the things people rarely think about, and it's particularly pernicious because the breakage is silent and nobody is aware until long after the new feature has been merged and stabilized, and it's too late to back out. T -- INTEL = Only half of "intelligence".
May 23 2019
prev sibling parent reply Yuxuan Shui <yshuiv7 gmail.com> writes:
On Thursday, 23 May 2019 at 18:18:02 UTC, H. S. Teoh wrote:
 On Thu, May 23, 2019 at 06:05:41PM +0000, Yuxuan Shui via 
 Digitalmars-d wrote:
 When I try to use non-copyable types, I just get all sorts of 
 resistance from the standard library.
 
 For example, most of the std.range functions assume the range 
 is copyable.  std.typecons.Tuple assume all of its types are 
 copyable.
 
 I tried to use the new copy constructor feature to make a 
 wrapper that moves by default. But apparently copy 
 constructors are ignored when postblit is disabled.
 
 How can we make using non-copyable types easier?
Wrap them in a reference. I.e., instead of passing the range itself, pass a pointer to the range. Or use a suitable wrapper struct with reference semantics. Thanks to D unifying member invocation via object and via pointer to object, this should be mostly transparent to Phobos algorithms. This gets around the complications with postblits / copy ctors and Phobos assumptions about copyability. NonCopyableRange r = ...; auto rp = &r; result = rp.map!(...).filter!(...).joiner; // etc. T
Somehow this trick doesn't work with fold: struct A { bool empty = false; int count = 0; int front = 1; void popFront() { count++; if (count > 100) empty = true; } } void main() { import std.range, std.stdio, std.array, std.algorithm; A range; auto x = (&range).fold!"a+b"(4); // doesn't work // auto x = range.fold!"a+b"(4); // works writeln(x); writeln(range.count); }
May 23 2019
parent reply Kagamin <spam here.lot> writes:
That's because foreach doesn't support pointer ranges.
May 24 2019
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, May 24, 2019 at 07:47:28AM +0000, Kagamin via Digitalmars-d wrote:
 That's because foreach doesn't support pointer ranges.
Aha, so that's where a wrapper reference range struct comes in handy. T -- First Rule of History: History doesn't repeat itself -- historians merely repeat each other.
May 24 2019