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