www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - const - Best practices

reply Peter Alexander <peter.alexander.au gmail.com> writes:
Ok, so while I'm still not 100% satisfied with the lack of logical const 
in D, I'm willing to see how far I can get without bitwise const without 
going crazy, and I just want to clarify some things.

Andrei has commented a couple of times on the fact that D's const has 
more guarantees than C++'s const, and as a result you should use it a 
lot less often than you do in C++.

Questions:

1. What exactly does this mean? What are the situations where you should 
use const in C++, but not in D?

2. When designing base classes and interfaces, when should you mark a 
member function as const? In C++ you would do so for any accessor, as 
they would be logically const. In D, you can't assume that an accessor 
is bitwise const (due to lazy initialisation and caching), so when do 
you make it const in the base class, and when do you leave it mutable?

3. When should you take const references in template functions 
(essentially same subquestions as above).

Final note: my intention here is not to start another logical const vs. 
bitwise const war. I just want to know how the differences in const from 
C++ affect library design, and what the best practices for 
const-correctness are in D.

Thanks in advance.
Dec 31 2010
next sibling parent reply "Rob" <rob2970 yah00.com> writes:
"Peter Alexander" <peter.alexander.au gmail.com> wrote in message 
news:ifl719$2hph$1 digitalmars.com...
 Ok, so while I'm still not 100% satisfied with the lack of logical 
 const in D, I'm willing to see how far I can get without bitwise const 
 without going crazy, and I just want to clarify some things.

 Andrei has commented a couple of times on the fact that D's const has 
 more guarantees than C++'s const, and as a result you should use it a 
 lot less often than you do in C++.

 Questions:

 1. What exactly does this mean? What are the situations where you 
 should use const in C++, but not in D?

 2. When designing base classes and interfaces, when should you mark a 
 member function as const? In C++ you would do so for any accessor, as 
 they would be logically const. In D, you can't assume that an accessor 
 is bitwise const (due to lazy initialisation and caching), so when do 
 you make it const in the base class, and when do you leave it mutable?

 3. When should you take const references in template functions 
 (essentially same subquestions as above).

 Final note: my intention here is not to start another logical const vs. 
 bitwise const war. I just want to know how the differences in const 
 from C++ affect library design, and what the best practices for 
 const-correctness are in D.
What he said, and a matrix of when to use const vs. mutable and vice versa please.
Dec 31 2010
parent Jimmy Cao <jcao219 gmail.com> writes:
I'm wondering about this, too.
Dec 31 2010
prev sibling next sibling parent reply Peter Alexander <peter.alexander.au gmail.com> writes:
On 31/12/10 6:33 PM, Peter Alexander wrote:
 Ok, so while I'm still not 100% satisfied with the lack of logical const
 in D, I'm willing to see how far I can get without bitwise const without
 going crazy, and I just want to clarify some things.

 Andrei has commented a couple of times on the fact that D's const has
 more guarantees than C++'s const, and as a result you should use it a
 lot less often than you do in C++.

 Questions:

 1. What exactly does this mean? What are the situations where you should
 use const in C++, but not in D?

 2. When designing base classes and interfaces, when should you mark a
 member function as const? In C++ you would do so for any accessor, as
 they would be logically const. In D, you can't assume that an accessor
 is bitwise const (due to lazy initialisation and caching), so when do
 you make it const in the base class, and when do you leave it mutable?

 3. When should you take const references in template functions
 (essentially same subquestions as above).

 Final note: my intention here is not to start another logical const vs.
 bitwise const war. I just want to know how the differences in const from
 C++ affect library design, and what the best practices for
 const-correctness are in D.

 Thanks in advance.
Sorry for the bump, but there's at least three people here that would like an answer to this. Surely someone knows how to use const in D? :-) Additional question: why are none of Object's methods const, e.g. toString and toHash?
Jan 02 2011
next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday 02 January 2011 10:41:16 Peter Alexander wrote:
 On 31/12/10 6:33 PM, Peter Alexander wrote:
 Ok, so while I'm still not 100% satisfied with the lack of logical const
 in D, I'm willing to see how far I can get without bitwise const without
 going crazy, and I just want to clarify some things.
 
 Andrei has commented a couple of times on the fact that D's const has
 more guarantees than C++'s const, and as a result you should use it a
 lot less often than you do in C++.
 
 Questions:
 
 1. What exactly does this mean? What are the situations where you should
 use const in C++, but not in D?
 
 2. When designing base classes and interfaces, when should you mark a
 member function as const? In C++ you would do so for any accessor, as
 they would be logically const. In D, you can't assume that an accessor
 is bitwise const (due to lazy initialisation and caching), so when do
 you make it const in the base class, and when do you leave it mutable?
 
 3. When should you take const references in template functions
 (essentially same subquestions as above).
 
 Final note: my intention here is not to start another logical const vs.
 bitwise const war. I just want to know how the differences in const from
 C++ affect library design, and what the best practices for
 const-correctness are in D.
 
 Thanks in advance.
Sorry for the bump, but there's at least three people here that would like an answer to this. Surely someone knows how to use const in D? :-) Additional question: why are none of Object's methods const, e.g. toString and toHash?
http://d.puremagic.com/issues/show_bug.cgi?id=1824 For the const/immutable situation to be totally useable, several dmd bugs must be fixed. Some of the major ones are 1824 (Object is not const-correct), 3659 (opEquals() and const needs to be sorted out), 4867 (postblit doesn't work with const objects), and 3748 (inout is broken). The auto ref issue which came up recently when discussing the fact that const ref doesn't work with temporories needs to be resolved as well. There are a number of critical issues with const and immutable as things stand. The result is that that they can quickly become rather problematic to use correctly, if not outright impossible. There are definitely cases where const and immutable can and should be used, but you really have to want to use them at this point to be able to manage it, because they have a lot of problems. And until those problems are fixed, there's no way that we can know what the "best practices" of const in D will really be. To really understand that, we need to write code which uses const and immutable heavily and really see how it all works out. We can speculate on what we think will work well and point out areas where it's clear that there will be problems or which will pretty much need to use const in some manner (like opEquals()), but not enough people have been using const for there to be much experience with it, and those who have tried have consistently run into serious bugs which prevented them from using it like they'd like to. Another big thing is that Phobos as a whole is not const-correct. I don't believe that much attempt has been made to ensure that many of Phobos' functions work properly when const and immutable are used. The lack of tail-const for arbitrary ranges is one of the big open issues which has yet to be resolved. As I understdand it, there are some things work for arrays which will never work for other ranges. And some things have worked in the past because of compiler bugs. Such bugs have hidden issues (including issues relating to const) and made it look like const worked where it doesn't. Until Phobos is properly const- correct, there are a lot of cases where you're not going to be able to use const (this is also true for purity, though I believe that the situation is quite a bit worse for purity). I expect that there is going to have to be a definite focus on fixing bugs related to const and immutable in both the compiler and in Phobos before const and immutable will really be generally useable. It's on the TODO list, though it's not clear whether that or dynamic libraries is next after the 64-bit port (regardless, the 64-bit port is taking longer than expected so, it's going to be longer before the rest of the stuff in line gets focused on and fixed). Personally, I think that const should be used as much as it can be reasonably used. But it does generally preclude any kind of caching of values and the like that you could do with logical const (though you could do things like have a mutable version of a getter on top of a non-mutable one and have _it_ cache the value, so that if either it or the const version is called later and the value doesn't need to be recalculated, the const one can use the cached value and still gain some efficiency from caching). So, if you're expecting to have to cache values and the like, then don't use const. But honestly, in my experience in C++, I have almost never used mutable. And when I have, it's generally been for mutexes and the like (which won't be necessary in the same way in D). Doing things like caching calculations is extremely rare. So, the lack of logical const really wouldn't affect much from what I've seen. The transivity of const is what really affects things IMHO (things like returning a list of mutable objects from a const getter where that list is a member variable of the object aren't going to work in D, but they work just fine in C++ since the container itself isn't copied). I'm sure that there is code out there which does high-cost calculations on a regular basis and pretty much needs to be able to cache the results to be properly efficient, but that is not the norm. So, while some people are going to acutely feel the lack of logical const, a _lot_ of people are going to be able to use const in D very much like they do in C++ without having any issues with the lack of logical const. _Transivity_ may very well make it impossible to use const like they would have in C++, but the lack of logical const really isn't that big a deal in _most_ cases. - Jonathan M Davis
Jan 02 2011
parent reply Peter Alexander <peter.alexander.au gmail.com> writes:
On 2/01/11 10:43 PM, Jonathan M Davis wrote:
 http://d.puremagic.com/issues/show_bug.cgi?id=1824

 For the const/immutable situation to be totally useable, several dmd bugs must
 be fixed. Some of the major ones are 1824 (Object is not const-correct), 3659
 (opEquals() and const needs to be sorted out), 4867 (postblit doesn't work with
 const objects), and 3748 (inout is broken). The auto ref issue which came up
 recently when discussing the fact that const ref doesn't work with temporories
 needs to be resolved as well.

 There are a number of critical issues with const and immutable as things stand.
 The result is that that they can quickly become rather problematic to use
 correctly, if not outright impossible. There are definitely cases where const
and
 immutable can and should be used, but you really have to want to use them at
 this point to be able to manage it, because they have a lot of problems. And
 until those problems are fixed, there's no way that we can know what the "best
 practices" of const in D will really be. To really understand that, we need to
 write code which uses const and immutable heavily and really see how it all
 works out. We can speculate on what we think will work well and point out areas
 where it's clear that there will be problems or which will pretty much need to
 use const in some manner (like opEquals()), but not enough people have been
 using const for there to be much experience with it, and those who have tried
 have consistently run into serious bugs which prevented them from using it like
 they'd like to.

 Another big thing is that Phobos as a whole is not const-correct. I don't
 believe that much attempt has been made to ensure that many of Phobos'
functions
 work properly when const and immutable are used. The lack of tail-const for
 arbitrary ranges is one of the big open issues which has yet to be resolved. As
 I understdand it, there are some things work for arrays which will never work
 for other ranges. And some things have worked in the past because of compiler
 bugs. Such bugs have hidden issues (including issues relating to const) and
made
 it look like const worked where it doesn't. Until Phobos is properly const-
 correct, there are a lot of cases where you're not going to be able to use
const
 (this is also true for purity, though I believe that the situation is quite a
 bit worse for purity).

 I expect that there is going to have to be a definite focus on fixing bugs
related
 to const and immutable in both the compiler and in Phobos before const and
 immutable will really be generally useable. It's on the TODO list, though it's
 not clear whether that or dynamic libraries is next after the 64-bit port
 (regardless, the 64-bit port is taking longer than expected so, it's going to
be
 longer before the rest of the stuff in line gets focused on and fixed).

 Personally, I think that const should be used as much as it can be reasonably
 used. But it does generally preclude any kind of caching of values and the like
 that you could do with logical const (though you could do things like have a
 mutable version of a getter on top of a non-mutable one and have _it_ cache the
 value, so that if either it or the const version is called later and the value
 doesn't need to be recalculated, the const one can use the cached value and
 still gain some efficiency from caching). So, if you're expecting to have to
cache
 values and the like, then don't use const.

 But honestly, in my experience in C++, I have almost never used mutable. And
 when I have, it's generally been for mutexes and the like (which won't be
 necessary in the same way in D). Doing things like caching calculations is
 extremely rare. So, the lack of logical const really wouldn't affect much from
 what I've seen. The transivity of const is what really affects things IMHO
 (things like returning a list of mutable objects from a const getter where that
 list is a member variable of the object aren't going to work in D, but they
work
 just fine in C++ since the container itself isn't copied). I'm sure that there
is
 code out there which does high-cost calculations on a regular basis and pretty
 much needs to be able to cache the results to be properly efficient, but that
is
 not the norm. So, while some people are going to acutely feel the lack of
 logical const, a _lot_ of people are going to be able to use const in D very
 much like they do in C++ without having any issues with the lack of logical
 const. _Transivity_ may very well make it impossible to use const like they
 would have in C++, but the lack of logical const really isn't that big a deal
in
 _most_ cases.

 - Jonathan M Davis
Huge thanks Jonathan for writing that. Cheers. So basically, const-correctness in DMD is completely broken :-( I had no idea the situation was so dire, which in itself is a major problem. I don't envy people outside this NG that have no idea what's going on at all. I found this in one of those bugs. It's a post from Andrei, and I think it basically sums up my thoughts on const. --- begin quote I'm thinking of our current stance on const: if you don't care about const, don't use it and for the most part it won't intrude on you. For example, string's use of immutability is fairly confined. opEquals is a stark deviation from the stance above. It *will* intrude. The classic example is this: class Widget { bool opEquals(Widget); } Compiling this issues: Warning: object.Object.opEquals(Object o) is hidden by Widget It only gets downhill from there: struct Widget { bool opEquals(Widget) { return true; } } This time it's an error: Error: function test.Widget.opEquals type signature should be const bool(ref const(Widget)) not bool(ref Widget) So you simply can't "not care" about const. But then it's all getting viral because cons is viral. Consider the user grudgingly agrees to add const, and then... class Widget { private Gadget g; bool opEquals(const Widget rhs) { return compatibleGadgets(g, rhs.g); } } But now it's still not fine because compatibleGadgets is also written without caring about const. It's all a mess. Now consider that the user capitulates and decides to use const wherever applicable. Things will still not work in certain cases. For example if opEquals must compare members that are lazily computed, you can't make it compile. Cheating by casting away const will mess up a variety of assumptions. As an aside, I know what it takes to define lazily computed state to work with const, but Walter is at the bottom of a 5000 TeV potential hole that spells like "this is like C++ mutable and C++ mutable is incorrect, therefore I will not process any information henceforth". So I am unable to even start explaining that to him. Besides, assuming Walter is convinced of the correctness of the feature, it's unclear whether it will pull its weight. It will complicate the language, and the benefits, while there, are rather subtle. So I don't know what to do. --- end quote :-(
Jan 02 2011
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 1/3/11 12:06 AM, Peter Alexander wrote:
[quoting an older post of mine]
 As an aside, I know what it takes to define lazily computed state to
 work with
 const, but Walter is at the bottom of a 5000 TeV potential hole that spells
 like "this is like C++ mutable and C++ mutable is incorrect, therefore I
 will
 not process any information henceforth". So I am unable to even start
 explaining that to him. Besides, assuming Walter is convinced of the
 correctness of the feature, it's unclear whether it will pull its
 weight. It
 will complicate the language, and the benefits, while there, are rather
 subtle.
I am glad to announce that now Walter and me see eye to eye in the matter of lazily computable state. We also have a path to get there, but it's a fair amount of work. Andrei
Jan 03 2011
prev sibling next sibling parent "Rob" <rob2970 yah00.com> writes:
"Peter Alexander" <peter.alexander.au gmail.com> wrote in message 
news:ifqg7o$1hnp$1 digitalmars.com...

 Sorry for the bump, but there's at least three people here that would 
 like an answer to this. Surely someone knows how to use const in D? :-)
http://www.digitalmars.com/d/2.0/const-faq.html
Jan 02 2011
prev sibling parent reply Caligo <iteronvexor gmail.com> writes:
On Sun, Jan 2, 2011 at 4:43 PM, Jonathan M Davis <jmdavisProg gmx.com>wrote:

 - Jonathan M Davis
Almost a year has passed, and what is the state of const now? Is it still broken? I've been reading some threads on SO, and I'm a bit confused as to what's correct in my code and what's not.
Dec 31 2011
parent Mehrdad <wfunction hotmail.com> writes:
On 12/31/2011 9:02 AM, Caligo wrote:
 On Sun, Jan 2, 2011 at 4:43 PM, Jonathan M Davis <jmdavisProg gmx.com 
 <mailto:jmdavisProg gmx.com>> wrote:



     - Jonathan M Davis


 Almost a year has passed, and what is the state of const now?  Is it 
 still broken?  I've been reading some threads on SO, and I'm a bit 
 confused as to what's correct in my code and what's not.
AFAIK it's still broken. People will probably tell you otherwise, but last time I checked, as soon as things get 'atypical' (e.g. opApply with inout) it breaks.
Dec 31 2011
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, December 31, 2011 08:02:52 Caligo wrote:
 On Sun, Jan 2, 2011 at 4:43 PM, Jonathan M Davis <jmdavisProg gmx.com>wrote:
 - Jonathan M Davis
Almost a year has passed, and what is the state of const now? Is it still broken? I've been reading some threads on SO, and I'm a bit confused as to what's correct in my code and what's not.
Progress has definitely been made. inout finally works. Dynamic arrays are being changed to instantiate as tail-const with IFTI, so templated functions in Phobos will do much better with const (I think that the change is in github but not released yet; if not, it should be on github soon). A number of bugs have been fixed as well. But not everything has been sorted out yet. I believe that the two biggest issues are 1. Const-correctness. We have pull requests to fix this which are under discussion, but due to some of the non-trivial implications of the changes, they haven't been merged in yet. 2. Postblit constructors don't work with const or immutable (which makes a fair bit of sense when you consider how they work by memcpying and then changing the state of the copy). Walter and Andrei supposedly have a solution, but they haven't revealed what it is yet, let alone implemented it. So, the situation is definitely better than it was, and const is quite usable in plenty of circumstances, but there are still definitely outstanding issues which need to be resolved. Fortunately however, solutions for them _are_ in the works. - Jonathan M Davis
Dec 31 2011