digitalmars.D.learn - opApply with/without ref
- H. S. Teoh (48/48) Jan 12 2012 Hi all,
- bearophile (4/6) Jan 13 2012 Bye,
- H. S. Teoh (6/12) Jan 13 2012 [...]
- Steven Schveighoffer (18/43) Jan 13 2012 It's been around for a while. It was designed that way, I think without...
Hi all,
I'm experimenting with overloading foreach() with opApply, and I found
that this code doesn't compile:
	class C {
		void opApply(int delegate(uint n) cb) const {
			...
		}
	}
	unittest {
		auto c = new C;
		foreach (n; c) {
			...
		}
	}
I get this error:
Error: function intset.intset.opApply (int delegate(uint n) cb) is not
callable using argument types (int delegate(ref uint __applyArg0)  safe)
Eventually I found out that it's because the argument to the delegate
must be a ref, that is, opApply must be modified to read:
	void opApply(int delegate(ref uint n) cb) const {
		...
	}
This appears to be the case whether or not I write foreach(n; C) or
foreach(ref n; C). My question is, is this a compiler bug, or is this
intentional in the language? If the latter, why?
More to the point, I'm implementing a class which stores a set of
unsigned numbers in a compact form; I wrote an opApply() method to make
it easy to iterate over. However, the compact representation doesn't
allow arbitrary mutation to set elements. That is, the following is not
allowed:
	foreach (ref n; c) {
		n++;
	}
The problem is that this code compiles just fine, because there's no way
to say that opApply is only allowed for non-ref indices, but that it
doesn't do what it appears to say. The collection c is unchanged because
opApply() calls the delegate with a local variable: the number itself is
not actually stored in the data structure, so opApply() computes it
on-the-fly and hands it to the delegate. So modifying n in the foreach
body doesn't actually update the structure, even though it appears to.
How can I specify that only non-ref foreach loops are allowed for this
class? I thought that declaring opApply() the way I did at first (i.e.,
without the ref argument) is the correct way, but obviously the compiler
doesn't like it. (FWIW, I'm using GDC, I don't know if dmd has the same
behaviour.)
T
-- 
Lottery: tax on the stupid. -- Slashdotter
 Jan 12 2012
H. S. Teoh:void opApply(int delegate(ref uint n) cb) const {Use "const ref":void opApply(int delegate(const ref uint n) cb) const {Bye, bearophile
 Jan 13 2012
On Fri, Jan 13, 2012 at 07:36:40AM -0500, bearophile wrote:H. S. Teoh:[...] Thanks a lot! That works like a charm! T -- Let's not fight disease by killing the patient. -- Sean 'Shaleh' Perryvoid opApply(int delegate(ref uint n) cb) const {Use "const ref":void opApply(int delegate(const ref uint n) cb) const {
 Jan 13 2012
On Thu, 12 Jan 2012 22:49:49 -0500, H. S. Teoh <hsteoh quickfur.ath.cx>  
wrote:
 Hi all,
 I'm experimenting with overloading foreach() with opApply, and I found
 that this code doesn't compile:
 	class C {
 		void opApply(int delegate(uint n) cb) const {
 			...
 		}
 	}
 	unittest {
 		auto c = new C;
 		foreach (n; c) {
 			...
 		}
 	}
 I get this error:
 Error: function intset.intset.opApply (int delegate(uint n) cb) is not
 callable using argument types (int delegate(ref uint __applyArg0)  safe)
 Eventually I found out that it's because the argument to the delegate
 must be a ref, that is, opApply must be modified to read:
 	void opApply(int delegate(ref uint n) cb) const {
 		...
 	}
 This appears to be the case whether or not I write foreach(n; C) or
 foreach(ref n; C). My question is, is this a compiler bug, or is this
 intentional in the language? If the latter, why?
It's been around for a while.  It was designed that way, I think without  
knowing how bad it would be.  Back then, there was no const, so it  
probably wasn't imagined how it would affect things.
What the compiler does when you do foreach without a ref is transform your  
non-ref foreach body into a ref one (it essentially just makes a temporary  
copy of the ref coming in).
This means if you don't actually want the data to change via foreach, you  
have to make an lvalue copy in your opApply, pass it to the ref delegate,  
which will then be copied into a local variable.  It's a ridiculous  
workaround of D.
I've filed a bug on it a long time ago (2008), I think it has been fixed  
in git head last December, I haven't tested it.
http://d.puremagic.com/issues/show_bug.cgi?id=2443
If you feel adventurous, you could try compiling a git clone copy of dmd  
to see if it works.
-Steve
 Jan 13 2012








 
  
  
 
 "H. S. Teoh" <hsteoh quickfur.ath.cx>
 "H. S. Teoh" <hsteoh quickfur.ath.cx> 