www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Frustrations with const

reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
I'm writing an AA implementation, and ran into this problem with the way
const behaves in D. Basically, I have an auxiliary function that
searches the internal hash table for a given key, and returns the slot
containing the matching entry, if found.

The problem is, how to write this function so that it can be called from
*both* a const public method and a non-const public method? Since the
method itself doesn't actually modify anything, it *should* in theory be
possible to mark it as const:

	const Slot *findSlot(Key key) { ... }

However, because the const applies to 'this', the compiler insists that
referencing anything via 'this', including reading a pointer to a slot,
must also be const, so it refuses to let the return type be Slot*; it
has to be const(Slot)*.

But this is silly, because now the caller isn't allowed to modify the
Slot either, so now I need to bloat the code with two identical copies
of findSlot, one with const, and one without (since if it wasn't marked
const, then a const method couldn't call it).

Is there any way to work around this?

Or more importantly, why does the compiler force all internal members of
this to be const inside a const method, when what it really should be
enforcing is that nothing is being *modified*? What's wrong with
extracting non-const members from the object in a const method, if you
never actually do anything to it?


T

-- 
Never wrestle a pig. You both get covered in mud, and the pig likes it.
Mar 08 2012
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 03/08/2012 08:09 PM, H. S. Teoh wrote:
 I'm writing an AA implementation, and ran into this problem with the way
 const behaves in D. Basically, I have an auxiliary function that
 searches the internal hash table for a given key, and returns the slot
 containing the matching entry, if found.

 The problem is, how to write this function so that it can be called from
 *both* a const public method and a non-const public method? Since the
 method itself doesn't actually modify anything, it *should* in theory be
 possible to mark it as const:

 	const Slot *findSlot(Key key) { ... }

 However, because the const applies to 'this', the compiler insists that
 referencing anything via 'this', including reading a pointer to a slot,
 must also be const, so it refuses to let the return type be Slot*; it
 has to be const(Slot)*.

 But this is silly, because now the caller isn't allowed to modify the
 Slot either, so now I need to bloat the code with two identical copies
 of findSlot, one with const, and one without (since if it wasn't marked
 const, then a const method couldn't call it).

 Is there any way to work around this?

 Or more importantly, why does the compiler force all internal members of
 this to be const inside a const method, when what it really should be
 enforcing is that nothing is being *modified*? What's wrong with
 extracting non-const members from the object in a const method, if you
 never actually do anything to it?


 T

inout(Slot)* findSlot(Key key) inout { ... }
Mar 08 2012
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Mar 08, 2012 at 08:22:05PM +0100, Timon Gehr wrote:
 On 03/08/2012 08:09 PM, H. S. Teoh wrote:

[...]
The problem is, how to write this function so that it can be called from
*both* a const public method and a non-const public method? Since the
method itself doesn't actually modify anything, it *should* in theory be
possible to mark it as const:

	const Slot *findSlot(Key key) { ... }

However, because the const applies to 'this', the compiler insists that
referencing anything via 'this', including reading a pointer to a slot,
must also be const, so it refuses to let the return type be Slot*; it
has to be const(Slot)*.

But this is silly, because now the caller isn't allowed to modify the
Slot either, so now I need to bloat the code with two identical copies
of findSlot, one with const, and one without (since if it wasn't marked
const, then a const method couldn't call it).


[...]
 inout(Slot)* findSlot(Key key) inout { ... }

Ahhh. Thanks! But that still doesn't solve the problem: inout(Slot)* findSlot(Key key) inout { auto slot = slots[hash(key)]; while (slot) { if (slot.hash == hash(key) && slot.key == key) return slot; // Error: cannot modify inout(Slot)* ---> slot = slot.next; } return null; } T -- Help a man when he is in trouble and he will remember you when he is in trouble again.
Mar 08 2012
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 08 Mar 2012 14:49:12 -0500, H. S. Teoh <hsteoh quickfur.ath.cx>  
wrote:

 On Thu, Mar 08, 2012 at 08:22:05PM +0100, Timon Gehr wrote:
 On 03/08/2012 08:09 PM, H. S. Teoh wrote:

[...]
The problem is, how to write this function so that it can be called  

from
*both* a const public method and a non-const public method? Since the
method itself doesn't actually modify anything, it *should* in theory  

be
possible to mark it as const:

	const Slot *findSlot(Key key) { ... }

However, because the const applies to 'this', the compiler insists that
referencing anything via 'this', including reading a pointer to a slot,
must also be const, so it refuses to let the return type be Slot*; it
has to be const(Slot)*.

But this is silly, because now the caller isn't allowed to modify the
Slot either, so now I need to bloat the code with two identical copies
of findSlot, one with const, and one without (since if it wasn't marked
const, then a const method couldn't call it).


[...]
 inout(Slot)* findSlot(Key key) inout { ... }

Ahhh. Thanks! But that still doesn't solve the problem: inout(Slot)* findSlot(Key key) inout { auto slot = slots[hash(key)];

What is type slot, and how is it constructed? This snippit isn't enough to provide help.
 		while (slot) {
 			if (slot.hash == hash(key) && slot.key == key)
 				return slot;

 			// Error: cannot modify inout(Slot)*

An exact message is preferrable.
 --->			slot = slot.next;
 		}
 		return null;
 	}

-Steve
Mar 08 2012
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Mar 08, 2012 at 02:50:50PM -0500, Steven Schveighoffer wrote:
 On Thu, 08 Mar 2012 14:49:12 -0500, H. S. Teoh
 <hsteoh quickfur.ath.cx> wrote:
 
On Thu, Mar 08, 2012 at 08:22:05PM +0100, Timon Gehr wrote:


[...]
inout(Slot)* findSlot(Key key) inout { ... }

Ahhh. Thanks! But that still doesn't solve the problem: inout(Slot)* findSlot(Key key) inout { auto slot = slots[hash(key)];

What is type slot, and how is it constructed? This snippit isn't enough to provide help.

[...] Slot is a struct, and slots is Slots*[]. But anyway, I found the problem. I needed to explicitly declare slot as: inout(Slot)* slot = ... because for whatever reason, auto turns it into inout(Slot*) which cannot be modified. I think inout is one of those things that really needs more thorough treatment for newbies, because coming from a C/C++ background I had no idea what was wrong. Now that I get it, it makes so much more sense. T -- Life would be easier if I had the source code. -- YHL
Mar 08 2012
next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 08 Mar 2012 15:06:19 -0500, H. S. Teoh <hsteoh quickfur.ath.cx>  
wrote:

 On Thu, Mar 08, 2012 at 02:50:50PM -0500, Steven Schveighoffer wrote:
 On Thu, 08 Mar 2012 14:49:12 -0500, H. S. Teoh
 <hsteoh quickfur.ath.cx> wrote:

On Thu, Mar 08, 2012 at 08:22:05PM +0100, Timon Gehr wrote:


[...]
inout(Slot)* findSlot(Key key) inout { ... }

Ahhh. Thanks! But that still doesn't solve the problem: inout(Slot)* findSlot(Key key) inout { auto slot = slots[hash(key)];

What is type slot, and how is it constructed? This snippit isn't enough to provide help.

[...] Slot is a struct, and slots is Slots*[]. But anyway, I found the problem. I needed to explicitly declare slot as: inout(Slot)* slot = ... because for whatever reason, auto turns it into inout(Slot*) which cannot be modified.

I was wondering, because your error message identified it as inout(Slot)*, which should be modifiable...
 I think inout is one of those things that really needs more thorough
 treatment for newbies, because coming from a C/C++ background I had no
 idea what was wrong. Now that I get it, it makes so much more sense.

It's kind of a new concept to D even (since 2.056). It's not completely fleshed out yet, Timon, Kenji, and Stuart have some great ideas on making it better. This article might help: http://drdobbs.com/blogs/cpp/231902461 I plan to write a const/inout article at some point. -Steve
Mar 08 2012
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 03/08/2012 09:06 PM, H. S. Teoh wrote:
 On Thu, Mar 08, 2012 at 02:50:50PM -0500, Steven Schveighoffer wrote:
 On Thu, 08 Mar 2012 14:49:12 -0500, H. S. Teoh
 <hsteoh quickfur.ath.cx>  wrote:

 On Thu, Mar 08, 2012 at 08:22:05PM +0100, Timon Gehr wrote:


[...]
 inout(Slot)* findSlot(Key key) inout { ... }

Ahhh. Thanks! But that still doesn't solve the problem: inout(Slot)* findSlot(Key key) inout { auto slot = slots[hash(key)];

What is type slot, and how is it constructed? This snippit isn't enough to provide help.

[...] Slot is a struct, and slots is Slots*[]. But anyway, I found the problem. I needed to explicitly declare slot as: inout(Slot)* slot = ... because for whatever reason, auto turns it into inout(Slot*) which cannot be modified.

Probably it does not. What is the type of the "slots" hashtable?
 I think inout is one of those things that really needs more thorough
 treatment for newbies, because coming from a C/C++ background I had no
 idea what was wrong. Now that I get it, it makes so much more sense.


 T

This is a general issue with how auto infers types. It would often be useful to get the head-mutable type out of the type deduction instead of the actual type.
Mar 09 2012
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Mar 09, 2012 at 11:24:46AM +0100, Timon Gehr wrote:
 On 03/08/2012 09:06 PM, H. S. Teoh wrote:
On Thu, Mar 08, 2012 at 02:50:50PM -0500, Steven Schveighoffer wrote:


[...]
What is type slot, and how is it constructed?  This snippit isn't
enough to provide help.

[...] Slot is a struct, and slots is Slots*[]. But anyway, I found the problem. I needed to explicitly declare slot as: inout(Slot)* slot = ... because for whatever reason, auto turns it into inout(Slot*) which cannot be modified.

Probably it does not. What is the type of the "slots" hashtable?

Here are the relevant declarations: struct Slot { Slot *next; hash_t hash; Key key; Value value; this(hash_t h, Key k, Value v) { hash = h; key = k; value = v; } } struct Impl { Slot*[] slots; size_t nodes; // Prevent extra allocations for very small AA's. Slot*[4] binit; } [...]
I think inout is one of those things that really needs more thorough
treatment for newbies, because coming from a C/C++ background I had
no idea what was wrong. Now that I get it, it makes so much more
sense.


[...]
 This is a general issue with how auto infers types. It would often be
 useful to get the head-mutable type out of the type deduction instead
 of the actual type.

Yeah, that would be very useful. T -- Tech-savvy: euphemism for nerdy.
Mar 09 2012
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Friday, 9 March 2012 at 15:30:50 UTC, H. S. Teoh wrote:
 Yeah, that would be very useful.

I think it was actually decided to do that... they made the change for templates already. PS: you might want to take a look at your mutt settings. Your replies seem to have a truncated References header which messes up threading in many clients.
Mar 09 2012
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Mar 09, 2012 at 04:39:13PM +0100, Adam D. Ruppe wrote:
[...]
 PS: you might want to take a look at your mutt settings.  Your replies
 seem to have a truncated References header which messes up threading
 in many clients.

Oh really? I don't think I changed any settings related to that. AFAICT it does generate the right references header. How do I fix it? P.S. I do have mutt set on mailing list mode when replying to this list, though, and I usually reply using 'L'. Does that make a difference? T -- Computerese Irregular Verb Conjugation: I have preferences. You have biases. He/She has prejudices. -- Gene Wirchenko
Mar 09 2012
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 03/08/2012 08:49 PM, H. S. Teoh wrote:
 On Thu, Mar 08, 2012 at 08:22:05PM +0100, Timon Gehr wrote:
 On 03/08/2012 08:09 PM, H. S. Teoh wrote:

[...]
 The problem is, how to write this function so that it can be called from
 *both* a const public method and a non-const public method? Since the
 method itself doesn't actually modify anything, it *should* in theory be
 possible to mark it as const:

 	const Slot *findSlot(Key key) { ... }

 However, because the const applies to 'this', the compiler insists that
 referencing anything via 'this', including reading a pointer to a slot,
 must also be const, so it refuses to let the return type be Slot*; it
 has to be const(Slot)*.

 But this is silly, because now the caller isn't allowed to modify the
 Slot either, so now I need to bloat the code with two identical copies
 of findSlot, one with const, and one without (since if it wasn't marked
 const, then a const method couldn't call it).


[...]
 inout(Slot)* findSlot(Key key) inout { ... }

Ahhh. Thanks! But that still doesn't solve the problem: inout(Slot)* findSlot(Key key) inout { auto slot = slots[hash(key)];

inout(Slot)* slot = slots[hash(key)];
 		while (slot) {
 			if (slot.hash == hash(key)&&  slot.key == key)
 				return slot;

 			// Error: cannot modify inout(Slot)*
 --->			slot = slot.next;
 		}
 		return null;
 	}



 T

Mar 08 2012