June 30, 2005 Re: Reference counting | ||||
---|---|---|---|---|
| ||||
Posted in reply to Regan Heath | Oh, before you get the impression I think we *need* opAssign... I don't.
It all stems from a desire to protect data and/or be totally sure about when to COW and when not to COW. Guessing, which is where we are now is inefficient and seems like voodoo magic as opposed to computer science.
We might want to return a slice to some private data, eg.
class Foo {
private char[] data;
public char[] get() { return data[0..4]; }
}
Foo f = new Foo();
char[] p = f.get();
p[1] = 'd'; //oops they just nuked it.
So, we're left saying:
class Foo {
private char[] data;
public char[] get() { return data[0..4].dup; }
}
which works, but in cases where they didn't write to it, or also thought "I should dup this before I write to it" (COW) we have a wasted an allocation or 2.
The best soln I've read here / thought of is to enforce some sort of "readonly" (be it the oft maligned "const" or some other keyword) the result would be a *compile* time enforcement of "readonly", eg.
class Foo {
private char[] data;
public readonly char[] get() { return data[0..4]; }
}
Foo f = new Foo();
char[] p = f.get();
p[1] = 'd'; //error 'p' is readonly
(note, "readonly" is not required to be applied to the declaration of p. This is an important difference between this idea and 'const' from C++ "readonly char[]" is _not_ a seperate type to "char[]" it's simply achar[] with a readonly flagged to true during compile)
Further, 'in' parameters also require readonly enforcement, eg.
Foo f = new Foo();
mutate(f.get());
void mutate(char[] p) {
p[0] = 'd'; //error p is in/readonly
}
if a function wants to modify a parameter, that parameter should be 'out' or 'inout', meaning, if we re-write mutate:
Foo f = new Foo();
mutate(f.get()); //error, f.get() is readonly
void mutate(inout char[] p) {
p[0] = 'd'; //no error
}
It's compile time checking, all the compiler need do is flag variables as being readonly, and pass that flag on during assignment (as in the case of "char[] p = f.get();" above). It's not the "const hell" some see in C++.
With these abilities I believe the requirement for Auto pointers and reference counting is lessened (perhaps removed entirely)
Regan
p.s. This has got to be the 10th time I've posted this idea, I'm sorry if I sound like a broken record but I've yet to hear any opposition to it, from anyone, Walter included.
On Thu, 30 Jun 2005 22:58:44 +1200, Regan Heath <regan@netwin.co.nz> wrote:
> On Thu, 30 Jun 2005 11:19:04 +0100, Stewart Gordon <smjg_1998@yahoo.com> wrote:
>> Regan Heath wrote:
>>> I got little or no comment on this idea/code I posted to another (rather large) thread here, I figured I'd post it again and see what people thought.
>>> After reading this thread (the one I mention above) and also this one Andrew dug up:
>>> http://www.digitalmars.com/d/archives/7988.html
>>> It appears you need a type that:
>>> 1 - is stack based.
>>> 2 - has deterministic destruction.
>>> 3 - has opAssign.
>>
>> You can give a struct or class a method called opAssign if you want. Whether
>>
>> qwert = yuiop;
>>
>> will act as syntactic sugar for it is another matter.
>
> True, but not the problem.. read on :)
>
>>> It seems (from recent posts) that Walter plans to put auto classes on the stack, so they'd then fulfil requirements 1 and 2.
>>> But what about 3? To me, opAssign isn't generally a good idea for classes (assigning a value to a reference seems illogical) but would be a good for structs (they're value types, assignment assigns a value).
>>> Can you do reference counting without opAssign?
>> <snip>
>>
>> Yes.
>
> Then please enlighten me. I've tried, it 'almost' works but it suffers the fatal flaw shown below.
>
>> Of the claims by various people that we "need" overloading of the assignment operator, I'm yet to see one that's true. AFAIC if you want something to the effect of custom assignment behaviour, you can create your own method with whatever name that does it. Can anyone supply an example, let alone a real-world one, that doesn't work unless the aforementioned syntactic sugar is available?
>
> The problem, as I see it, is that it's not just syntactic sugar. You cannot prevent reference assignment in cases where the idea/technique requires it. As in these 2 cases. (reference counting, auto pointer)
>
> I've tried to write a reference counting implementation and also an auto pointer implementation using the recommended practices found on the D website. Both work, both use a "copy constructor" instead of opAssign (as suggested by the D spec/website). The problem is this:
>
> auto AutoPtr p = new AutoPtr(object); //create ptr passing object, p owns object. (or "pwns" for those of you..)
> auto AutoPtr s;
>
> s = new AutoPtr(p); //no problem, the copy constructor transfers ownership.
> s = p; //problem, s now refers to p, both 'own' "object".
>
> The same problem occurs with the reference counting implementation. s becomes a 2nd reference to object but the reference count does not increase.
>
> We can 'ask' people not to use a straight reference assignment but we cannot enforce it.
>
> Logically I agree, opAssign makes no sense for reference types, you assign references to reference types *not* values. opAssign would effectively change a reference assignment into a value assignment in a non obvious way. I agree with all this reasoning, however, the problem remains.
>
> Regan
|
June 30, 2005 Re: Reference counting | ||||
---|---|---|---|---|
| ||||
Posted in reply to Regan Heath | "Regan Heath" <regan@netwin.co.nz> wrote in message news:opss0boj0r23k2f5@nrage.netwin.co.nz... >I got little or no comment on this idea/code I posted to another (rather large) thread here, I figured I'd post it again and see what people thought. > > After reading this thread (the one I mention above) and also this one > Andrew dug up: > http://www.digitalmars.com/d/archives/7988.html > > It appears you need a type that: > > 1 - is stack based. > 2 - has deterministic destruction. > 3 - has opAssign. > > It seems (from recent posts) that Walter plans to put auto classes on the stack, so they'd then fulfil requirements 1 and 2. > > But what about 3? To me, opAssign isn't generally a good idea for classes (assigning a value to a reference seems illogical) but would be a good for structs (they're value types, assignment assigns a value). > > Can you do reference counting without opAssign? See the section in the Memory Management doc about reference counting. It says the add/release must be explicit. Perhaps Walter could expound on that design decision somewhere to address people's concerns. Personally I have no beef with making non-trivial memory mangement explicit since it should be rarely needed. And in those cases having it explicit will alert code maintainers to what is going on. Also it avoids having to add special "give me a reference without manipulating the count for performance" issue. On the other hand by making it explicit one has to advertise the fact it uses fancy memory management so that users know to increment and decrement the count. Currently D errs on the side of simplicity and making code explicit - which is a fairly common tactic in D in general. |
June 30, 2005 Re: Reference counting | ||||
---|---|---|---|---|
| ||||
Posted in reply to Regan Heath | > The best soln I've read here / thought of is to enforce some sort of "readonly" (be it the oft maligned "const" or some other keyword) the result would be a *compile* time enforcement of "readonly", eg. > > class Foo { > private char[] data; > public readonly char[] get() { return data[0..4]; } > } > > Foo f = new Foo(); > char[] p = f.get(); > p[1] = 'd'; //error 'p' is readonly > > (note, "readonly" is not required to be applied to the declaration of p. This is an important difference between this idea and 'const' from C++ "readonly char[]" is _not_ a seperate type to "char[]" it's simply achar[] with a readonly flagged to true during compile) Hmm. What about char[] p = f.get(); char[] q = p; q[1] = 'd'; //is 'q' readonly? or char[] p = test ? some_string : f.get(); p[1] = 'd'; //is 'p' readonly if some_string isn't? or void foo(char[] x){ x[1] = 'd'; } char[] p = f.get(); foo(p); I think it would need to be carried around as a different type. > Further, 'in' parameters also require readonly enforcement, eg. > > Foo f = new Foo(); > mutate(f.get()); > > void mutate(char[] p) { > p[0] = 'd'; //error p is in/readonly > } > > if a function wants to modify a parameter, that parameter should be 'out' or 'inout', meaning, if we re-write mutate: > > Foo f = new Foo(); > mutate(f.get()); //error, f.get() is readonly > > void mutate(inout char[] p) { > p[0] = 'd'; //no error > } I could see explicit 'in' array parameters are readonly. I don't think that would cause much pain. It wouldn't have any effect on non-array parameters. Making implicit 'in' array parameters readonly would be too much IMHO. It is non-trivial making an 'in' parameter into 'inout' since 'inout' requires an lvalue while 'in' simply requires an rvalue. > It's compile time checking, all the compiler need do is flag variables as being readonly, and pass that flag on during assignment (as in the case of "char[] p = f.get();" above). It's not the "const hell" some see in C++. > > With these abilities I believe the requirement for Auto pointers and reference counting is lessened (perhaps removed entirely) > > Regan > > p.s. This has got to be the 10th time I've posted this idea, I'm sorry if I sound like a broken record but I've yet to hear any opposition to it, from anyone, Walter included. |
June 30, 2005 Re: Reference counting | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ben Hinkle | On Thu, 30 Jun 2005 07:38:01 -0400, Ben Hinkle <ben.hinkle@gmail.com> wrote: > "Regan Heath" <regan@netwin.co.nz> wrote in message > news:opss0boj0r23k2f5@nrage.netwin.co.nz... >> I got little or no comment on this idea/code I posted to another (rather >> large) thread here, I figured I'd post it again and see what people >> thought. >> >> After reading this thread (the one I mention above) and also this one >> Andrew dug up: >> http://www.digitalmars.com/d/archives/7988.html >> >> It appears you need a type that: >> >> 1 - is stack based. >> 2 - has deterministic destruction. >> 3 - has opAssign. >> >> It seems (from recent posts) that Walter plans to put auto classes on the >> stack, so they'd then fulfil requirements 1 and 2. >> >> But what about 3? To me, opAssign isn't generally a good idea for classes >> (assigning a value to a reference seems illogical) but would be a good for >> structs (they're value types, assignment assigns a value). >> >> Can you do reference counting without opAssign? > > See the section in the Memory Management doc about reference counting. It > says the add/release must be explicit. Indeed. This strikes me as being rather bug intolerant i.e. open for mistakes and bugs to creep in. I am fine with it being this explicit: RefCnt r = new RefCnt(object); //explicit RefCnt s; s = new RefCent(r); //explicit However the problem is actually this: s = r; //needs to be disallowed or to inc refcnt. > Perhaps Walter could expound on that design decision somewhere to address people's concerns. Yes please. > Personally I have no beef with making non-trivial memory mangement explicit since it should be > rarely needed. And in those cases having it explicit will alert code > maintainers to what is going on. I agree. > Also it avoids having to add special "give me a reference without manipulating the count forperformance" issue. Ah, but that is the problem. > On the other hand by making it explicit one has to advertise the fact it uses fancy memory management so that users know to increment and decrement the count. So, you're saying it should be done: RefCnt r = new RefCnt(object); r.inc(); //perhaps optional?; RefCnt s = r; s.inc(); the room for error here is rather larger than I'd like. > Currently D errs on the side of simplicity and making code explicit - which is a fairly common tactic in D in general. I am a big believer in simple code and making odd things explicit. What I want in this case is something simple, that is bug tolerant. I don't mind if it can be abused/broken with intent, it just needs to be accident(bug) tolerant. Regan |
June 30, 2005 Re: Reference counting | ||||
---|---|---|---|---|
| ||||
Posted in reply to Ben Hinkle | On Thu, 30 Jun 2005 07:58:33 -0400, Ben Hinkle <ben.hinkle@gmail.com> wrote: > Hmm. What about > char[] p = f.get(); > char[] q = p; > q[1] = 'd'; //is 'q' readonly? Yes. (as I said) The compile time readonly flag is passed on during assignment. > or > char[] p = test ? some_string : f.get(); > p[1] = 'd'; //is 'p' readonly if some_string isn't? Ahh, this is the first example (anyones given me) where we require runtime checking also. The other half of my idea is that we have runtime flags also. They'd be in the same class as array bounds checking, i.e. disabled by -release. > or > void foo(char[] x){ x[1] = 'd'; } error 'in' parameters are readonly. > char[] p = f.get(); > foo(p); > I think it would need to be carried around as a different type. Most cases can be handled at compile time without that. The case above that cannot, why does it need to be a seperate type? I dont think it does, it's the same type with a readonly bit set to true (rather than false). This bit can be checked at runtime, and disabled by -release. One goal IMO is to remove the need for us to tell the compiler it's readonly when it can clearly tell all by itself (in many cases) and verify for itself in others (runtime readonly flag). So we don't need to use 'const' or 'readonly' everywhere eg. readonly char[] p = f.get(); char[] s; p = s; //error incompatible types >> Further, 'in' parameters also require readonly enforcement, eg. >> >> Foo f = new Foo(); >> mutate(f.get()); >> >> void mutate(char[] p) { >> p[0] = 'd'; //error p is in/readonly >> } >> >> if a function wants to modify a parameter, that parameter should be 'out' >> or 'inout', meaning, if we re-write mutate: >> >> Foo f = new Foo(); >> mutate(f.get()); //error, f.get() is readonly >> >> void mutate(inout char[] p) { >> p[0] = 'd'; //no error >> } > > I could see explicit 'in' array parameters are readonly. I don't think that would cause much pain. It wouldn't have any effect on non-array parameters. > Making implicit 'in' array parameters readonly would be too much IMHO. It has to be implicit to work. It's not bug tolerant otherwise. > It is non-trivial making an 'in' parameter into 'inout' since 'inout' requires an lvalue while 'in' simply requires an rvalue. I can see how this would be painful for value types, but why is it painful for references (can I have an example?). Regan |
June 30, 2005 Re: Reference counting | ||||
---|---|---|---|---|
| ||||
Posted in reply to Regan Heath | "Regan Heath" <regan@netwin.co.nz> wrote in message news:opss6mi5iy23k2f5@nrage.netwin.co.nz... > On Thu, 30 Jun 2005 07:38:01 -0400, Ben Hinkle <ben.hinkle@gmail.com> wrote: >> "Regan Heath" <regan@netwin.co.nz> wrote in message news:opss0boj0r23k2f5@nrage.netwin.co.nz... >>> I got little or no comment on this idea/code I posted to another (rather large) thread here, I figured I'd post it again and see what people thought. >>> >>> After reading this thread (the one I mention above) and also this one >>> Andrew dug up: >>> http://www.digitalmars.com/d/archives/7988.html >>> >>> It appears you need a type that: >>> >>> 1 - is stack based. >>> 2 - has deterministic destruction. >>> 3 - has opAssign. >>> >>> It seems (from recent posts) that Walter plans to put auto classes on >>> the >>> stack, so they'd then fulfil requirements 1 and 2. >>> >>> But what about 3? To me, opAssign isn't generally a good idea for >>> classes >>> (assigning a value to a reference seems illogical) but would be a good >>> for >>> structs (they're value types, assignment assigns a value). >>> >>> Can you do reference counting without opAssign? >> >> See the section in the Memory Management doc about reference counting. It says the add/release must be explicit. > > Indeed. This strikes me as being rather bug intolerant i.e. open for > mistakes and bugs to creep in. > I am fine with it being this explicit: > > RefCnt r = new RefCnt(object); //explicit > RefCnt s; > s = new RefCent(r); //explicit > > However the problem is actually this: > > s = r; //needs to be disallowed or to inc refcnt. People will want to be able to get a reference without changing the ref count and = does that. There are advantages and disadvantages to using =. To me the main advantage is that = does the right thing and matches the user's expectations of what = does and the main disadvantage is that people who don't know they need to care about ref counting will just use = and get into trouble. To me the advantages outweigh the disadvantages. >> Perhaps Walter could expound on that design decision somewhere to address people's concerns. > > Yes please. > >> Personally I have no beef with making non-trivial memory mangement >> explicit since it should be >> rarely needed. And in those cases having it explicit will alert code >> maintainers to what is going on. > > I agree. > >> Also it avoids having to add special "give me a reference without manipulating the count forperformance" issue. > > Ah, but that is the problem. I'm not sure what you mean. I don't think it's unusual to have a ref-counting API that has a way to get a reference without changing the count. >> On the other hand by making it explicit one has to advertise the fact it uses fancy memory management so that users know to increment and decrement the count. > > So, you're saying it should be done: > > RefCnt r = new RefCnt(object); > r.inc(); //perhaps optional?; > > RefCnt s = r; > s.inc(); > > the room for error here is rather larger than I'd like. Actually I wouldn't even bother having a separate RefCnt class. The ref counting can be mixed into the class of object, much like it is for COM objects. So for example Foo f = new Foo(); // creates with refcount of 1 Foo g = f.incRef; // inc ref count and return this g.decRef; // dec ref count >> Currently D errs on the side of simplicity and making code explicit - which is a fairly common tactic in D in general. > > I am a big believer in simple code and making odd things explicit. What I want in this case is something simple, that is bug tolerant. I don't mind if it can be abused/broken with intent, it just needs to be accident(bug) tolerant. I agree but I think in this case there are a number of factors that make the decision non-trivial. Sometimes it's best to keep everything simple and allow people to shoot themselves in the foot instead of making things complicated to prevent them from shooting themselves in the foot. The weights one gives to all the factors can tip the design in one way or another. The current situation is a reasonable set of compromises IMHO. Adding opAssign would have an impact beyond ref-counting so it's a pretty big step to take. |
June 30, 2005 Re: Reference counting | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrew Fedoniouk | Andrew Fedoniouk wrote: > "Regan Heath" <regan@netwin.co.nz> wrote in message news:opss5jwkvj23k2f5@nrage.netwin.co.nz... > >>On Wed, 29 Jun 2005 09:41:01 -0400, David Medlock <noone@nowhere.com> wrote: >> >>>Regan Heath wrote: >>> >>> >>>>Regan >>> >>>I thought the stack based types were to be useful for speed gains, not managing memory. >> >>Correct. We need/want the reference counter or auto pointer to be as fast as possible because they're typically instantiated and destroyed with abandon. Andrew posted a good example of this, where his graphics library needs to obtain ownership of a graphics context on every call to the 'paint' method, it needs to get it, use it and release it, this function is called very often. >> So why not simply grab the reference once per frame(as in frames per second) and draw all the needed objects? Dont pass the context to the objects, pass the objects to the context owner. I've written code of this type (managing texture memory in OpenGL for one) and its much easier managed on a per-frame basis than per object. (Sorry if I am not understanding this correctly, couldnt find the example you mention) >> >>>Why do you need reference counting? >> >>I don't, yet. Andrew does, now. > > > Reference counting is one of most famous design patterns. > RC as a memory management has its own limitations though ( e.g. cyclic refs ). > Its power in those areas where GC is weak. And vice versa. > So be able to use both will benefit the whole system twice. > > Good example of RC is in implementations of COW strings and shared_ptr > in C++. > > Those are required in C++ because there is no garbage collector. In C++ Copy on write is a programming discipline/design. Its still up to the programmer though, const just helps to keep the programmers honest. With (non-transactional)concurrent programming, there is no way to use reference counting without locks at each assignment/deref. Consider: A resource we call A has 2 references to it, owned by a variable in both Thread T and U. Thread T calls deref, but is blocked before it can check the reference count of A. Thread U calls deref as well, sees the reference count is zero, and deallocates the reference. Thread T awakens, sees the reference count is zero, calls destructor....OOPS. Not to mention modern architectures are very cache-sensitive. When you call destructors throughout your code, you 'thrash the cache' jumping around in memory a lot more than a gc calling the same destructors several times in sequence. I reject the assertion that managing references at each object is superior to an external collector(of any type, not simply memory). All those AddRef/Decref/Ref checks are not free. Managed resources should be managed by the code, not the runtime. I'm sure some will disagree with some(or all) of this, but I have yet to see evidence to the contrary. -DavidM |
June 30, 2005 Re: Reference counting | ||||
---|---|---|---|---|
| ||||
Posted in reply to Regan Heath | Regan Heath wrote: > On Thu, 30 Jun 2005 11:19:04 +0100, Stewart Gordon <smjg_1998@yahoo.com> wrote: <snip> >> Of the claims by various people that we "need" overloading of the assignment operator, I'm yet to see one that's true. AFAIC if you want something to the effect of custom assignment behaviour, you can create your own method with whatever name that does it. Can anyone supply an example, let alone a real-world one, that doesn't work unless the aforementioned syntactic sugar is available? > > The problem, as I see it, is that it's not just syntactic sugar. You cannot prevent reference assignment in cases where the idea/technique requires it. As in these 2 cases. (reference counting, auto pointer) I guess that if you use either of these, you're supposed to know what you're doing and that you have to be careful. Remember that because of the way D is designed, most applications will be using the built-in GC rather than this. As such, a facility for disabling the assignment operator for select types would probably be seen as an unnecessary complexity. > I've tried to write a reference counting implementation and also an auto pointer implementation using the recommended practices found on the D website. Both work, both use a "copy constructor" instead of opAssign (as suggested by the D spec/website). The problem is this: > > auto AutoPtr p = new AutoPtr(object); //create ptr passing object, p owns object. (or "pwns" for those of you..) > auto AutoPtr s; > > s = new AutoPtr(p); //no problem, the copy constructor transfers ownership. > s = p; //problem, s now refers to p, both 'own' "object". http://www.digitalmars.com/d/attribute.html#auto "Assignment to an auto, other than initialization, is not allowed." I just had a go at it with GDC 0.10. It appears from the destruction order that if an auto variable is initialised to another, then the new reference becomes the owning one. However, there seems to be a bug whereby it accepts assignments after initialisation; however, doing this doesn't seem to have any effect on what gets destructed when. Here's the program I just tried: ---------- import std.stream; import std.stdio; class Thing { int value; this(int v) { value = v; } ~this() { writefln("destructed %d", value); } } void main() { auto Thing o = new Thing(1); auto Thing p = new Thing(2); char[] line = std.stream.stdin.readLine(); auto Thing q = line[0] == 'y' ? o : new Thing(3); if (line[1] == 'y') o = q; writefln(q.value); writefln(o.value); } ---------- > The same problem occurs with the reference counting implementation. s becomes a 2nd reference to object but the reference count does not increase. > > We can 'ask' people not to use a straight reference assignment but we cannot enforce it. <snip> True. But I guess what we really need is a clear spec of what happens if one auto is assigned to another. If only by some means (not sure how easy or not it would be to implement) the auto stayed with the original owner, then such reference assignment could be OK (and indeed more efficient) as long as it isn't going to outlive the original. Stewart. -- My e-mail is valid but not my primary mailbox. Please keep replies on the 'group where everyone may benefit. |
June 30, 2005 Re: Reference counting | ||||
---|---|---|---|---|
| ||||
Posted in reply to Regan Heath | Regan Heath wrote:
> On Thu, 30 Jun 2005 07:58:33 -0400, Ben Hinkle <ben.hinkle@gmail.com> wrote:
>
>> Hmm. What about
>> char[] p = f.get();
>> char[] q = p;
>> q[1] = 'd'; //is 'q' readonly?
>
>
> Yes. (as I said) The compile time readonly flag is passed on during assignment.
IMHO, the answer here should be "no". I would (I think) prefer that the line
char[] p = f.get();
flags an error if f.get() happens to return an immutable char[]. Ie, you cannot explicitly assign from an immutable char[] to a char[]. That leaves the problem solvable at compile time. The correct line would be
immutable char[] p = f.get();
Now, for the sake of simplicity I do not think that the compiler should do run-time checking, nor should it prevent you from casting immutability away. IMHO all we want/need is a way to prevent accidental mistakes & be more descriptive in the way a variable is used. Yes, casting away const is bad - but any language with pointers can eventually do it.
Brad
|
June 30, 2005 Re: Reference counting | ||||
---|---|---|---|---|
| ||||
Posted in reply to Regan Heath | Regan (and everyone) please keep posting. Everytime you post an example, it just further shows what a useful and necessary feature this is. So please, keep the posts coming! -Kramer P.S. - Let me just say this. I enjoy reading these examples and posts because other people who are smarter and better programmers explain these things in much clearer ways than I ever could. Having said that, without even knowing if this feature existed or not in D, I went looking for const/readonly so I could use it. It seems like such a logical thing to have to make really robust software. It helps the programmer better state what they want their code to do; the compiler should help. I'm not a compiler writer and don't know how difficult it is, but it just seems right to have it. I would feel better just knowing it's coming even if we didn't get it for several releases. But there's been no word on it. I think that's the frustrating part... In article <opss6km00b23k2f5@nrage.netwin.co.nz>, Regan Heath says... > >Oh, before you get the impression I think we *need* opAssign... I don't. > >It all stems from a desire to protect data and/or be totally sure about when to COW and when not to COW. Guessing, which is where we are now is inefficient and seems like voodoo magic as opposed to computer science. > >We might want to return a slice to some private data, eg. > >class Foo { > private char[] data; > public char[] get() { return data[0..4]; } >} > >Foo f = new Foo(); >char[] p = f.get(); >p[1] = 'd'; //oops they just nuked it. > >So, we're left saying: > >class Foo { > private char[] data; > public char[] get() { return data[0..4].dup; } >} > >which works, but in cases where they didn't write to it, or also thought "I should dup this before I write to it" (COW) we have a wasted an allocation or 2. > >The best soln I've read here / thought of is to enforce some sort of "readonly" (be it the oft maligned "const" or some other keyword) the result would be a *compile* time enforcement of "readonly", eg. > >class Foo { > private char[] data; > public readonly char[] get() { return data[0..4]; } >} > >Foo f = new Foo(); >char[] p = f.get(); >p[1] = 'd'; //error 'p' is readonly > >(note, "readonly" is not required to be applied to the declaration of p. This is an important difference between this idea and 'const' from C++ "readonly char[]" is _not_ a seperate type to "char[]" it's simply achar[] with a readonly flagged to true during compile) > >Further, 'in' parameters also require readonly enforcement, eg. > >Foo f = new Foo(); >mutate(f.get()); > >void mutate(char[] p) { > p[0] = 'd'; //error p is in/readonly >} > >if a function wants to modify a parameter, that parameter should be 'out' or 'inout', meaning, if we re-write mutate: > >Foo f = new Foo(); >mutate(f.get()); //error, f.get() is readonly > >void mutate(inout char[] p) { > p[0] = 'd'; //no error >} > >It's compile time checking, all the compiler need do is flag variables as being readonly, and pass that flag on during assignment (as in the case of "char[] p = f.get();" above). It's not the "const hell" some see in C++. > >With these abilities I believe the requirement for Auto pointers and reference counting is lessened (perhaps removed entirely) > >Regan > >p.s. This has got to be the 10th time I've posted this idea, I'm sorry if I sound like a broken record but I've yet to hear any opposition to it, > from anyone, Walter included. > >On Thu, 30 Jun 2005 22:58:44 +1200, Regan Heath <regan@netwin.co.nz> wrote: > >> On Thu, 30 Jun 2005 11:19:04 +0100, Stewart Gordon <smjg_1998@yahoo.com> wrote: >>> Regan Heath wrote: >>>> I got little or no comment on this idea/code I posted to another >>>> (rather large) thread here, I figured I'd post it again and see what >>>> people thought. >>>> After reading this thread (the one I mention above) and also this >>>> one Andrew dug up: >>>> http://www.digitalmars.com/d/archives/7988.html >>>> It appears you need a type that: >>>> 1 - is stack based. >>>> 2 - has deterministic destruction. >>>> 3 - has opAssign. >>> >>> You can give a struct or class a method called opAssign if you want. Whether >>> >>> qwert = yuiop; >>> >>> will act as syntactic sugar for it is another matter. >> >> True, but not the problem.. read on :) >> >>>> It seems (from recent posts) that Walter plans to put auto classes on >>>> the stack, so they'd then fulfil requirements 1 and 2. >>>> But what about 3? To me, opAssign isn't generally a good idea for >>>> classes (assigning a value to a reference seems illogical) but would >>>> be a good for structs (they're value types, assignment assigns a >>>> value). >>>> Can you do reference counting without opAssign? >>> <snip> >>> >>> Yes. >> >> Then please enlighten me. I've tried, it 'almost' works but it suffers the fatal flaw shown below. >> >>> Of the claims by various people that we "need" overloading of the assignment operator, I'm yet to see one that's true. AFAIC if you want something to the effect of custom assignment behaviour, you can create your own method with whatever name that does it. Can anyone supply an example, let alone a real-world one, that doesn't work unless the aforementioned syntactic sugar is available? >> >> The problem, as I see it, is that it's not just syntactic sugar. You cannot prevent reference assignment in cases where the idea/technique requires it. As in these 2 cases. (reference counting, auto pointer) >> >> I've tried to write a reference counting implementation and also an auto pointer implementation using the recommended practices found on the D website. Both work, both use a "copy constructor" instead of opAssign (as suggested by the D spec/website). The problem is this: >> >> auto AutoPtr p = new AutoPtr(object); //create ptr passing object, p >> owns object. (or "pwns" for those of you..) >> auto AutoPtr s; >> >> s = new AutoPtr(p); //no problem, the copy constructor transfers >> ownership. >> s = p; //problem, s now refers to p, both 'own' "object". >> >> The same problem occurs with the reference counting implementation. s becomes a 2nd reference to object but the reference count does not increase. >> >> We can 'ask' people not to use a straight reference assignment but we cannot enforce it. >> >> Logically I agree, opAssign makes no sense for reference types, you assign references to reference types *not* values. opAssign would effectively change a reference assignment into a value assignment in a non obvious way. I agree with all this reasoning, however, the problem remains. >> >> Regan > |
Copyright © 1999-2021 by the D Language Foundation