www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - D2.0: an example of use-case for casting invariant away

reply eao197 <eao197 intervale.ru> writes:
	Hello!

It is a question only for clarification for myself. If I understand  =

http://www.digitalmars.com/d/final-const-invariant.html correctly the  =

casting invariant away allowed for situations like this:

// An interface for some third-party library
// (which is probably written in some exotic language).
extern(C) void initSomeSubsystem( char * initializationString );
...
// Our D2.0 stuff.
int main()
   {
     invariant char* initializationString =3D "...";
     // Casting invariant away is necessary here.
     initSomeSubsystem( cast(invariant) initializationString );
   }

Am I correct?

-- =

Regards,
Yauheni Akhotnikau
Jun 18 2007
next sibling parent reply Brad Roberts <braddr puremagic.com> writes:
eao197 wrote:
     Hello!
 
 It is a question only for clarification for myself. If I understand 
 http://www.digitalmars.com/d/final-const-invariant.html correctly the 
 casting invariant away allowed for situations like this:
 
 // An interface for some third-party library
 // (which is probably written in some exotic language).
 extern(C) void initSomeSubsystem( char * initializationString );
 ...
 // Our D2.0 stuff.
 int main()
   {
     invariant char* initializationString = "...";
     // Casting invariant away is necessary here.
     initSomeSubsystem( cast(invariant) initializationString );
   }
 
 Am I correct?
 
 --Regards,
 Yauheni Akhotnikau
First, is the library actually treating the initString as read only? If it's not, then you shouldn't pass an invariant (or const) string down to it. The results would be undefined. If it's actually treating the string as read-only, then I'd be tempted to change the extern(c) declaration to pass the argument as const char * rather than casting away the invariantness. Note, I haven't tried this, just an off the top of my head suggestion. Later, Brad
Jun 18 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Brad Roberts wrote:
 If it's actually treating the string as read-only, then I'd be tempted 
 to change the extern(c) declaration to pass the argument as const char * 
 rather than casting away the invariantness.
The need to be able to cast away const-ness is to interface to libraries that you cannot go and change. Also, the type system is not meant to be a straitjacket. If you absolutely must do something, and you know what you are doing, there should be a way to do it.
Jun 18 2007
parent reply Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
 Brad Roberts wrote:
 If it's actually treating the string as read-only, then I'd be tempted 
 to change the extern(c) declaration to pass the argument as const char 
 * rather than casting away the invariantness.
The need to be able to cast away const-ness is to interface to libraries that you cannot go and change. Also, the type system is not meant to be a straitjacket. If you absolutely must do something, and you know what you are doing, there should be a way to do it.
That's understandable, but isn't it contrary to your statement that const in C++ is useless because of const_cast? Or is it the presence of a cast specifically intended to cast away const that you take issue with? Sean
Jun 18 2007
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Sean Kelly wrote:
 Walter Bright wrote:
 Brad Roberts wrote:
 If it's actually treating the string as read-only, then I'd be
 tempted to change the extern(c) declaration to pass the argument as
 const char * rather than casting away the invariantness.
The need to be able to cast away const-ness is to interface to libraries that you cannot go and change. Also, the type system is not meant to be a straitjacket. If you absolutely must do something, and you know what you are doing, there should be a way to do it.
That's understandable, but isn't it contrary to your statement that const in C++ is useless because of const_cast? Or is it the presence of a cast specifically intended to cast away const that you take issue with? Sean
Walter's said on a few occasions that the problem with C++'s const is that casting away const is a *well-defined* operation. That means that const cannot be used to do any kind of optimisation, since it doesn't really mean anything. The new page on const specifically states that casting away const or invariance is *not* a well-defined operation, and you'd better know what you're doing when you do it. -- Daniel
Jun 18 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Daniel Keep wrote:
 Walter's said on a few occasions that the problem with C++'s const is
 that casting away const is a *well-defined* operation.  That means that
 const cannot be used to do any kind of optimisation, since it doesn't
 really mean anything.
 
 The new page on const specifically states that casting away const or
 invariance is *not* a well-defined operation, and you'd better know what
 you're doing when you do it.
More specifically, in C++, you can cast away const-ness and then modify the underlying data, and this is defined to be legal, defined behavior. With D, you can cast away const-ness, that is legal. But if you subsequently modify the underlying data, that is undefined behavior.
Jun 18 2007
next sibling parent reply James Dennett <jdennett acm.org> writes:
Walter Bright wrote:
 Daniel Keep wrote:
 Walter's said on a few occasions that the problem with C++'s const is
 that casting away const is a *well-defined* operation.  That means that
 const cannot be used to do any kind of optimisation, since it doesn't
 really mean anything.

 The new page on const specifically states that casting away const or
 invariance is *not* a well-defined operation, and you'd better know what
 you're doing when you do it.
More specifically, in C++, you can cast away const-ness and then modify the underlying data, and this is defined to be legal, defined behavior.
More specifically still, in C++ you can cast away const-ness and then modify the underlying object only if that underlying object is not defined as being const. If you modify an object that is actually const (where I think Walter might say that it has const as it "storage class", though that doesn't match C++ terminology) then you have undefined behavior.
 With D, you can cast away const-ness, that is legal. But if you
 subsequently modify the underlying data, that is undefined behavior.
Seems reasonable to me (though one of C++'s faults is that it has *too much* undefined behavior). -- James
Jun 18 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
James Dennett wrote:
 Seems reasonable to me (though one of C++'s faults is that it has
 *too much* undefined behavior).
C++ has too much implementation defined and undefined behavior. D has less, but it should be even less. ID and UB impede source portability.
Jun 19 2007
prev sibling parent reply Don Clugston <dac nospam.com.au> writes:
Walter Bright wrote:
 Daniel Keep wrote:
 Walter's said on a few occasions that the problem with C++'s const is
 that casting away const is a *well-defined* operation.  That means that
 const cannot be used to do any kind of optimisation, since it doesn't
 really mean anything.

 The new page on const specifically states that casting away const or
 invariance is *not* a well-defined operation, and you'd better know what
 you're doing when you do it.
More specifically, in C++, you can cast away const-ness and then modify the underlying data, and this is defined to be legal, defined behavior. With D, you can cast away const-ness, that is legal. But if you subsequently modify the underlying data, that is undefined behavior.
It sounds that in D, it will be too easy to cast away constness accidentally. With C++, at least you can grep for const_cast and detect potentially dangerous code, and you get a strong visual clue. Suppose I've written a D function like this: void f(int *b, uint c) { // maybe I'm avoiding a compiler warning or something. uint *d = cast(uint *)b; d += c; } Months later, I'm refactoring the code, and I convert the int * parameter to an invariant, without recognising that it's changing the value of b. Oops. C++'s const would catch this mistake, but if I understand correctly, D will compile it without error. Suddenly the function has moved into the realm of undefined behaviour. I hope I'm wrong. Or did I miss something?
Jun 21 2007
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Don Clugston wrote:
 Walter Bright wrote:
 With D, you can cast away const-ness, that is legal. But if you 
 subsequently modify the underlying data, that is undefined behavior.
It sounds that in D, it will be too easy to cast away constness accidentally. With C++, at least you can grep for const_cast and detect potentially dangerous code, and you get a strong visual clue. Suppose I've written a D function like this: void f(int *b, uint c) { // maybe I'm avoiding a compiler warning or something. uint *d = cast(uint *)b; d += c; } Months later, I'm refactoring the code, and I convert the int * parameter to an invariant, without recognising that it's changing the value of b. Oops. C++'s const would catch this mistake, but if I understand correctly, D will compile it without error. Suddenly the function has moved into the realm of undefined behaviour. I hope I'm wrong. Or did I miss something?
No, you're not missing something. It is a general problem with cast - cast is a blunt instrument which can easily hide problems.
Jun 21 2007
next sibling parent reply Don Clugston <dac nospam.com.au> writes:
Walter Bright wrote:
 Don Clugston wrote:
 Walter Bright wrote:
 With D, you can cast away const-ness, that is legal. But if you 
 subsequently modify the underlying data, that is undefined behavior.
It sounds that in D, it will be too easy to cast away constness accidentally. With C++, at least you can grep for const_cast and detect potentially dangerous code, and you get a strong visual clue. Suppose I've written a D function like this: void f(int *b, uint c) { // maybe I'm avoiding a compiler warning or something. uint *d = cast(uint *)b; d += c; } Months later, I'm refactoring the code, and I convert the int * parameter to an invariant, without recognising that it's changing the value of b. Oops. C++'s const would catch this mistake, but if I understand correctly, D will compile it without error. Suddenly the function has moved into the realm of undefined behaviour. I hope I'm wrong. Or did I miss something?
No, you're not missing something. It is a general problem with cast - cast is a blunt instrument which can easily hide problems.
This means that cast() has just become even more unsafe. So for 2.0, it will be even more important to provide ways to avoid usage of cast(). The .ptr, .re, and .im properties were a huge help; maybe the idea can be extended to other cases where a cast is perfectly safe. Usage of invariants inside unions is suspect too. At least where some members are invariant and others are not const, it's asking for trouble -- cast() by stealth. union U { invariant C *c; C *d; } Though arguably unions are always a blunt, dangerous instrument as well.
Jun 21 2007
next sibling parent Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
Don Clugston skrev:
 Walter Bright wrote:
 Don Clugston wrote:
 Walter Bright wrote:
 With D, you can cast away const-ness, that is legal. But if you 
 subsequently modify the underlying data, that is undefined behavior.
It sounds that in D, it will be too easy to cast away constness accidentally. With C++, at least you can grep for const_cast and detect potentially dangerous code, and you get a strong visual clue. Suppose I've written a D function like this: void f(int *b, uint c) { // maybe I'm avoiding a compiler warning or something. uint *d = cast(uint *)b; d += c; } Months later, I'm refactoring the code, and I convert the int * parameter to an invariant, without recognising that it's changing the value of b. Oops. C++'s const would catch this mistake, but if I understand correctly, D will compile it without error. Suddenly the function has moved into the realm of undefined behaviour. I hope I'm wrong. Or did I miss something?
No, you're not missing something. It is a general problem with cast - cast is a blunt instrument which can easily hide problems.
This means that cast() has just become even more unsafe. So for 2.0, it will be even more important to provide ways to avoid usage of cast(). The .ptr, .re, and .im properties were a huge help; maybe the idea can be extended to other cases where a cast is perfectly safe.
I had look at the places I use casts in some of my projects, and found some categories (in no particular order): 1. Cast needed by the archaic typeinfo system, example: class C { int opCmp(Object o) { auto c = cast(C) o; assert(c !is null); ... } } typeid(char[]).compare(cast(void *) &str1, cast(void *) &str2); 2. casts from void[], void* 3. Unsigned/signed casts and working around stupid promotion rules: template T(int n) { ... } uint n = ...; T!(cast(int) n) // cast required double randomDelta() { return (rand() % 3) - 1; } void fun(int[] arr) { long d = arr.length - 10; while (d > 0) arr[--d] = 0; } 4. casts to subclasses (c++ dynamic_cast) (personally very seldom used) 5. casts needed for overload resolution: std.math.pow(cast(real) doubleVar, intVar); // sigh 6. rational -> floating point cast(double) a.length / b.length got something like generic method injection, one could replace cast(double) intVar with intVar.toDouble, or intVar.to!(double) but is that any better? /Oskar
Jun 21 2007
prev sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Don Clugston wrote:
 Walter Bright wrote:
 No, you're not missing something. It is a general problem with cast - 
 cast is a blunt instrument which can easily hide problems.
This means that cast() has just become even more unsafe. So for 2.0, it will be even more important to provide ways to avoid usage of cast(). The .ptr, .re, and .im properties were a huge help; maybe the idea can be extended to other cases where a cast is perfectly safe.
I think you're right.
 Usage of invariants inside unions is suspect too.
 At least where some members are invariant and others are not const, it's 
 asking for trouble -- cast() by stealth.
 
 union U {
  invariant C *c;
  C *d;
 }
 Though arguably unions are always a blunt, dangerous instrument as well.
This is why some languages disallow unions completely. But I'm not concerned about this case, as it's a deliberate attempt to circumvent the typing rules. If someone is doing it deliberately, D gives them the benefit of the doubt and presumes they know what they're doing. I'm only concerned about the former inadvertent case.
Jun 21 2007
prev sibling next sibling parent eao197 <eao197 intervale.ru> writes:
On Thu, 21 Jun 2007 11:32:53 +0400, Walter Bright  =

<newshound1 digitalmars.com> wrote:

 Don Clugston wrote:
 Suppose I've written a D function like this:
  void f(int *b, uint c)
 {
   // maybe I'm avoiding a compiler warning or something.
   uint *d =3D cast(uint *)b;
   d +=3D c;
 }
  Months later, I'm refactoring the code, and I convert the int *  =
 parameter to an invariant, without recognising that it's changing the=
=
 value of b. Oops.
  C++'s const would catch this mistake, but if I understand correctly,=
D =
 will compile it without error. Suddenly the function has moved into t=
he =
 realm of undefined behaviour.
  I hope I'm wrong. Or did I miss something?
No, you're not missing something. It is a general problem with cast - =
=
 cast is a blunt instrument which can easily hide problems.
May be it is better to define two new cast operators: const_cast, that = removes only const, and invariant_cast, that removes only invariant. = Ordinal cast() can't remove const/invariantness. In such case Don Clugston's example will produce compile-time error = (because cast() can't remove invariant). And all potentially dangerous = places in a program can be easily detected by simply greeping = /(const|invariant)_cast/ -- = Regards, Yauheni Akhotnikau
Jun 21 2007
prev sibling parent reply Regan Heath <regan netmail.co.nz> writes:
Walter Bright Wrote:
 Don Clugston wrote:
 Walter Bright wrote:
 With D, you can cast away const-ness, that is legal. But if you 
 subsequently modify the underlying data, that is undefined behavior.
It sounds that in D, it will be too easy to cast away constness accidentally. With C++, at least you can grep for const_cast and detect potentially dangerous code, and you get a strong visual clue. Suppose I've written a D function like this: void f(int *b, uint c) { // maybe I'm avoiding a compiler warning or something. uint *d = cast(uint *)b; d += c; } Months later, I'm refactoring the code, and I convert the int * parameter to an invariant, without recognising that it's changing the value of b. Oops. C++'s const would catch this mistake, but if I understand correctly, D will compile it without error. Suddenly the function has moved into the realm of undefined behaviour. I hope I'm wrong. Or did I miss something?
No, you're not missing something. It is a general problem with cast - cast is a blunt instrument which can easily hide problems.
So.. we're going to have to put up with this potential nasty bug? What about a new cast which only removes 'const' and/or 'invariant and prohibit normal cast from removing it. Perhaps calling it 'vary', eg. void f(const int *b, uint c) { int *d = vary() b; } Not sure about the (), if they're needed, or if vary(b) would be a better syntax. The basic point being that cast cannot then cause the nasty bug and vary can be searched/grepped for. Regan
Jun 21 2007
parent reply Don Clugston <dac nospam.com.au> writes:
Regan Heath wrote:
 Walter Bright Wrote:
 Don Clugston wrote:
 Walter Bright wrote:
 With D, you can cast away const-ness, that is legal. But if you 
 subsequently modify the underlying data, that is undefined behavior.
It sounds that in D, it will be too easy to cast away constness accidentally. With C++, at least you can grep for const_cast and detect potentially dangerous code, and you get a strong visual clue. Suppose I've written a D function like this: void f(int *b, uint c) { // maybe I'm avoiding a compiler warning or something. uint *d = cast(uint *)b; d += c; } Months later, I'm refactoring the code, and I convert the int * parameter to an invariant, without recognising that it's changing the value of b. Oops. C++'s const would catch this mistake, but if I understand correctly, D will compile it without error. Suddenly the function has moved into the realm of undefined behaviour. I hope I'm wrong. Or did I miss something?
No, you're not missing something. It is a general problem with cast - cast is a blunt instrument which can easily hide problems.
So.. we're going to have to put up with this potential nasty bug? What about a new cast which only removes 'const' and/or 'invariant and prohibit normal cast from removing it. Perhaps calling it 'vary', eg. void f(const int *b, uint c) { int *d = vary() b; }
To avoid a new keyword... int* d = cast(break const) b; IMHO, we want something that looks really nasty.
Jun 21 2007
next sibling parent Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
Don Clugston skrev:

 To avoid a new keyword...
 
 int* d = cast(break const) b;
 
 IMHO, we want something that looks really nasty.
Reusing break is brilliant! Another alternative: int *d = break(const) b; /Oskar
Jun 21 2007
prev sibling next sibling parent Eugene Pelekhay <pelekhay nospam.org> writes:
Don Clugston Wrote:

 Regan Heath wrote:
 Walter Bright Wrote:
 Don Clugston wrote:
 Walter Bright wrote:
 With D, you can cast away const-ness, that is legal. But if you 
 subsequently modify the underlying data, that is undefined behavior.
It sounds that in D, it will be too easy to cast away constness accidentally. With C++, at least you can grep for const_cast and detect potentially dangerous code, and you get a strong visual clue. Suppose I've written a D function like this: void f(int *b, uint c) { // maybe I'm avoiding a compiler warning or something. uint *d = cast(uint *)b; d += c; } Months later, I'm refactoring the code, and I convert the int * parameter to an invariant, without recognising that it's changing the value of b. Oops. C++'s const would catch this mistake, but if I understand correctly, D will compile it without error. Suddenly the function has moved into the realm of undefined behaviour. I hope I'm wrong. Or did I miss something?
No, you're not missing something. It is a general problem with cast - cast is a blunt instrument which can easily hide problems.
So.. we're going to have to put up with this potential nasty bug? What about a new cast which only removes 'const' and/or 'invariant and prohibit normal cast from removing it. Perhaps calling it 'vary', eg. void f(const int *b, uint c) { int *d = vary() b; }
To avoid a new keyword... int* d = cast(break const) b;
what about? int* d =cast(!const)b;
 
 IMHO, we want something that looks really nasty.
Jun 21 2007
prev sibling next sibling parent Daniel Keep <daniel.keep.lists gmail.com> writes:
Don Clugston wrote:
 To avoid a new keyword...
 
 int* d = cast(break const) b;
 
 IMHO, we want something that looks really nasty.
That's inspired, Don. No need for new keywords, *and* it perfectly conveys what it's doing. I also like that it's somewhat longer than a regular cast, since this is not the sort of thing you want to do lightly. -- Daniel
Jun 21 2007
prev sibling next sibling parent Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
Don Clugston wrote:
 To avoid a new keyword...
 
 int* d = cast(break const) b;
 
 IMHO, we want something that looks really nasty.
Great! ++votes; -- Tomasz Stachowiak http://h3.team0xf.com/ h3/h3r3tic on #D freenode
Jun 21 2007
prev sibling next sibling parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Don Clugston wrote:
 Regan Heath wrote:
 Walter Bright Wrote:
 Don Clugston wrote:
 Walter Bright wrote:
 With D, you can cast away const-ness, that is legal. But if you 
 subsequently modify the underlying data, that is undefined behavior.
It sounds that in D, it will be too easy to cast away constness accidentally. With C++, at least you can grep for const_cast and detect potentially dangerous code, and you get a strong visual clue. Suppose I've written a D function like this: void f(int *b, uint c) { // maybe I'm avoiding a compiler warning or something. uint *d = cast(uint *)b; d += c; } Months later, I'm refactoring the code, and I convert the int * parameter to an invariant, without recognising that it's changing the value of b. Oops. C++'s const would catch this mistake, but if I understand correctly, D will compile it without error. Suddenly the function has moved into the realm of undefined behaviour. I hope I'm wrong. Or did I miss something?
No, you're not missing something. It is a general problem with cast - cast is a blunt instrument which can easily hide problems.
So.. we're going to have to put up with this potential nasty bug? What about a new cast which only removes 'const' and/or 'invariant and prohibit normal cast from removing it. Perhaps calling it 'vary', eg. void f(const int *b, uint c) { int *d = vary() b; }
To avoid a new keyword... int* d = cast(break const) b; IMHO, we want something that looks really nasty.
New keyword? Why a new syntax a at all? This is the kind of stuff that should be possible to do with D's (current or future) meta programming capabilities. Here's a small proof of concept of what can currently be done in D: void main(char[][] args) { const(char *) cfoo; char* foo = unConst(cfoo); writeln(typeid(typeof(unConst(cfoo)))); const(char **) cfoo2; char** foo2 = unConst(cfoo2); writeln(typeid(typeof(unConst(cfoo2)))); } unConstType!(T) unConst(T)(T val) { writeln(typeid(unConstType!(T))); return cast(unConstType!(T)) val; } template unConstType(T : T*) { pragma(msg, "*"); alias unConstType!(T)* unConstType; } template unConstType(T) { alias typeof(T) unConstType; } It only unconstifies pointers (but that's easily extendable to other native types), and the value returned cannot be used as an lvalue, but in any case this is just to demonstrate that no new syntax should be required. The other case, a cast that only changes the core type, and not the modifiers, i.e., something like: const(char*) a = ...; auto b = castBaseType!(int *)(a); //typeof(b) == const(int *) should also be possible to implement at the meta-programming level. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jun 22 2007
next sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Bruno Medeiros wrote:
 and the value returned cannot be used as an lvalue, but 
 in any case this is just to demonstrate that no new syntax should be 
 required.
 
Hum, there is a verbose, but effective, way to go around the lvalue problem, just simply use unConstType directly: char* foo = cast(unConstType!(typeof(cfoo))) cfoo; -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jun 22 2007
prev sibling parent reply Don Clugston <dac nospam.com.au> writes:
Bruno Medeiros wrote:
 Don Clugston wrote:
 Regan Heath wrote:
 Walter Bright Wrote:
 Don Clugston wrote:
 Walter Bright wrote:
 With D, you can cast away const-ness, that is legal. But if you 
 subsequently modify the underlying data, that is undefined behavior.
It sounds that in D, it will be too easy to cast away constness accidentally. With C++, at least you can grep for const_cast and detect potentially dangerous code, and you get a strong visual clue. Suppose I've written a D function like this: void f(int *b, uint c) { // maybe I'm avoiding a compiler warning or something. uint *d = cast(uint *)b; d += c; } Months later, I'm refactoring the code, and I convert the int * parameter to an invariant, without recognising that it's changing the value of b. Oops. C++'s const would catch this mistake, but if I understand correctly, D will compile it without error. Suddenly the function has moved into the realm of undefined behaviour. I hope I'm wrong. Or did I miss something?
No, you're not missing something. It is a general problem with cast - cast is a blunt instrument which can easily hide problems.
So.. we're going to have to put up with this potential nasty bug? What about a new cast which only removes 'const' and/or 'invariant and prohibit normal cast from removing it. Perhaps calling it 'vary', eg. void f(const int *b, uint c) { int *d = vary() b; }
To avoid a new keyword... int* d = cast(break const) b; IMHO, we want something that looks really nasty.
New keyword? Why a new syntax a at all? This is the kind of stuff that should be possible to do with D's (current or future) meta programming capabilities. Here's a small proof of concept of what can currently be done in D:
[snip] Because as long as _any_ cast can remove const, you haven't fixed the problem. The challenge is to make it extremely difficult to remove const, but still possible. And easy to grep for instances of it.
Jun 23 2007
parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Don Clugston wrote:
 Bruno Medeiros wrote:
 Don Clugston wrote:
 Regan Heath wrote:
 Walter Bright Wrote:
 Don Clugston wrote:
 Walter Bright wrote:
 With D, you can cast away const-ness, that is legal. But if you 
 subsequently modify the underlying data, that is undefined behavior.
It sounds that in D, it will be too easy to cast away constness accidentally. With C++, at least you can grep for const_cast and detect potentially dangerous code, and you get a strong visual clue. Suppose I've written a D function like this: void f(int *b, uint c) { // maybe I'm avoiding a compiler warning or something. uint *d = cast(uint *)b; d += c; } Months later, I'm refactoring the code, and I convert the int * parameter to an invariant, without recognising that it's changing the value of b. Oops. C++'s const would catch this mistake, but if I understand correctly, D will compile it without error. Suddenly the function has moved into the realm of undefined behaviour. I hope I'm wrong. Or did I miss something?
No, you're not missing something. It is a general problem with cast - cast is a blunt instrument which can easily hide problems.
So.. we're going to have to put up with this potential nasty bug? What about a new cast which only removes 'const' and/or 'invariant and prohibit normal cast from removing it. Perhaps calling it 'vary', eg. void f(const int *b, uint c) { int *d = vary() b; }
To avoid a new keyword... int* d = cast(break const) b; IMHO, we want something that looks really nasty.
New keyword? Why a new syntax a at all? This is the kind of stuff that should be possible to do with D's (current or future) meta programming capabilities. Here's a small proof of concept of what can currently be done in D:
[snip] Because as long as _any_ cast can remove const, you haven't fixed the problem. The challenge is to make it extremely difficult to remove const, but still possible. And easy to grep for instances of it.
I don't understand. Maybe I'm misunderstanding the problem, but Regan said: "What about a new cast which only removes 'const' and/or 'invariant and prohibit normal cast from removing it." What I proposed was a template that would remove const, and another template that would change the core type, but maintain the modifiers (such as const/invariant) of the original type. This means *one would not use cast (directly) anymore*. This would be easily greppable (if the template's names were standard), and would remove the problem of accidently removing const/invariant, because you would only remove const/invariant (using the appropriate template) when you really want to do just that. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jun 23 2007
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
"Damnit; I don't understand what's going wrong here."

"It looks like you're modifying const data.  Did you cast const away
anywhere?"

"I use unConst, and I already checked all the lines that use that, and
they're fine!"

"But what if you *forgot* to use unConst?"

"... Uh-oh."

The problem with your suggestions is that unless either A) it's
compiler-enforced or B) humans suddenly become infallible, it's really
not solving the problem at all.  If someone forgets to use unConst, or
worse, casts away const *without realising it*, they're screwed.

And I *really* doubt B is going to happen.

	-- Daniel
Jun 23 2007
parent reply Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Daniel Keep wrote:
 "Damnit; I don't understand what's going wrong here."
 
 "It looks like you're modifying const data.  Did you cast const away
 anywhere?"
 
 "I use unConst, and I already checked all the lines that use that, and
 they're fine!"
 
 "But what if you *forgot* to use unConst?"
 
 "... Uh-oh."
 
 The problem with your suggestions is that unless either A) it's
 compiler-enforced or B) humans suddenly become infallible, it's really
 not solving the problem at all.  If someone forgets to use unConst, or
 worse, casts away const *without realising it*, they're screwed.
 
 And I *really* doubt B is going to happen.
 
 	-- Daniel
If you're not meant to write any cast, how the hell does one accidentally write a cast? (http://www.flicklife.com/edecadcc9a8691762b4f/The_Daily_Show_S rver_Crossfire.html :P ) -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jun 25 2007
parent BCS <ao pathlink.com> writes:
Reply to Bruno,

 If you're not meant to write any cast, how the hell does one
 accidentally write a cast?
somthing like this maybe? int[] foo; // an array of ints const int[]* pfoo; // a pointer to an array of ints auto pdfoo = cast(dchar[]*)pfoo; // a pointer to the same data but as dchars (*pdfoo)[5] = 'h'; //oops
Jun 25 2007
prev sibling next sibling parent "Anders Bergh" <anders1 gmail.com> writes:
On 6/21/07, Don Clugston <dac nospam.com.au> wrote:
 To avoid a new keyword...

 int* d = cast(break const) b;

 IMHO, we want something that looks really nasty.
Only jumping in to say that looks excellent! votes++; -- Anders
Jun 23 2007
prev sibling parent reply Jason House <jason.james.house gmail.com> writes:
Don Clugston wrote:
 int* d = cast(break const) b;
 
 IMHO, we want something that looks really nasty.
Based on all the discussion in this thread, I like this alternative best. I'd only insist that if someone did int *d = cast(int*) b; that the compiler would issue an error saying to use cast(break const) instead
Jun 24 2007
parent reply James Dennett <jdennett acm.org> writes:
Jason House wrote:
 Don Clugston wrote:
 int* d = cast(break const) b;

 IMHO, we want something that looks really nasty.
Based on all the discussion in this thread, I like this alternative best. I'd only insist that if someone did int *d = cast(int*) b; that the compiler would issue an error saying to use cast(break const) instead
Interesting: my experience suggests that it shouldn't do so. Those who know enough to use cast(break const) appropriately will find out how to do so. Others would just read the error message and blindly do what it said, pausing only briefly to wonder why the compiler issued an error message if it knew exactly how to "fix" the code. Far too many programmers' first reaction to type warnings/errors is to add casts and mask the problem rather than addressing root cause. -- James
Jun 24 2007
next sibling parent reply Jason House <jason.james.house gmail.com> writes:
James Dennett wrote:
 Jason House wrote:
 Don Clugston wrote:
 int* d = cast(break const) b;

 IMHO, we want something that looks really nasty.
Based on all the discussion in this thread, I like this alternative best. I'd only insist that if someone did int *d = cast(int*) b; that the compiler would issue an error saying to use cast(break const) instead
Interesting: my experience suggests that it shouldn't do so. Those who know enough to use cast(break const) appropriately will find out how to do so. Others would just read the error message and blindly do what it said, pausing only briefly to wonder why the compiler issued an error message if it knew exactly how to "fix" the code. Far too many programmers' first reaction to type warnings/errors is to add casts and mask the problem rather than addressing root cause. -- James
... And what about those programmers who know what they're doing but don't know the ins and outs of the language perfectly? Should they start scouring the documentation and news groups to find the proper fix? IMHO, error messages should be as helpful as possible. Besides giving a new syntax with "break const" embedded within it. I don't think there's really anything else we can do to stop crazy programmers who'll do anything to get their code to function. D has many cool features that attracted me to use it, but I'm already near my limit of frustration using such a volatile language. PS: How do you fix a large program that gives just this one compilation error (with gdc after compiling and running perfectly with dmd): /include/d/4.1.1/std/traits.d:134: Error: void initializer has no value $ head -n 134 /include/d/4.1.1/std/traits.d | tail -n 3 template isStaticArray_impl(T) { const T inst = void;
Jun 24 2007
next sibling parent James Dennett <jdennett acm.org> writes:
Jason House wrote:
 James Dennett wrote:
 Jason House wrote:
 Don Clugston wrote:
 int* d = cast(break const) b;

 IMHO, we want something that looks really nasty.
Based on all the discussion in this thread, I like this alternative best. I'd only insist that if someone did int *d = cast(int*) b; that the compiler would issue an error saying to use cast(break const) instead
Interesting: my experience suggests that it shouldn't do so. Those who know enough to use cast(break const) appropriately will find out how to do so. Others would just read the error message and blindly do what it said, pausing only briefly to wonder why the compiler issued an error message if it knew exactly how to "fix" the code. Far too many programmers' first reaction to type warnings/errors is to add casts and mask the problem rather than addressing root cause. -- James
... And what about those programmers who know what they're doing but don't know the ins and outs of the language perfectly?
They'll know what to do. This isn't obscure; knowing how to handle issues such as constness is basic to any language which supports such a notion. Programmers who "Know what they're doing" know what they're doing. They're just in the minority.
 Should they
 start scouring the documentation and news groups to find the proper fix?
Even if they didn't know, it shouldn't take much to find the answer. Typing the error message into Google would likely do the job.
 IMHO, error messages should be as helpful as possible.  Besides giving a
 new syntax with "break const" embedded within it.  I don't think there's
 really anything else we can do to stop crazy programmers who'll do
 anything to get their code to function. 
You can't stop people writing bad code, but you can avoid making the bad code easier to write than the good code in many cases. -- James
Jun 24 2007
prev sibling parent Don Clugston <dac nospam.com.au> writes:
Jason House wrote:
 James Dennett wrote:
 Jason House wrote:
 Don Clugston wrote:
 int* d = cast(break const) b;

 IMHO, we want something that looks really nasty.
Based on all the discussion in this thread, I like this alternative best. I'd only insist that if someone did int *d = cast(int*) b; that the compiler would issue an error saying to use cast(break const) instead
Interesting: my experience suggests that it shouldn't do so. Those who know enough to use cast(break const) appropriately will find out how to do so. Others would just read the error message and blindly do what it said, pausing only briefly to wonder why the compiler issued an error message if it knew exactly how to "fix" the code. Far too many programmers' first reaction to type warnings/errors is to add casts and mask the problem rather than addressing root cause. -- James
... And what about those programmers who know what they're doing but don't know the ins and outs of the language perfectly? Should they start scouring the documentation and news groups to find the proper fix?
In general, I agree with you, but remember that in this case, cast(break const) is an ugly hack which leads you into undefined behaviour. It's not like C++'s const_cast<> which is well-defined. I don't think we want Microsoft's paperclip: "You like you're trying to hang yourself, would you some help?". <g> Hopefully breaking const is very, very rare. If a novice program actually needs to break const in ordinary situations, there's a big language problem.
Jun 24 2007
prev sibling parent reply Carlos Santander <csantander619 gmail.com> writes:
James Dennett escribió:
 Jason House wrote:
 Don Clugston wrote:
 int* d = cast(break const) b;

 IMHO, we want something that looks really nasty.
Based on all the discussion in this thread, I like this alternative best. I'd only insist that if someone did int *d = cast(int*) b; that the compiler would issue an error saying to use cast(break const) instead
Interesting: my experience suggests that it shouldn't do so. Those who know enough to use cast(break const) appropriately will find out how to do so. Others would just read the error message and blindly do what it said, pausing only briefly to wonder why the compiler issued an error message if it knew exactly how to "fix" the code. Far too many programmers' first reaction to type warnings/errors is to add casts and mask the problem rather than addressing root cause. -- James
But they're going to find it anyway. They'll read an error message, come here and ask what that means, someone will tell them to use cast(break const), and they'll be left wondering why the compiler couldn't tell that in the first place. Not only that, but after a while it'll become FAQ material. I agree with your point, but not much will be gained in the end. -- Carlos Santander Bernal
Jun 24 2007
parent torhu <fake address.dude> writes:
Carlos Santander wrote:
 James Dennett escribió:
 Jason House wrote:
 Don Clugston wrote:
 int* d = cast(break const) b;

 IMHO, we want something that looks really nasty.
Based on all the discussion in this thread, I like this alternative best. I'd only insist that if someone did int *d = cast(int*) b; that the compiler would issue an error saying to use cast(break const) instead
Interesting: my experience suggests that it shouldn't do so. Those who know enough to use cast(break const) appropriately will find out how to do so. Others would just read the error message and blindly do what it said, pausing only briefly to wonder why the compiler issued an error message if it knew exactly how to "fix" the code. Far too many programmers' first reaction to type warnings/errors is to add casts and mask the problem rather than addressing root cause. -- James
But they're going to find it anyway. They'll read an error message, come here and ask what that means, someone will tell them to use cast(break const), and they'll be left wondering why the compiler couldn't tell that in the first place. Not only that, but after a while it'll become FAQ material. I agree with your point, but not much will be gained in the end.
What if the error message is something like 'can't assign const int* b to non-const int* d'? If they really want a const cast, they'll then go look at the docs for cast expressions to see why it didn't work. Otherwise, they'll try making d a const.
Jun 24 2007
prev sibling parent Jascha Wetzel <firstname mainia.de> writes:
Don Clugston wrote:
 It sounds that in D, it will be too easy to cast away constness 
 accidentally.
 With C++, at least you can grep for const_cast and detect potentially 
 dangerous code, and you get a strong visual clue.
 Suppose I've written a D function like this:
 
 void f(int *b, uint c)
 {
   // maybe I'm avoiding a compiler warning or something.
   uint *d = cast(uint *)b;
   d += c;
 }
 
 Months later, I'm refactoring the code, and I convert the int * 
 parameter to an invariant, without recognising that it's changing the 
 value of b. Oops.
a syntactical search would be needed instead of a grep. until refactoring tools for D surface, an optional compiler warning for casts resulting in undefined behaviour would be nice...
Jun 21 2007
prev sibling parent Daniel Keep <daniel.keep.lists gmail.com> writes:
In addition to Brad's response, note that this is *not* "casting
invariant away".  You are *adding* invariant[1].  This might be more
accurate:

extern(C) void initSomeSubsystem(char * initializationString);

int main()
{
    invariant char * initString = "...";
    // Cast away invariantness of initString so we can pass it to the C
    // function; be it on your head if the function tries to change it!
    initSomeSubsystem( cast(char*)initString );
}

	-- Daniel

[1] Although it's already there, so it wouldn't have any effect, I don't
think.
Jun 18 2007