digitalmars.D - One more shot at this const thing.
- Dave (131/131) Jul 27 2006 I can still hear the horse whinnying, so I thought I'd beat it with a
- Reiner Pope (13/22) Jul 27 2006 This presumably has the consequence that you can't get a pointer to a
- Dave (53/81) Jul 27 2006 You're right and that (c.ptr) would need to be disallowed, probably
- Andrei Khropov (15/15) Jul 27 2006 Reiner Pope wrote:
I can still hear the horse whinnying, so I thought I'd beat it with a
slightly different club <g> And that would be taking the best of C++
const modified to be more in line with D goals. I don't think the exact
same has been outlined before, so if it has I apologize.
IIRC Walter's primary (and valid IMO) arguments against C++ const are:
1) it does not guarantee anything to a code maintainer
2) it does not guarantee anything to the compiler
3) it's ugly (const sprinkled everywhere).
But, I'm beginning to think that maybe C++'s const [with roughly the
same compiler enforcement rules but slightly different semantics and
syntax more applicable to D] would work Ok.
Also maybe const would apply only to built-in reference types (including
class objects) and not pointers, justifiable because D has these
built-in whereas C++ doesn't. The same 'const' would not apply to value
types either, for simplicity.
Consider the following C++ code:
class C
{
public:
const char *str;
C(const char *val): str(val)
{
}
char
const * // const reference data return value
foo(char // const reference data
const * // const reference data
const val // const reference
) const // const object
{
val = new char[100]; // error: assignment of read-only parameter
val[0] = 'a'; // error: assignment of read-only location
str = new char[100]; // error: assign to member of const object
str[0] = 'a'; // error: assignment of read-only location
return str;
}
int i;
void bar(int val) { i = val; }
int baz() const
{
return i;
}
};
int main()
{
C c("test string");
char* str, *st2;
st2 = c.foo(str); // error: invalid conversion const char* to char*
const char* st3 = c.foo(str); // Ok
return 0;
}
int bar(const C &c)
{
c.i = 10; // error: assignment of data-member in read-only structure
c.foo("test");
c.bar(10); // error: discards qualifiers
return c.baz() * 10;
}
So in D this would be:
class C
{
const char[] str;
this(char[] val)
{
str = val;
}
// const reference & data for return value, argument and object
// ugly, but necessary and not nearly as ugly as C++
const char[] foo(const char[] val) const
{
val = new char[100]; // error: assignment of read-only parameter
val[0] = 'a'; // error: assignment of read-only location
str = new char[100]; // error: assign to member of const object
str[0] = 'a'; // error: assignment of read-only location
return str;
}
int i;
void bar(int val) { i = val; }
int baz() const
{
return i;
}
}
void main()
{
C c = new C("test string");
char[] str, st2;
st2 = c.foo(str); // error: conversion from const char[] to char[]
const char[] st3 = c.foo(str); // Ok
// The C++ rules plus all this looks readily enforceable by the
// compiler to me.
st2 = cast(char[])c.foo(str); // error: invalid cast
st2 = st3; // error
str[0] = st3[0]; // Ok
st3 = st2; // error
st3 = cast(const char[]) st2; // error: invalid cast
st3[0] = st2[0]; // error
const char* ptr; // error, invalid type (how much broken code?)
}
int bar(const C c)
{
c.i = 10; // error
c.foo("test"); // Ok
c.bar(10); // error
return c.baz * 10; // Ok
}
The compiler can't enforce everything you can do to a const in D. To
make it so that const can actually be meaningful, additional language
could be added to the spec. That language could be as simple as
something like: "subverting const can result in undefined behavior".
This spec. language is justifiable (again) because D has built-in
reference types that C++ doesn't, and it's done like that for numerous
other things in D (e.g.: missing return statements and order of
evaluation) so it's not inconsistent.
I think all of the above is enforceable for a compiler (so it's
meaningful in the general case) and also is not as ugly or convoluted as
C++ syntax. I believe it takes care of the issues of a) not having any
help by the compiler on enforcing COW and b) being able to pass class
and other reference objects into a function and 'guarantee' that the
object not be modified inside the function.
The actual keyword used is not important.
One of the major reasons Java is slow is because of all the data
duplication that needs to go on. D needs to avoid this... Billions of $
and years of research into Java GC and runtime optimizers has not solved
the problem, leading me to believe it never will. If D gets the same rap
as Java on the perf. issues, game over. Likewise if programmer-only
enforced COW turns out to be a bug-ridden maintenance nightmare for
corporate programmers out there.
Thoughts on this method of 'const' for D?
Thanks,
- Dave
Jul 27 2006
Dave wrote:Also maybe const would apply only to built-in reference types (including class objects) and not pointers, justifiable because D has these built-in whereas C++ doesn't. The same 'const' would not apply to value types either, for simplicity.This presumably has the consequence that you can't get a pointer to a const object? Otherwise it could be trivially subverted: const char[] c; char* ptr = c.ptr; *ptr = 5; // Whoops! Const violation! Pointers do seem to be one of the main sources of trouble with const, though, so it's an interesting idea.The compiler can't enforce everything you can do to a const in D. To make it so that const can actually be meaningful, additional language could be added to the spec. That language could be as simple as something like: "subverting const can result in undefined behavior".Wouldn't that then put the onus back on the programmer to avoid const violations? Back to square one? Unless, of course, a const violation was explicitly obvious, in which case it would be an entirely different matter. Cheers, Reiner
Jul 27 2006
Reiner Pope wrote:Dave wrote:You're right and that (c.ptr) would need to be disallowed, probably along with other things I may have forgotten.Also maybe const would apply only to built-in reference types (including class objects) and not pointers, justifiable because D has these built-in whereas C++ doesn't. The same 'const' would not apply to value types either, for simplicity.This presumably has the consequence that you can't get a pointer to a const object? Otherwise it could be trivially subverted: const char[] c; char* ptr = c.ptr; *ptr = 5; // Whoops! Const violation!Pointers do seem to be one of the main sources of trouble with const, though, so it's an interesting idea.I don't think so because you'd have to do something pretty explicit to subvert the compile-time checks (as in the original post). I think the compile-time checks in the original post are doable because most of them have been done w/ C++ implementations for years now and the ones I added should not be too difficult either (IM0). The stuff I added was to also not allow a const to be assigned to a non-const [or vice-versa] with or without casting from one to the other: const char[] a; char[] b; ... b = a; // error b = cast(char[])a; // error ... a = b; // error a = cast(const char[])b; // error Now of course, another check would be that the .ptr property would not be allowed for const ref. types (as you caught above). What about things like multi-dim arrays of reference objects? Currently in C++: void foo(MyClass const* const* const* const arg) { // error: assignment of read-only parameter arg = new MyClass**[10]; // error: assignment of read-only location arg[0] = new MyClass*[10]; // error: assignment of read-only location arg[0][0] = new MyClass(); // error: assignment of member of read-only structure arg[0][0]->i = 10; } In D: // In D, const means "readonly" for all members of any type of // aggregate, just like it would if arg was 'const MyClass arg' // or const char[] arg. void foo(const MyClass[][] arg) { // error: assignment of read-only parameter arg = new MyClass[][10]; // error: assignment of read-only location arg[0] = new MyClass[10]; // error: assignment of read-only location arg[0][0] = new MyClass; // error: assignment of member of read-only structure arg[0][0].i = 10; } The idea is to use the error checking that already works for C++, extend it to not be easily subverted by casting or simple assignment and apply it to all members of any type of reference aggregate. Thanks, - DaveThe compiler can't enforce everything you can do to a const in D. To make it so that const can actually be meaningful, additional language could be added to the spec. That language could be as simple as something like: "subverting const can result in undefined behavior".Wouldn't that then put the onus back on the programmer to avoid const violations? Back to square one? Unless, of course, a const violation wasexplicitly obvious, in which case it would be an entirely different matter. Cheers, Reiner
Jul 27 2006
Reiner Pope wrote: Should be: ---------------------------------------------------------- const char[] c; char* ptr = c.ptr; // Compilation error: cannot cast away const *ptr = 5; ---------------------------------------------------------- const char[] c; const char* ptr = c.ptr; // ok: pointer to const char *ptr = 5; // Compilation error: try to assign to a const value ---------------------------------------------------------- That's how it works in C++ (and it's correct). The only way to break this system is to use explicit casts. -- AKhropov
Jul 27 2006









Dave <Dave_member pathlink.com> 