www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - refRange with non copyable struct

reply Jerry <Kickupx gmail.com> writes:
Hello guys, so I wanted to have a noncopyable range on the stack.
So my thoughts was to make it non copyable and use refRange 
whenever I want to use it with map and others.

But I got a compiler warning when doing so like this:

import std.range;

void main() {
	NonCopyable v;
	
	refRange(&v);
}

struct NonCopyable
{
	 disable this(this);
	
	int data;
	
	enum empty = false;
	void popFront() {}
	int front() { return data; }
}





With the error message:

C:\D\dmd2\windows\bin\..\..\src\phobos\std\range\package.d(8941): 
Error: struct reproduction.NonCopyable is not copyable because it 
is annotated with  disable
C:\D\dmd2\windows\bin\..\..\src\phobos\std\range\package.d(8982): 
Error: mutable method reproduction.NonCopyable.front is not 
callable using a const object
C:\D\dmd2\windows\bin\..\..\src\phobos\std\range\package.d(9649): 
Error: template instance std.range.RefRange!(NonCopyable) error 
instantiating
reproduction.d(6):        instantiated from here: 
refRange!(NonCopyable)




Is there any workaround?
Is this a bug?
Apr 17 2017
parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Monday, April 17, 2017 17:23:32 Jerry via Digitalmars-d-learn wrote:
 Hello guys, so I wanted to have a noncopyable range on the stack.
 So my thoughts was to make it non copyable and use refRange
 whenever I want to use it with map and others.

 But I got a compiler warning when doing so like this:

 import std.range;

 void main() {
   NonCopyable v;

   refRange(&v);
 }

 struct NonCopyable
 {
    disable this(this);

   int data;

   enum empty = false;
   void popFront() {}
   int front() { return data; }
 }





 With the error message:

 C:\D\dmd2\windows\bin\..\..\src\phobos\std\range\package.d(8941):
 Error: struct reproduction.NonCopyable is not copyable because it
 is annotated with  disable
 C:\D\dmd2\windows\bin\..\..\src\phobos\std\range\package.d(8982):
 Error: mutable method reproduction.NonCopyable.front is not
 callable using a const object
 C:\D\dmd2\windows\bin\..\..\src\phobos\std\range\package.d(9649):
 Error: template instance std.range.RefRange!(NonCopyable) error
 instantiating
 reproduction.d(6):        instantiated from here:
 refRange!(NonCopyable)




 Is there any workaround?
 Is this a bug?
Well, I don't think that much range-based code in general is going to work with a disabled postblit constructor, and it's not something that's generally tested for unless someone is specifically trying to use such a type with a specific piece of code. Non-copyable types tend to wreak havoc with things - not that they shouldn't necessarily work, but most stuff tends to assume that types are copyable, and supporting non-copyable often complicates things quite a bit. Most of Phobos simply hasn't been tested with non-copyable types even if the functionality in question should arguably work with them. In this particular case, it looks like the main problem is RefRange's opAssign. For it to work, the type needs to be copyable. It might be reasonable for RefRange to be enhanced so that it doesn't compile in opAssign if the range isn't copyable, but I'd have to study RefRange in depth to know what the exact consequences of that would be, since it's been quite a while since I did anything with it. My guess is that such a change would be reasonable, but I don't know without studying it. - Jonathan M Davis
Apr 17 2017
next sibling parent Jerry <Kickupx gmail.com> writes:
On Monday, 17 April 2017 at 18:07:36 UTC, Jonathan M Davis wrote:
 Non-copyable types tend to wreak havoc with things
 - Jonathan M Davis
Basicly what I use this for is to combine RAII with ranges. Which I find quite useful when doing DB queries and the data is lazily fetched since this allows me to guarantee that the query is "closed" and another query can take place.
Apr 17 2017
prev sibling parent reply Jerry <Kickupx gmail.com> writes:
On Monday, 17 April 2017 at 18:07:36 UTC, Jonathan M Davis wrote:
 In this particular case, it looks like the main problem is 
 RefRange's opAssign. For it to work, the type needs to be 
 copyable. It might be reasonable for RefRange to be enhanced so 
 that it doesn't compile in opAssign if the range isn't 
 copyable, but I'd have to study RefRange in depth to know what 
 the exact consequences of that would be, since it's been quite 
 a while since I did anything with it. My guess is that such a 
 change would be reasonable, but I don't know without studying 
 it.

 - Jonathan M Davis
I took a look on RefRange and the reasoning is clearly explained in the docs like this: This does not assign the pointer of $(D rhs) to this $(D RefRange). Rather it assigns the range pointed to by $(D rhs) to the range pointed to by this $(D RefRange). This is because $(I any) operation on a RefRange) is the same is if it occurred to the original range. The exception is when a $(D RefRange) is assigned $(D null) either or because $(D rhs) is $(D null). In that case, $(D RefRange) longer refers to the original range but is $(D null). But what I do not understand is why this is important.
Apr 17 2017
parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Monday, April 17, 2017 18:45:46 Jerry via Digitalmars-d-learn wrote:
 On Monday, 17 April 2017 at 18:07:36 UTC, Jonathan M Davis wrote:
 In this particular case, it looks like the main problem is
 RefRange's opAssign. For it to work, the type needs to be
 copyable. It might be reasonable for RefRange to be enhanced so
 that it doesn't compile in opAssign if the range isn't
 copyable, but I'd have to study RefRange in depth to know what
 the exact consequences of that would be, since it's been quite
 a while since I did anything with it. My guess is that such a
 change would be reasonable, but I don't know without studying
 it.

 - Jonathan M Davis
I took a look on RefRange and the reasoning is clearly explained in the docs like this: This does not assign the pointer of $(D rhs) to this $(D RefRange). Rather it assigns the range pointed to by $(D rhs) to the range pointed to by this $(D RefRange). This is because $(I any) operation on a RefRange) is the same is if it occurred to the original range. The exception is when a $(D RefRange) is assigned $(D null) either or because $(D rhs) is $(D null). In that case, $(D RefRange) longer refers to the original range but is $(D null). But what I do not understand is why this is important.
Because otherwise, it's not acting like a reference to the original range, which is the whole point of RefRange. The correct solution would probably be to disable opAssign in the case where the original range can't be overwritten by another range. - Jonathan M Davis
Apr 17 2017
parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Monday, 17 April 2017 at 19:00:44 UTC, Jonathan M Davis wrote:

 Because otherwise, it's not acting like a reference to the 
 original range, which is the whole point of RefRange. The 
 correct solution would probably be to  disable opAssign in the 
 case where the original range can't be overwritten by another 
 range.
This doesn't look quite right. References in D are rebindable. That is, assigning a reference to a reference does not copy referenced object, only the reference itself. It seems that RefRange is trying to impersonate a C++ reference.
Apr 17 2017
parent Jonathan M Davis via Digitalmars-d-learn writes:
On Monday, April 17, 2017 19:39:25 Stanislav Blinov via Digitalmars-d-learn 
wrote:
 On Monday, 17 April 2017 at 19:00:44 UTC, Jonathan M Davis wrote:
 Because otherwise, it's not acting like a reference to the
 original range, which is the whole point of RefRange. The
 correct solution would probably be to  disable opAssign in the
 case where the original range can't be overwritten by another
 range.
This doesn't look quite right. References in D are rebindable. That is, assigning a reference to a reference does not copy referenced object, only the reference itself. It seems that RefRange is trying to impersonate a C++ reference.
The term reference in D is a bit overloaded. The whole point of RefRange is to have the original range affected by everything that happens to the new range as if it were the original range - which is what happens with ref (which is not rebindable) except that ref only works on parameters and return types. So, yes, it is similar to a C++ reference. It's not trying to be a pointer, which is more like what a class reference is. - Jonathan M Davis
Apr 17 2017