www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Casting away const

reply simendsjo <simen.endsjo pandavre.com> writes:
I'm totally new to the const/immutable thing, so this might be a naive 
question..

The spec says:
"modification after casting away const" => "undefined behavior"

// ptr to const int
const(int)* p;
int* q = cast(int*)p;
*q = 3;   // undefined behavior

But why would you want to cast away const then? If it's for passing to a 
function that doesn't take const, you're just shooting yourself in the 
foot by giving it illegal data to work with.

This little test works:
unittest
{
	const int i = 10;
	const(int)* p = &i;
	assert(*p == 10);
	assert(p == &i);
	
	int* q = cast(int*)p;
	assert(q == &i);
	assert(*q == 10);
	
	*q = 1; // spec says undefined behavior
	assert(*q == 1); // but the value is changed
	assert(*p == 1); // but p is also changed
	
	assert(p == q); // still same reference.
	assert(q == &i);
	assert(p == &i);
	assert(i == 10); // i still 10 though.. How is this possible?
}
Aug 08 2010
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
simendsjo:
 But why would you want to cast away const then?

I presume for a situation where you are sure your code is just reading the information and not modifying it, and you need something that is not const.
 	assert(i == 10); // i still 10 though.. How is this possible?

You have done something that you are not supposed to do. Undefined behaviour means that it generally produces unexpected effects/results :-) Bye, bearophile
Aug 08 2010
prev sibling next sibling parent div0 <div0 sourceforge.net> writes:
On 08/08/2010 22:56, simendsjo wrote:
 I'm totally new to the const/immutable thing, so this might be a naive
 question..

 The spec says:
 "modification after casting away const" => "undefined behavior"

Casting away const is mostly for dealing with external non D libraries. Some languages have no notion of const, so you'll cast const away when calling functions written in other languages. Also a lot of old c & c++ code is none const correct so again you have to discard const to interface with that code. -- My enormous talent is exceeded only by my outrageous laziness. http://www.ssTk.co.uk
Aug 08 2010
prev sibling next sibling parent BCS <none anon.com> writes:
Hello simendsjo,

 I'm totally new to the const/immutable thing, so this might be a naive
 question..
 
 The spec says:
 "modification after casting away const" => "undefined behavior"
 // ptr to const int
 const(int)* p;
 int* q = cast(int*)p;
 *q = 3;   // undefined behavior
 But why would you want to cast away const then? If it's for passing to
 a function that doesn't take const, you're just shooting yourself in
 the foot by giving it illegal data to work with.
 
 This little test works:
 unittest
 {
 const int i = 10;
 const(int)* p = &i;
 assert(*p == 10);
 assert(p == &i);
 int* q = cast(int*)p;
 assert(q == &i);
 assert(*q == 10);
 *q = 1; // spec says undefined behavior

Your code at this point can do anything
 assert(*q == 1); // but the value is changed
 assert(*p == 1); // but p is also changed
 assert(p == q); // still same reference.
 assert(q == &i);
 assert(p == &i);
 assert(i == 10); // i still 10 though.. How is this possible?
 }

Because i is const int and initialized with a constant, the compiler is free to do things replace instances of i with the value rather than loading the value from ram. Allowing that sort of things is the reason for const and the undefined behavior bit is to prevent the compiler from needing to check for what you did. Off hand, other ways that your example could fail include: -the compiler putting i in a global variable (if this were in a recursive function...) -combining it with other global consts, or even code, that happen to have the same bit pattern as 10 (at which point you just messed up code that uses them) -putting it in a read only memory segment (and then "*q = 1" will seg-v) -- ... <IXOYE><
Aug 08 2010
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Sun, 08 Aug 2010 17:56:25 -0400, simendsjo <simen.endsjo pandavre.com>  
wrote:

 I'm totally new to the const/immutable thing, so this might be a naive  
 question..

 The spec says:
 "modification after casting away const" => "undefined behavior"

I thought it was "you're on your own", not undefined behavior. The former implies there is some "right" way to do this if you know more about the data than the compiler, the latter implies that there is no right way to cast away const. Am I wrong? -Steve
Aug 09 2010
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Steven Schveighoffer:
 I thought it was "you're on your own", not undefined behavior.  The former  
 implies there is some "right" way to do this if you know more about the  
 data than the compiler, the latter implies that there is no right way to  
 cast away const.  Am I wrong?

In my opinion if this thing is well designed then you go in undefined behaviour only when you change the contents of something after you have removed its const nature with a cast. Just casting const away and then reading the data can't be undefined behaviour, otherwise casting const away is useless and can be totally disallowed. Bye, bearophile
Aug 09 2010
next sibling parent Don <nospam nospam.com> writes:
Steven Schveighoffer wrote:
 On Mon, 09 Aug 2010 09:57:47 -0400, bearophile 
 <bearophileHUGS lycos.com> wrote:
 
 Steven Schveighoffer:
 I thought it was "you're on your own", not undefined behavior.  The 
 former
 implies there is some "right" way to do this if you know more about the
 data than the compiler, the latter implies that there is no right way to
 cast away const.  Am I wrong?

In my opinion if this thing is well designed then you go in undefined behaviour only when you change the contents of something after you have removed its const nature with a cast. Just casting const away and then reading the data can't be undefined behaviour, otherwise casting const away is useless and can be totally disallowed.

Casting away const just to read the data is useless. You can read const data without a cast.

No you can't. You can't pass it to a C function.
 
 But my understanding is that casting away const to write should be 
 doable if you know what you're doing.  If this is undefined behavior, 
 then that's fine, I'm just unclear on what "undefined behavior" actually 
 means.  I thought "undefined behavior" means that you cannot count on 
 the behavior to work in the future.
 
 An example of where casting away const to write should be allowed is for 
 a hypothetical mutable member:
 
 class C
 {
    private Mutable!(int) cache = -1;
    int expensiveFunction() const { return cache == -1 ? cache = 
 _expensiveFunctionImpl() : cache; }
    private int _expensiveFunctionImpl() const {...}
 }
 
 If this is undefined, then something like this cannot be relied on, even 
 when performance is critical.
 
 -Steve

I really can't see how the compiler could make that work, without destroying most of the benefits of const. For example, if that code is legal, it disallows most const-related optimisation.
Aug 09 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisprog gmail.com> writes:
On Monday, August 09, 2010 07:13:31 Steven Schveighoffer wrote:
 Casting away const just to read the data is useless.  You can read const
 data without a cast.

That's not actually quite true. If all code were const-correct, then it would be, but much of D is not. For instance, toHash() is not const even though it should be. The result is that if you need to call it with a const object, you have to cast away the constness. No writing is going on there (unless the writer of the toHash() function in the derived class(es) screwed it up), but you can't call it with a const object. I've run into plenty of similar situations in C++ where whoever wrote the code didn't choose to use const at all, and it made making that code deal with const a royal pain if not totally unreasonable. Casting away constness in cases where you knew that no write was going to take place or just dropping the constness in in your code were the only two options. If anything, my reaction would have been that the programmer has no business writing to anything that they cast away the constness of. Dealing with code which _could_ and _should_ be const but isn't is the only place that I've ever even considered casting away const. - Jonathan M Davis
Aug 09 2010
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 09 Aug 2010 17:15:41 -0400, Jonathan M Davis  
<jmdavisprog gmail.com> wrote:

 On Monday, August 09, 2010 07:13:31 Steven Schveighoffer wrote:
 Casting away const just to read the data is useless.  You can read const
 data without a cast.

That's not actually quite true. If all code were const-correct, then it would be, but much of D is not. For instance, toHash() is not const even though it should be. The result is that if you need to call it with a const object, you have to cast away the constness. No writing is going on there (unless the writer of the toHash() function in the derived class(es) screwed it up), but you can't call it with a const object.

Then the author failed to make it const, and it's a bug in the function definition. "Casting away const if you don't write" is crap, and should be treated as suspiciously as code that writes to const data. What if calculating the hash is expensive, and you know you don't have to recalculate it, you might cache it as a member of the class. Believe me, if a programmer can do it, he will. Documentation saying "don't do this!" isn't enough.
 I've run into plenty of similar situations in C++
 where whoever wrote the code didn't choose to use const at all, and it  
 made
 making that code deal with const a royal pain if not totally  
 unreasonable.

Casting away const in C++ and writing to the data I think is not undefined. I believe that's one of Walter's gripes with C++ const. -Steve
Aug 09 2010
prev sibling next sibling parent Don <nospam nospam.com> writes:
Steven Schveighoffer wrote:
 On Sun, 08 Aug 2010 17:56:25 -0400, simendsjo 
 <simen.endsjo pandavre.com> wrote:
 
 I'm totally new to the const/immutable thing, so this might be a naive 
 question..

 The spec says:
 "modification after casting away const" => "undefined behavior"

I thought it was "you're on your own", not undefined behavior. The former implies there is some "right" way to do this if you know more about the data than the compiler, the latter implies that there is no right way to cast away const. Am I wrong? -Steve

I think you're wrong. It's OK to cast away const, as long as you don't modify the thing which is supposed to be const. But if you modify it, there's no way the compiler can guarantee that your code will work. Anything could happen.
Aug 09 2010
prev sibling next sibling parent BCS <none anon.com> writes:
Hello Steven,

 On Sun, 08 Aug 2010 17:56:25 -0400, simendsjo
 <simen.endsjo pandavre.com>  wrote:
 
 I'm totally new to the const/immutable thing, so this might be a
 naive  question..
 
 The spec says:
 "modification after casting away const" => "undefined behavior"

former implies there is some "right" way to do this if you know more about the data than the compiler, the latter implies that there is no right way to cast away const. Am I wrong?

I think you are right re the meaning of those terms but I think what you thought it to be is more along the lines of what is correct. I /think/ the situation is that there are things that will work reliably and things that result in undefined behavior and you're on your own figuring out what is what.
 -Steve
 

... <IXOYE><
Aug 09 2010
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 09 Aug 2010 10:24:56 -0400, BCS <none anon.com> wrote:

 Hello Steven,

 On Sun, 08 Aug 2010 17:56:25 -0400, simendsjo
 <simen.endsjo pandavre.com>  wrote:

 I'm totally new to the const/immutable thing, so this might be a
 naive  question..
  The spec says:
 "modification after casting away const" => "undefined behavior"

former implies there is some "right" way to do this if you know more about the data than the compiler, the latter implies that there is no right way to cast away const. Am I wrong?

I think you are right re the meaning of those terms but I think what you thought it to be is more along the lines of what is correct. I /think/ the situation is that there are things that will work reliably and things that result in undefined behavior and you're on your own figuring out what is what.

I'm sort of interpreting what you're saying is: In some cases, you can cast away const and modify the variable with no adverse effects, but we can't tell you which cases those are. Which is kind of like saying "yes, casting away const is allowed, but only people who wrote the compiler can do it". -Steve
Aug 09 2010
parent BCS <none anon.com> writes:
Hello Steven,

 On Mon, 09 Aug 2010 10:24:56 -0400, BCS <none anon.com> wrote:
 
 Hello Steven,
 
 On Sun, 08 Aug 2010 17:56:25 -0400, simendsjo
 <simen.endsjo pandavre.com>  wrote:
 I'm totally new to the const/immutable thing, so this might be a
 naive  question..
 The spec says:
 "modification after casting away const" => "undefined behavior"

former implies there is some "right" way to do this if you know more about the data than the compiler, the latter implies that there is no right way to cast away const. Am I wrong?

you thought it to be is more along the lines of what is correct. I /think/ the situation is that there are things that will work reliably and things that result in undefined behavior and you're on your own figuring out what is what.

In some cases, you can cast away const and modify the variable with no adverse effects, but we can't tell you which cases those are.

Yes and no. Yes, sometimes casting away const and then writing to the pointer doesn't crash your program (or do anything else) but no that's not what I was saying. It's still undefined behavior even if it "works". What "your on your own" with is making sure you don't write to the pointer. Once you cast, the compiler won't check your work.
 
 Which is kind of like saying "yes, casting away const is allowed, but
 only  people who wrote the compiler can do it".
 
 -Steve
 

... <IXOYE><
Aug 09 2010
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 09 Aug 2010 09:57:47 -0400, bearophile <bearophileHUGS lycos.com>  
wrote:

 Steven Schveighoffer:
 I thought it was "you're on your own", not undefined behavior.  The  
 former
 implies there is some "right" way to do this if you know more about the
 data than the compiler, the latter implies that there is no right way to
 cast away const.  Am I wrong?

In my opinion if this thing is well designed then you go in undefined behaviour only when you change the contents of something after you have removed its const nature with a cast. Just casting const away and then reading the data can't be undefined behaviour, otherwise casting const away is useless and can be totally disallowed.

Casting away const just to read the data is useless. You can read const data without a cast. But my understanding is that casting away const to write should be doable if you know what you're doing. If this is undefined behavior, then that's fine, I'm just unclear on what "undefined behavior" actually means. I thought "undefined behavior" means that you cannot count on the behavior to work in the future. An example of where casting away const to write should be allowed is for a hypothetical mutable member: class C { private Mutable!(int) cache = -1; int expensiveFunction() const { return cache == -1 ? cache = _expensiveFunctionImpl() : cache; } private int _expensiveFunctionImpl() const {...} } If this is undefined, then something like this cannot be relied on, even when performance is critical. -Steve
Aug 09 2010
parent BCS <none anon.com> writes:
Hello Steven,

 On Mon, 09 Aug 2010 09:57:47 -0400, bearophile
 <bearophileHUGS lycos.com>  wrote:
 
 Steven Schveighoffer:
 
 I thought it was "you're on your own", not undefined behavior.  The
 former
 implies there is some "right" way to do this if you know more about
 the
 data than the compiler, the latter implies that there is no right
 way to
 cast away const.  Am I wrong?

behaviour only when you change the contents of something after you have removed its const nature with a cast. Just casting const away and then reading the data can't be undefined behaviour, otherwise casting const away is useless and can be totally disallowed.

const data without a cast. But my understanding is that casting away const to write should be doable if you know what you're doing. If this is undefined behavior, then that's fine, I'm just unclear on what "undefined behavior" actually means. I thought "undefined behavior" means that you cannot count on the behavior to work in the future.

Undefined behavior implies exactly that, once you enter the realm of undefined behavior, /anything/ that happens is your fault, the program can do anything at all and sill be considered a conforming program. It need not even behave the same between one run and the next. The practical implications of this are that compilers are allowed to always assume the code never does anything that results in undefined behavior and generate code that is wrong in any number of ways if that assumption fails. -- ... <IXOYE><
Aug 09 2010
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 09 Aug 2010 10:11:39 -0400, Don <nospam nospam.com> wrote:

 Steven Schveighoffer wrote:
 On Sun, 08 Aug 2010 17:56:25 -0400, simendsjo  
 <simen.endsjo pandavre.com> wrote:

 I'm totally new to the const/immutable thing, so this might be a naive  
 question..

 The spec says:
 "modification after casting away const" => "undefined behavior"

former implies there is some "right" way to do this if you know more about the data than the compiler, the latter implies that there is no right way to cast away const. Am I wrong? -Steve

I think you're wrong. It's OK to cast away const, as long as you don't modify the thing which is supposed to be const. But if you modify it, there's no way the compiler can guarantee that your code will work. Anything could happen.

But then what is the point of casting away const? If you are not going to modify it, then there is no reason to cast it away. What about how Rebindable works? It doesn't exactly cast away const, it uses a union, but the effect is the same. Is this OK simply because a union is used? Also, see my reply to bearophile, with hypothetical example. -Steve
Aug 09 2010
next sibling parent reply BCS <none anon.com> writes:
Hello Steven,

 On Mon, 09 Aug 2010 10:11:39 -0400, Don <nospam nospam.com> wrote:
 
 Steven Schveighoffer wrote:
 
 On Sun, 08 Aug 2010 17:56:25 -0400, simendsjo
 <simen.endsjo pandavre.com> wrote:
 
 I'm totally new to the const/immutable thing, so this might be a
 naive  question..
 
 The spec says:
 "modification after casting away const" => "undefined behavior"

former implies there is some "right" way to do this if you know more about the data than the compiler, the latter implies that there is no right way to cast away const. Am I wrong? -Steve

don't modify the thing which is supposed to be const. But if you modify it, there's no way the compiler can guarantee that your code will work. Anything could happen.

But then what is the point of casting away const? If you are not going to modify it, then there is no reason to cast it away.

There are some cases where non-const pointers are used but never modified (C api's for instance) cast as always is just a way subvert the type system where it gets in your way. -- ... <IXOYE><
Aug 09 2010
parent reply bearophile <bearophileHUGS lycos.com> writes:
Steven Schveighoffer:
 extern(C) int strlen(const(char)* str);

C has no const(char)* so I am now thinking about a possible D2 diagnostic enhancement request that turns that line of code into a compile time error :-) Bye, bearophile
Aug 09 2010
parent reply bearophile <bearophileHUGS lycos.com> writes:
Steven Schveighoffer:
 But an extern(C) function does not have to be written in C :)

You are right. But that function written in an arbitrary language has to follow the C interface rules and limitations, and among those there is no way to define a variable to be const(char)*. So in that line of code you are writing something that can't be enforced. Generally D design refuses features that the compiler is unable to verify. So I think an enhancement request to disallow that is good here. An extern(C) call has to specify only things that are understood by the C interface. On the other hand in C you have const, but its semantics is different. Uhm... Bye, bearophile
Aug 09 2010
parent reply Mafi <mafi example.org> writes:
Am 09.08.2010 17:31, schrieb bearophile:
 Steven Schveighoffer:
 But an extern(C) function does not have to be written in C :)

You are right. But that function written in an arbitrary language has to follow the C interface rules and limitations, and among those there is no way to define a variable to be const(char)*. So in that line of code you are writing something that can't be enforced. Generally D design refuses features that the compiler is unable to verify. So I think an enhancement request to disallow that is good here. An extern(C) call has to specify only things that are understood by the C interface. On the other hand in C you have const, but its semantics is different. Uhm... Bye, bearophile

I think, that isn't a good idea. I mean const-ness a compile time thing so the c abi has no problem with it. What's wrong when I define a extern(c) bool thinkeAboutMyInt(const int* x) { .... } I want to inform the D typesystem that I'm not going to change the int but I need a pointer because the adress is important. I want this function to be also callable outside D (eg C). Mafi
Aug 09 2010
parent reply bearophile <bearophileHUGS lycos.com> writes:
Mafi:
 I think, that isn't a good idea.

I agree, that idea doesn't work well. Bye, bearophile
Aug 09 2010
parent reply Lutger <lutger.blijdestijn gmail.com> writes:
bearophile wrote:

 Mafi:
 I think, that isn't a good idea.

I agree, that idea doesn't work well. Bye, bearophile

I think it still is a good idea to forbid this in safe mode. Perhaps in trusted too.
Aug 09 2010
parent Lutger <lutger.blijdestijn gmail.com> writes:
Steven Schveighoffer wrote:

 On Mon, 09 Aug 2010 14:39:58 -0400, Lutger <lutger.blijdestijn gmail.com>
 wrote:
 
 bearophile wrote:

 Mafi:
 I think, that isn't a good idea.

I agree, that idea doesn't work well. Bye, bearophile

I think it still is a good idea to forbid this in safe mode. Perhaps in trusted too.

Note, this isn't any less safe than defining whatever you want for a C function: extern(C) int strlen(int x); C has no mangling, so there is no storage of parameter types in the symbol. You can call any C function with whatever parameters you want to define for them. Making some set of parameters illegal because in some cases it might not be true where you don't prevent it in others because you can't prove it, is just simply useless. -Steve

Well you manually add typing, I think that is useful. But come to think of it, extern(C) functions should not be allowed in safe code at all, only via trusted and then the typing is useful. Perhaps this has been talked about, but I'm not sure how far trusted can be allowed to go.
Aug 09 2010
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 09 Aug 2010 10:37:14 -0400, BCS <none anon.com> wrote:

 Hello Steven,

 On Mon, 09 Aug 2010 10:11:39 -0400, Don <nospam nospam.com> wrote:

 Steven Schveighoffer wrote:

 On Sun, 08 Aug 2010 17:56:25 -0400, simendsjo
 <simen.endsjo pandavre.com> wrote:

 I'm totally new to the const/immutable thing, so this might be a
 naive  question..
  The spec says:
 "modification after casting away const" => "undefined behavior"

former implies there is some "right" way to do this if you know more about the data than the compiler, the latter implies that there is no right way to cast away const. Am I wrong? -Steve

don't modify the thing which is supposed to be const. But if you modify it, there's no way the compiler can guarantee that your code will work. Anything could happen.

But then what is the point of casting away const? If you are not going to modify it, then there is no reason to cast it away.

There are some cases where non-const pointers are used but never modified (C api's for instance) cast as always is just a way subvert the type system where it gets in your way.

C's api can be modified at declaration. It has no mangling, so you can type it how it should be (if C had const). For example: extern(C) int strlen(const(char)* str); I find that much more pleasant than having to cast away const. -Steve
Aug 09 2010
next sibling parent BCS <none anon.com> writes:
Hello Steven,

 On Mon, 09 Aug 2010 10:37:14 -0400, BCS <none anon.com> wrote:
 
 Hello Steven,
 
 On Mon, 09 Aug 2010 10:11:39 -0400, Don <nospam nospam.com> wrote:
 
 Steven Schveighoffer wrote:
 
 On Sun, 08 Aug 2010 17:56:25 -0400, simendsjo
 <simen.endsjo pandavre.com> wrote:
 I'm totally new to the const/immutable thing, so this might be a
 naive  question..
 The spec says:
 "modification after casting away const" => "undefined behavior"

The former implies there is some "right" way to do this if you know more about the data than the compiler, the latter implies that there is no right way to cast away const. Am I wrong? -Steve

don't modify the thing which is supposed to be const. But if you modify it, there's no way the compiler can guarantee that your code will work. Anything could happen.

going to modify it, then there is no reason to cast it away.

modified (C api's for instance) cast as always is just a way subvert the type system where it gets in your way.

can type it how it should be (if C had const). For example: extern(C) int strlen(const(char)* str); I find that much more pleasant than having to cast away const.

OTOH that is effectively a hidden cast and has 100% of the same issues (re undefined behavior) as casting away const while being slightly harder to find. -- ... <IXOYE><
Aug 09 2010
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 09 Aug 2010 10:53:48 -0400, BCS <none anon.com> wrote:

 Hello Steven,

 On Mon, 09 Aug 2010 10:37:14 -0400, BCS <none anon.com> wrote:

 Hello Steven,

 On Mon, 09 Aug 2010 10:11:39 -0400, Don <nospam nospam.com> wrote:

 Steven Schveighoffer wrote:

 On Sun, 08 Aug 2010 17:56:25 -0400, simendsjo
 <simen.endsjo pandavre.com> wrote:
 I'm totally new to the const/immutable thing, so this might be a
 naive  question..
 The spec says:
 "modification after casting away const" => "undefined behavior"

The former implies there is some "right" way to do this if you know more about the data than the compiler, the latter implies that there is no right way to cast away const. Am I wrong? -Steve

don't modify the thing which is supposed to be const. But if you modify it, there's no way the compiler can guarantee that your code will work. Anything could happen.

going to modify it, then there is no reason to cast it away.

modified (C api's for instance) cast as always is just a way subvert the type system where it gets in your way.

can type it how it should be (if C had const). For example: extern(C) int strlen(const(char)* str); I find that much more pleasant than having to cast away const.

OTOH that is effectively a hidden cast and has 100% of the same issues (re undefined behavior) as casting away const while being slightly harder to find.

But you just said that casting and reading is not undefined? Isn't this the same thing? Const is such a strange beast because it plays no role in code generation, it's effectively only a tool to help the compiler decide what is possible. It doesn't occupy any space or translate whatsoever to the underlying code. I think there are definite good uses for writing to const or immutable data besides ones that can be stored in ROM. That is, if you are sure a const or immutable piece of data is on the heap/stack, it should be reasonable to be able to modify it for performance gains. -Steve
Aug 09 2010
parent BCS <none anon.com> writes:
Hello Steven,

 On Mon, 09 Aug 2010 10:53:48 -0400, BCS <none anon.com> wrote:
 
 C's api can be modified at declaration.  It has no mangling, so you
 can  type it how it should be (if C had const).  For example:
 extern(C) int strlen(const(char)* str);
 I find that much more pleasant than having to cast away const.

issues (re undefined behavior) as casting away const while being slightly harder to find.

this the same thing?

Casting away const or tacking const into a extern(C) prototype is safe under exactly the same conditions. The gains a savings of a few keystrokes and looses some degree of findability, aside from that, there is effectively no difference between them.
 if you are sure
 a  const or immutable piece of data is on the heap/stack, it should be
 reasonable to be able to modify it for performance gains.

A conforming compiler can be implemented in such a way that you can never be sure of that without looking at the generated asm or even in such a way that it can't be know till runtime. -- ... <IXOYE><
Aug 09 2010
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 09 Aug 2010 11:04:40 -0400, Steven Schveighoffer  
<schveiguy yahoo.com> wrote:

 On Mon, 09 Aug 2010 10:53:48 -0400, BCS <none anon.com> wrote:

 OTOH that is effectively a hidden cast and has 100% of the same issues  
 (re undefined behavior) as casting away const while being slightly  
 harder to find.

But you just said that casting and reading is not undefined? Isn't this the same thing? Const is such a strange beast because it plays no role in code generation, it's effectively only a tool to help the compiler decide what is possible. It doesn't occupy any space or translate whatsoever to the underlying code. I think there are definite good uses for writing to const or immutable data besides ones that can be stored in ROM. That is, if you are sure a const or immutable piece of data is on the heap/stack, it should be reasonable to be able to modify it for performance gains.

I should say, when performance gains are not possible because of the limitations of the const/immutable notation. The classic example is the mutable cache for avoiding recalculations on an immutable object. -Steve
Aug 09 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 09 Aug 2010 11:15:38 -0400, bearophile <bearophileHUGS lycos.com>  
wrote:

 Steven Schveighoffer:
 extern(C) int strlen(const(char)* str);

C has no const(char)* so I am now thinking about a possible D2 diagnostic enhancement request that turns that line of code into a compile time error :-)

But an extern(C) function does not have to be written in C :) -Steve
Aug 09 2010
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 09 Aug 2010 14:39:58 -0400, Lutger <lutger.blijdestijn gmail.com>  
wrote:

 bearophile wrote:

 Mafi:
 I think, that isn't a good idea.

I agree, that idea doesn't work well. Bye, bearophile

I think it still is a good idea to forbid this in safe mode. Perhaps in trusted too.

Note, this isn't any less safe than defining whatever you want for a C function: extern(C) int strlen(int x); C has no mangling, so there is no storage of parameter types in the symbol. You can call any C function with whatever parameters you want to define for them. Making some set of parameters illegal because in some cases it might not be true where you don't prevent it in others because you can't prove it, is just simply useless. -Steve
Aug 09 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 09 Aug 2010 10:27:09 -0400, Don <nospam nospam.com> wrote:

 Steven Schveighoffer wrote:
 On Mon, 09 Aug 2010 09:57:47 -0400, bearophile  
 <bearophileHUGS lycos.com> wrote:

 Steven Schveighoffer:
 I thought it was "you're on your own", not undefined behavior.  The  
 former
 implies there is some "right" way to do this if you know more about  
 the
 data than the compiler, the latter implies that there is no right way  
 to
 cast away const.  Am I wrong?

In my opinion if this thing is well designed then you go in undefined behaviour only when you change the contents of something after you have removed its const nature with a cast. Just casting const away and then reading the data can't be undefined behaviour, otherwise casting const away is useless and can be totally disallowed.

const data without a cast.

No you can't. You can't pass it to a C function.

Sure you can. extern(C) int strlen(const(char) *arg);
  But my understanding is that casting away const to write should be  
 doable if you know what you're doing.  If this is undefined behavior,  
 then that's fine, I'm just unclear on what "undefined behavior"  
 actually means.  I thought "undefined behavior" means that you cannot  
 count on the behavior to work in the future.
  An example of where casting away const to write should be allowed is  
 for a hypothetical mutable member:
  class C
 {
    private Mutable!(int) cache = -1;
    int expensiveFunction() const { return cache == -1 ? cache =  
 _expensiveFunctionImpl() : cache; }
    private int _expensiveFunctionImpl() const {...}
 }
  If this is undefined, then something like this cannot be relied on,  
 even when performance is critical.
  -Steve

I really can't see how the compiler could make that work, without destroying most of the benefits of const. For example, if that code is legal, it disallows most const-related optimisation.

Why not? What possible optimization can the compiler do here? Mutable has an assign operation that is labeled as const, it should be callable by the compiler. I haven't really seen what const optimizations can be, so maybe an example (even if unimplemented) is helpful. -Steve
Aug 09 2010
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Steven Schveighoffer <schveiguy yahoo.com> wrote:

  class C
 {
    private Mutable!(int) cache = -1;
    int expensiveFunction() const { return cache == -1 ? cache =  
 _expensiveFunctionImpl() : cache; }
    private int _expensiveFunctionImpl() const {...}
 }
  If this is undefined, then something like this cannot be relied on,  
 even when performance is critical.
  -Steve

I really can't see how the compiler could make that work, without destroying most of the benefits of const. For example, if that code is legal, it disallows most const-related optimisation.

Why not? What possible optimization can the compiler do here? Mutable has an assign operation that is labeled as const, it should be callable by the compiler. I haven't really seen what const optimizations can be, so maybe an example (even if unimplemented) is helpful.

The compiler may decide to store the value of cache in a register, and use that value instead of fetching it after _expensiveFunctionImpl is called. That's the simplest example I could come up with. -- Simen
Aug 09 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 09 Aug 2010 11:31:18 -0400, Simen kjaeraas  
<simen.kjaras gmail.com> wrote:

 Steven Schveighoffer <schveiguy yahoo.com> wrote:

  class C
 {
    private Mutable!(int) cache = -1;
    int expensiveFunction() const { return cache == -1 ? cache =  
 _expensiveFunctionImpl() : cache; }
    private int _expensiveFunctionImpl() const {...}
 }
  If this is undefined, then something like this cannot be relied on,  
 even when performance is critical.
  -Steve

I really can't see how the compiler could make that work, without destroying most of the benefits of const. For example, if that code is legal, it disallows most const-related optimisation.

Why not? What possible optimization can the compiler do here? Mutable has an assign operation that is labeled as const, it should be callable by the compiler. I haven't really seen what const optimizations can be, so maybe an example (even if unimplemented) is helpful.

The compiler may decide to store the value of cache in a register, and use that value instead of fetching it after _expensiveFunctionImpl is called. That's the simplest example I could come up with.

The compiler will rewrite the code as follows: return cache.opEquals(-1) ? cache.opAssign(_expensiveFunctionImpl()) : cache.xxx; I'm assuming for cache.xxx that it's some sort of alias this to a getter function. All of these function would be const. We can define the opAssign in such a way that it cannot be inlined, forcing the compiler to assume nothing about the result of opAssign. Note also that the optimization you stated is not possible, even without casting away const, but would be possible on an immutable class. But the fact that the compiler cannot peek into the implementation of the opAssign means it's forced to make no assumptions about the result of opAssign. How does one write a conforming compiler that alters the result of this? -Steve
Aug 09 2010
prev sibling next sibling parent "Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> writes:
On Mon, 09 Aug 2010 10:35:02 -0400, Steven Schveighoffer wrote:

 On Mon, 09 Aug 2010 10:27:09 -0400, Don <nospam nospam.com> wrote:
 
 Steven Schveighoffer wrote:
 On Mon, 09 Aug 2010 09:57:47 -0400, bearophile
 <bearophileHUGS lycos.com> wrote:

 Steven Schveighoffer:
 I thought it was "you're on your own", not undefined behavior.  The
 former
 implies there is some "right" way to do this if you know more about
 the
 data than the compiler, the latter implies that there is no right
 way to
 cast away const.  Am I wrong?

In my opinion if this thing is well designed then you go in undefined behaviour only when you change the contents of something after you have removed its const nature with a cast. Just casting const away and then reading the data can't be undefined behaviour, otherwise casting const away is useless and can be totally disallowed.

const data without a cast.

No you can't. You can't pass it to a C function.

Sure you can. extern(C) int strlen(const(char) *arg);
  But my understanding is that casting away const to write should be
 doable if you know what you're doing.  If this is undefined behavior,
 then that's fine, I'm just unclear on what "undefined behavior"
 actually means.  I thought "undefined behavior" means that you cannot
 count on the behavior to work in the future.
  An example of where casting away const to write should be allowed is
 for a hypothetical mutable member:
  class C
 {
    private Mutable!(int) cache = -1;
    int expensiveFunction() const { return cache == -1 ? cache =
 _expensiveFunctionImpl() : cache; }
    private int _expensiveFunctionImpl() const {...}
 }
  If this is undefined, then something like this cannot be relied on,
 even when performance is critical.
  -Steve

I really can't see how the compiler could make that work, without destroying most of the benefits of const. For example, if that code is legal, it disallows most const-related optimisation.

Why not? What possible optimization can the compiler do here? Mutable has an assign operation that is labeled as const, it should be callable by the compiler. I haven't really seen what const optimizations can be, so maybe an example (even if unimplemented) is helpful.

The compiler does an "optimization" on const/immutable struct members which I reported as a bug: http://d.puremagic.com/issues/show_bug.cgi?id=3449 -Lars
Aug 09 2010
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Steven Schveighoffer <schveiguy yahoo.com> wrote:

 Note also that the optimization you stated is not possible, even without  
 casting away const, but would be possible on an immutable class.  But  
 the fact that the compiler cannot peek into the implementation of the  
 opAssign means it's forced to make no assumptions about the result of  
 opAssign

You're right, I was thinking of immutable. -- Simen
Aug 09 2010
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Simen kjaeraas <simen.kjaras gmail.com> wrote:

 Steven Schveighoffer <schveiguy yahoo.com> wrote:

 Note also that the optimization you stated is not possible, even  
 without casting away const, but would be possible on an immutable  
 class.  But the fact that the compiler cannot peek into the  
 implementation of the opAssign means it's forced to make no assumptions  
 about the result of opAssign

You're right, I was thinking of immutable.

This said, one of the reasons casting away const is undefined, is because the underlying type may be immutable. -- Simen
Aug 09 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 09 Aug 2010 15:49:12 -0400, Simen kjaeraas  
<simen.kjaras gmail.com> wrote:

 Simen kjaeraas <simen.kjaras gmail.com> wrote:

 Steven Schveighoffer <schveiguy yahoo.com> wrote:

 Note also that the optimization you stated is not possible, even  
 without casting away const, but would be possible on an immutable  
 class.  But the fact that the compiler cannot peek into the  
 implementation of the opAssign means it's forced to make no  
 assumptions about the result of opAssign

You're right, I was thinking of immutable.

This said, one of the reasons casting away const is undefined, is because the underlying type may be immutable.

Right. After reading all these replies, I think the way it looks to me is technically it's undefined to cast away const/immutable and write the data. But, in certain circumstances, I think it will be possible to write portable code that works on all compilers that does this. It's like a threading race condition. If you simply allow unchecked access to a variable, it's undefined, you get races and can be using a partially written value, but slap a mutex around it, and the variable suddenly becomes well defined and behaves perfectly. The compiler cannot guarantee this, nor detect it, but the programmer can determine based on analysis of all the code that it's safe. -Steve
Aug 09 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisprog gmail.com> writes:
On Monday, August 09, 2010 15:01:28 Steven Schveighoffer wrote:
 Then the author failed to make it const, and it's a bug in the function
 definition.  "Casting away const if you don't write" is crap, and should
 be treated as suspiciously as code that writes to const data.

I totally agree that the author of the code screwed up. However, sometimes you have to deal with other people's screw ups. And unfortunately, in my experience, a _lot_ of programmers don't bother to write const-correct code, and it causes huge problems for those of us who do.
 What if calculating the hash is expensive, and you know you don't have to
 recalculate it, you might cache it as a member of the class.  Believe me,
 if a programmer can do it, he will.  Documentation saying "don't do this!"
 isn't enough.

That's why mutable would be highly desirable, but we don't have it so tough luck for us on that count, I suppose. As for documentation, if the function is const, then no documentation is necessary. They just can't do it (not without casting away constness and going into undefined territory anyway). Personally, I'd say tough luck to the guy who wants to cache the hash by calculating it in toHash(). He can call some other function to cache it, or he could have a non-const version which caches it for cases where the object isn't const, or he could calculate it when something in the class changes (which naturally comes with its own set of pros and cons). From the perspective of logical constness, there is no reason why toHash() can't be const. The one thing that stumps me is why associative arrays allow for const keys with toHash() not being const. If I were to try and write a hashtable implementation myself, I'd have to cast away the constness of the keys to be able to call toHash() on them, which would be _really_ annoying. Maybe that's what associative arrays are doing internally. Personally, I tend to be of the opinion that if a function can be const, it should be const. There are always exceptions of course, but generally I think that functions should be const when possible. It allows for writing const- correct code much more easily (if not just outright makes it possible), and that can reduce the number of mutation bugs that programmers have to deal with. - Jonathan M Davis
Aug 09 2010
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 09 Aug 2010 19:35:38 -0400, Jonathan M Davis  
<jmdavisprog gmail.com> wrote:

 On Monday, August 09, 2010 15:01:28 Steven Schveighoffer wrote:
 Then the author failed to make it const, and it's a bug in the function
 definition.  "Casting away const if you don't write" is crap, and should
 be treated as suspiciously as code that writes to const data.

I totally agree that the author of the code screwed up. However, sometimes you have to deal with other people's screw ups. And unfortunately, in my experience, a _lot_ of programmers don't bother to write const-correct code, and it causes huge problems for those of us who do.

Yes, but by using such code and casting away const you are: 1) opening up your application to potential undefined behavior if the code you are using changes in a way that actually *does* write to the object 2) opening up your *own* code to potential undefined behavior in case you accidentally forget that you casted away const. Casting away const is bad unless you control all the elements involved, and I think you need to limit it to small pieces of code where you scrutinize every aspect of it. I think creating a Mutable wrapper for a type can potentially be an asset if it's done correctly.
 What if calculating the hash is expensive, and you know you don't have  
 to
 recalculate it, you might cache it as a member of the class.  Believe  
 me,
 if a programmer can do it, he will.  Documentation saying "don't do  
 this!"
 isn't enough.

That's why mutable would be highly desirable, but we don't have it so tough luck for us on that count, I suppose. As for documentation, if the function is const, then no documentation is necessary. They just can't do it (not without casting away constness and going into undefined territory anyway).

As long as its encapsulated, I think we are ok. Note that there are other parts of the instance that are always mutable, such as the monitor object.
 Personally, I'd say tough luck to the guy who wants to cache the hash by
 calculating it in toHash(). He can call some other function to cache it,  
 or he
 could have a non-const version which caches it for cases where the  
 object isn't
 const, or he could calculate it when something in the class changes  
 (which
 naturally comes with its own set of pros and cons). From the perspective  
 of
 logical constness, there is no reason why toHash() can't be const.

I've proven that logical const is doable without breaking const in a post a couple years ago. It's just lacking the language support. I also made an extremely complex proposal to allow specification of various levels of const but that was rejected on account of being too complex :) But looking at things like Rebindable, I think we should be able to make a logical const type that allows what we need in a controlled manner.
 The one thing that stumps me is why associative arrays allow for const  
 keys with
 toHash() not being const. If I were to try and write a hashtable  
 implementation
 myself, I'd have to cast away the constness of the keys to be able to  
 call
 toHash() on them, which would be _really_ annoying. Maybe that's what
 associative arrays are doing internally.

hehe, AA's are part of the runtime, so they manually must obey const rules. Basically, they get a TypeInfo and a void * (which the compiler strips of any const or type) and have to do the right thing. The TypeInfo's getHash() function takes a void *, so no const is obeyed, that's why it works.
 Personally, I tend to be of the opinion that if a function can be const,  
 it
 should be const. There are always exceptions of course, but generally I  
 think
 that functions should be const when possible. It allows for writing  
 const-
 correct code much more easily (if not just outright makes it possible),  
 and that
 can reduce the number of mutation bugs that programmers have to deal  
 with.

I agree. -Steve
Aug 10 2010