digitalmars.D - D2 toStringz Return Type
- Mike Parker (29/29) Nov 07 2008 I'm curious as to why toStringz in D2 returns const(char)* instead of
- Sean Kelly (10/43) Nov 07 2008 It affects what values may be passed as parameters to these routines.
- Andrei Alexandrescu (10/43) Nov 07 2008 We want to leave the opportunity open to not duplicate the actual memory...
- Steven Schveighoffer (8/16) Nov 07 2008 My recommendation -- have 2 functions. One which always copies (and ret...
- Andrei Alexandrescu (4/21) Nov 07 2008 You can't quite do that because dynamic conditions establish whether
- Steven Schveighoffer (11/31) Nov 07 2008 I can see how you interpreted it this way.
- Andrei Alexandrescu (9/43) Nov 07 2008 I see. So:
- Steven Schveighoffer (15/58) Nov 07 2008 Using zero terminated strings, even const ones, is a recipe for disaster...
- Andrei Alexandrescu (25/86) Nov 07 2008 Well writable ones are even more of a disaster. Reading random
- Steven Schveighoffer (4/11) Nov 08 2008 Nonsense. Tango currently has such a function with D 1.x, and I've neve...
- ore-sama (1/1) Nov 08 2008 Uh... I used toStringz, but didn't know that it's return type is const a...
- Andrei Alexandrescu (5/9) Nov 08 2008 strcpy sprintf strcat gets, off the top of my head. They are so bad and
- ore-sama (2/11) Nov 08 2008 that's because they get char buffer, not sz, so they can't determine end...
- Andrei Alexandrescu (3/17) Nov 08 2008 Good point.
- ore-sama (2/7) Nov 09 2008 But what can really simplify interoperability is modification of cat and...
- Andrei Alexandrescu (6/16) Nov 09 2008 At a point we thought of quite a few schemes along those lines. At the
- Andrei Alexandrescu (11/23) Nov 08 2008 And it's not me.
- Steven Schveighoffer (14/36) Nov 08 2008 OK, it's only the most used D library. Sorry for the unrelated referenc...
- Andrei Alexandrescu (30/72) Nov 08 2008 They do because dealing in mutable char* fosters transporting bare
I'm curious as to why toStringz in D2 returns const(char)* instead of just a plain char*. Considering that the primary use case foe the function is interfacing with C code, it seems rather pointless to return a const(char)*. Consider the D2 versions of the function declarations in the std.c package. The 'const char *' declarations from C are replaced with 'in char*' from D. To what end? The C side doesn't know and doesn't care about const declarations in D. You could add any sort of modifier you want on the D side and it would serve no real purpose. Then consider all of the existing (and future) bindings to C libraries out there. The vast majority have this sort of function prototype: ===================== // from the C header void someFunc(const char*); // in D extern(C) void someFunc(char*); ====================== This works with D1, but anyone using this library with D2 will either have to change such prototypes to include the 'in' modifier, or cast every call to toStringz. I'm updating Derelict now to work with D2 and found this to be incredibly annoying. But before I go through every API Derelict binds to in order to figure out which char* parameters should get the 'in' modifier, I thought I'd ask here if there is any real purpose in requiring this sort of thing? Is there any major use case for toStringz other than passing null-terminated strings to C functions? If not, then having a const(char)* return type is really superfluous. It doesn't do anything! Can we just remove the const?
Nov 07 2008
Mike Parker wrote:I'm curious as to why toStringz in D2 returns const(char)* instead of just a plain char*. Considering that the primary use case foe the function is interfacing with C code, it seems rather pointless to return a const(char)*. Consider the D2 versions of the function declarations in the std.c package. The 'const char *' declarations from C are replaced with 'in char*' from D. To what end? The C side doesn't know and doesn't care about const declarations in D. You could add any sort of modifier you want on the D side and it would serve no real purpose.It affects what values may be passed as parameters to these routines. String literals, for example, may only be passed as const or invariant parameters in D2.Then consider all of the existing (and future) bindings to C libraries out there. The vast majority have this sort of function prototype: ===================== // from the C header void someFunc(const char*); // in D extern(C) void someFunc(char*); ====================== This works with D1, but anyone using this library with D2 will either have to change such prototypes to include the 'in' modifier, or cast every call to toStringz.Yup. I wish someone had said to use "in" for this from the outset. But conversion isn't too terrible in most cases. I converted all of Tango's C and Posix headers in an afternoon.I'm updating Derelict now to work with D2 and found this to be incredibly annoying. But before I go through every API Derelict binds to in order to figure out which char* parameters should get the 'in' modifier, I thought I'd ask here if there is any real purpose in requiring this sort of thing?Passing const variables.Is there any major use case for toStringz other than passing null-terminated strings to C functions? If not, then having a const(char)* return type is really superfluous. It doesn't do anything! Can we just remove the const?See above :-) Sean
Nov 07 2008
Mike Parker wrote:I'm curious as to why toStringz in D2 returns const(char)* instead of just a plain char*. Considering that the primary use case foe the function is interfacing with C code, it seems rather pointless to return a const(char)*.We want to leave the opportunity open to not duplicate the actual memory underneath the string object. (Right now that opportunity is not effected.)Consider the D2 versions of the function declarations in the std.c package. The 'const char *' declarations from C are replaced with 'in char*' from D. To what end? The C side doesn't know and doesn't care about const declarations in D. You could add any sort of modifier you want on the D side and it would serve no real purpose.C functions should have the most descriptive D signatures. "in" means "const" and "scope". The latter is not yet fully described at the moment, and may be dropped entirely. In that case, "in" remains a shorter synonym for "const".Then consider all of the existing (and future) bindings to C libraries out there. The vast majority have this sort of function prototype: ===================== // from the C header void someFunc(const char*); // in D extern(C) void someFunc(char*); ======================I think the const information should be there in the D version as well.This works with D1, but anyone using this library with D2 will either have to change such prototypes to include the 'in' modifier, or cast every call to toStringz. I'm updating Derelict now to work with D2 and found this to be incredibly annoying. But before I go through every API Derelict binds to in order to figure out which char* parameters should get the 'in' modifier, I thought I'd ask here if there is any real purpose in requiring this sort of thing? Is there any major use case for toStringz other than passing null-terminated strings to C functions? If not, then having a const(char)* return type is really superfluous. It doesn't do anything! Can we just remove the const?I think it's better to keep it. Andrei
Nov 07 2008
"Andrei Alexandrescu" wroteMike Parker wrote:My recommendation -- have 2 functions. One which always copies (and returns char *), and one which does not. This at least leaves a safe alternative for people who have headers that aren't properly constified, and don't want to go through the hassle of looking it up themselves. Also good for those C functions which actually require a mutable char *, since D2 strings are mostly invariant. -SteveI'm curious as to why toStringz in D2 returns const(char)* instead of just a plain char*. Considering that the primary use case foe the function is interfacing with C code, it seems rather pointless to return a const(char)*.We want to leave the opportunity open to not duplicate the actual memory underneath the string object. (Right now that opportunity is not effected.)
Nov 07 2008
Steven Schveighoffer wrote:"Andrei Alexandrescu" wroteYou can't quite do that because dynamic conditions establish whether it's safe to avoid copying or not. AndreiMike Parker wrote:My recommendation -- have 2 functions. One which always copies (and returns char *), and one which does not. This at least leaves a safe alternative for people who have headers that aren't properly constified, and don't want to go through the hassle of looking it up themselves. Also good for those C functions which actually require a mutable char *, since D2 strings are mostly invariant.I'm curious as to why toStringz in D2 returns const(char)* instead of just a plain char*. Considering that the primary use case foe the function is interfacing with C code, it seems rather pointless to return a const(char)*.We want to leave the opportunity open to not duplicate the actual memory underneath the string object. (Right now that opportunity is not effected.)
Nov 07 2008
"Andrei Alexandrescu" wroteSteven Schveighoffer wrote:I can see how you interpreted it this way. What I meant was one is the toStringz as it is today, which might copy and might leave it in-place. This can be used to call C functions that take a const char *. The other function will *always* copy, and will return a mutable char *. This is for when you don't care to look at the function yourself (assuming the author got it correct), or the case where the C function actually does mutate the argument. If the C function does actually require a mutable argument, you are forced to do an extra dup for no reason with today's toStringz. -Steve"Andrei Alexandrescu" wroteYou can't quite do that because dynamic conditions establish whether it's safe to avoid copying or not.Mike Parker wrote:My recommendation -- have 2 functions. One which always copies (and returns char *), and one which does not. This at least leaves a safe alternative for people who have headers that aren't properly constified, and don't want to go through the hassle of looking it up themselves. Also good for those C functions which actually require a mutable char *, since D2 strings are mostly invariant.I'm curious as to why toStringz in D2 returns const(char)* instead of just a plain char*. Considering that the primary use case foe the function is interfacing with C code, it seems rather pointless to return a const(char)*.We want to leave the opportunity open to not duplicate the actual memory underneath the string object. (Right now that opportunity is not effected.)
Nov 07 2008
Steven Schveighoffer wrote:"Andrei Alexandrescu" wroteI see. So: const(char)* toStringzMayOrMayNotCopy(in char[]); char* toStringzWillAlwaysCopy(in char[]); Providing writable zero-terminated strings is a sure recipe for disaster (see the debates around sprintf, strcpy etc.). I think the need for such things is rare and at best avoided entirely by the standard library. If you so wish, you can always use malloc by hand. AndreiSteven Schveighoffer wrote:I can see how you interpreted it this way. What I meant was one is the toStringz as it is today, which might copy and might leave it in-place. This can be used to call C functions that take a const char *. The other function will *always* copy, and will return a mutable char *. This is for when you don't care to look at the function yourself (assuming the author got it correct), or the case where the C function actually does mutate the argument. If the C function does actually require a mutable argument, you are forced to do an extra dup for no reason with today's toStringz. -Steve"Andrei Alexandrescu" wroteYou can't quite do that because dynamic conditions establish whether it's safe to avoid copying or not.Mike Parker wrote:My recommendation -- have 2 functions. One which always copies (and returns char *), and one which does not. This at least leaves a safe alternative for people who have headers that aren't properly constified, and don't want to go through the hassle of looking it up themselves. Also good for those C functions which actually require a mutable char *, since D2 strings are mostly invariant.I'm curious as to why toStringz in D2 returns const(char)* instead of just a plain char*. Considering that the primary use case foe the function is interfacing with C code, it seems rather pointless to return a const(char)*.We want to leave the opportunity open to not duplicate the actual memory underneath the string object. (Right now that opportunity is not effected.)
Nov 07 2008
"Andrei Alexandrescu" wroteSteven Schveighoffer wrote:Using zero terminated strings, even const ones, is a recipe for disaster. Yet, there it is. And it's making me do 2 duplications. The reality is that as soon as you cross the boundary from D to C, you have lost all the safety benefits that D provides, even if the signature is const. The reality is, people are still going to call these functions, either with an extra dup (which buys you nothing in safety), or by editing the bindings to be const (which makes it even more unsafe). The reality is, most of these calls are pretty innocuous. People aren't using sprintf or strcpy, they are using C libraries that do things that D doesn't already do. Most of these are just using char * as a way to pass const strings, it isn't too much to ask for a function that complies. But you probably won't add it. That's ok, I don't use Phobos anyways. I'll be sure to add an appropriate function to Tango while porting it to D2. -Steve"Andrei Alexandrescu" wroteI see. So: const(char)* toStringzMayOrMayNotCopy(in char[]); char* toStringzWillAlwaysCopy(in char[]); Providing writable zero-terminated strings is a sure recipe for disaster (see the debates around sprintf, strcpy etc.). I think the need for such things is rare and at best avoided entirely by the standard library. If you so wish, you can always use malloc by hand.Steven Schveighoffer wrote:I can see how you interpreted it this way. What I meant was one is the toStringz as it is today, which might copy and might leave it in-place. This can be used to call C functions that take a const char *. The other function will *always* copy, and will return a mutable char *. This is for when you don't care to look at the function yourself (assuming the author got it correct), or the case where the C function actually does mutate the argument. If the C function does actually require a mutable argument, you are forced to do an extra dup for no reason with today's toStringz. -Steve"Andrei Alexandrescu" wroteYou can't quite do that because dynamic conditions establish whether it's safe to avoid copying or not.Mike Parker wrote:My recommendation -- have 2 functions. One which always copies (and returns char *), and one which does not. This at least leaves a safe alternative for people who have headers that aren't properly constified, and don't want to go through the hassle of looking it up themselves. Also good for those C functions which actually require a mutable char *, since D2 strings are mostly invariant.I'm curious as to why toStringz in D2 returns const(char)* instead of just a plain char*. Considering that the primary use case foe the function is interfacing with C code, it seems rather pointless to return a const(char)*.We want to leave the opportunity open to not duplicate the actual memory underneath the string object. (Right now that opportunity is not effected.)
Nov 07 2008
Steven Schveighoffer wrote:"Andrei Alexandrescu" wroteWell writable ones are even more of a disaster. Reading random characters can cause the program to fail but does not corrupt its state arbitrarily. So it's good to limit the damage. The C and C++ communities have much more beef with writable stringz's than read-only ones.Steven Schveighoffer wrote:Using zero terminated strings, even const ones, is a recipe for disaster. Yet, there it is."Andrei Alexandrescu" wroteI see. So: const(char)* toStringzMayOrMayNotCopy(in char[]); char* toStringzWillAlwaysCopy(in char[]); Providing writable zero-terminated strings is a sure recipe for disaster (see the debates around sprintf, strcpy etc.). I think the need for such things is rare and at best avoided entirely by the standard library. If you so wish, you can always use malloc by hand.Steven Schveighoffer wrote:I can see how you interpreted it this way. What I meant was one is the toStringz as it is today, which might copy and might leave it in-place. This can be used to call C functions that take a const char *. The other function will *always* copy, and will return a mutable char *. This is for when you don't care to look at the function yourself (assuming the author got it correct), or the case where the C function actually does mutate the argument. If the C function does actually require a mutable argument, you are forced to do an extra dup for no reason with today's toStringz. -Steve"Andrei Alexandrescu" wroteYou can't quite do that because dynamic conditions establish whether it's safe to avoid copying or not.Mike Parker wrote:My recommendation -- have 2 functions. One which always copies (and returns char *), and one which does not. This at least leaves a safe alternative for people who have headers that aren't properly constified, and don't want to go through the hassle of looking it up themselves. Also good for those C functions which actually require a mutable char *, since D2 strings are mostly invariant.I'm curious as to why toStringz in D2 returns const(char)* instead of just a plain char*. Considering that the primary use case foe the function is interfacing with C code, it seems rather pointless to return a const(char)*.We want to leave the opportunity open to not duplicate the actual memory underneath the string object. (Right now that opportunity is not effected.)And it's making me do 2 duplications.Not at all. string s = ...; auto sz = cast(char*) malloc(s.length + 1); sz[0 .. s.length] = s[]; sz[s.length] = 0; If you use it often in an application, put it in a function. I'm not putting it in the standard library.The reality is that as soon as you cross the boundary from D to C, you have lost all the safety benefits that D provides, even if the signature is const.I disagree. You lost automatic checking from the D side when interfacing with C, but if a C function is reliably not mutating its arguments its D signature is better tagged as const. It's a net win.The reality is, people are still going to call these functions, either with an extra dup (which buys you nothing in safety), or by editing the bindings to be const (which makes it even more unsafe). The reality is, most of these calls are pretty innocuous. People aren't using sprintf or strcpy, they are using C libraries that do things that D doesn't already do. Most of these are just using char * as a way to pass const strings, it isn't too much to ask for a function that complies.Maybe I got lucky, but I haven't run across any C libraries that don't use const in signatures. Anyhow the point is superfluous as you, not them, gets to write the D interfacing signatures. Const conveys a world of information. True, that is not 100% enforceable in D and in C alike, as a cast could always ruin things. But it's good if the signature reflects a guarantee that is reasonable and also reasonably easy to observe.But you probably won't add it. That's ok, I don't use Phobos anyways. I'll be sure to add an appropriate function to Tango while porting it to D2.You may want to rethink before putting dangerous functions in widely-used libraries. Returning a writable zero-terminated char* is as dangerous as it gets, and fostering bad coding style too. Andrei
Nov 07 2008
"Andrei Alexandrescu" wroteSteven Schveighoffer wrote:Nonsense. Tango currently has such a function with D 1.x, and I've never heard of any issues with it. I think you have overblown the danger here. -SteveBut you probably won't add it. That's ok, I don't use Phobos anyways. I'll be sure to add an appropriate function to Tango while porting it to D2.You may want to rethink before putting dangerous functions in widely-used libraries. Returning a writable zero-terminated char* is as dangerous as it gets, and fostering bad coding style too.
Nov 08 2008
Uh... I used toStringz, but didn't know that it's return type is const and that people have troubles with this :) AFAIK the only function, writing to its sz argument is strtok, C libraries usually follow popular convention that input strings are "in".
Nov 08 2008
ore-sama wrote:Uh... I used toStringz, but didn't know that it's return type is const and that people have troubles with this :) AFAIK the only function, writing to its sz argument is strtok, C libraries usually follow popular convention that input strings are "in".strcpy sprintf strcat gets, off the top of my head. They are so bad and dangerous, for some safer versions were introduced and are universally recommended: strncpy snprintf strncat. Andrei
Nov 08 2008
Andrei Alexandrescu Wrote:ore-sama wrote:that's because they get char buffer, not sz, so they can't determine end of buffer scanning for null (strcat does, but it's still buffer). toStringz has nothing to do with these functions.Uh... I used toStringz, but didn't know that it's return type is const and that people have troubles with this :) AFAIK the only function, writing to its sz argument is strtok, C libraries usually follow popular convention that input strings are "in".strcpy sprintf strcat gets, off the top of my head. They are so bad and dangerous, for some safer versions were introduced and are universally recommended: strncpy snprintf strncat.
Nov 08 2008
ore-sama wrote:Andrei Alexandrescu Wrote:Good point. Andreiore-sama wrote:that's because they get char buffer, not sz, so they can't determine end of buffer scanning for null (strcat does, but it's still buffer). toStringz has nothing to do with these functions.Uh... I used toStringz, but didn't know that it's return type is const and that people have troubles with this :) AFAIK the only function, writing to its sz argument is strtok, C libraries usually follow popular convention that input strings are "in".strcpy sprintf strcat gets, off the top of my head. They are so bad and dangerous, for some safer versions were introduced and are universally recommended: strncpy snprintf strncat.
Nov 08 2008
Andrei Alexandrescu Wrote:But what can really simplify interoperability is modification of cat and idup operators so that they allocate an extra char and write null to it, so toStringz can be replaced with idup ar avoided completely.that's because they get char buffer, not sz, so they can't determine end of buffer scanning for null (strcat does, but it's still buffer). toStringz has nothing to do with these functions.Good point.
Nov 09 2008
ore-sama wrote:Andrei Alexandrescu Wrote:At a point we thought of quite a few schemes along those lines. At the end of the day, however, it's not that often one does need zero-terminated strings in D so it seemed onerous to do the work always just in case someone needs a stringz. AndreiBut what can really simplify interoperability is modification of cat and idup operators so that they allocate an extra char and write null to it, so toStringz can be replaced with idup ar avoided completely.that's because they get char buffer, not sz, so they can't determine end of buffer scanning for null (strcat does, but it's still buffer). toStringz has nothing to do with these functions.Good point.
Nov 09 2008
Steven Schveighoffer wrote:"Andrei Alexandrescu" wroteThat doesn't mean much of anything.Steven Schveighoffer wrote:Nonsense. Tango currently has such a function with D 1.x, and I've never heard of any issues with it.But you probably won't add it. That's ok, I don't use Phobos anyways. I'll be sure to add an appropriate function to Tango while porting it to D2.You may want to rethink before putting dangerous functions in widely-used libraries. Returning a writable zero-terminated char* is as dangerous as it gets, and fostering bad coding style too.I think you have overblown the danger here.And it's not me. http://marc.info/?l=bugtraq&m=105673479925709&q=p4 http://seclists.org/vulnwatch/2007/q1/0069.html http://www.mail-archive.com/popt-devel rpm5.org/msg00072.html http://sourceware.org/ml/gdb-patches/2005-03/msg00237.html http://mailman.mit.edu/pipermail/krbdev/2008-October/007022.html I got bored of pasting. Just google for strcpy sprintf strcat gets. Again: please rethink. Andrei
Nov 08 2008
"Andrei Alexandrescu" wroteSteven Schveighoffer wrote:OK, it's only the most used D library. Sorry for the unrelated reference. Phobos 1 had the same issue BTW."Andrei Alexandrescu" wroteThat doesn't mean much of anything.Steven Schveighoffer wrote:Nonsense. Tango currently has such a function with D 1.x, and I've never heard of any issues with it.But you probably won't add it. That's ok, I don't use Phobos anyways. I'll be sure to add an appropriate function to Tango while porting it to D2.You may want to rethink before putting dangerous functions in widely-used libraries. Returning a writable zero-terminated char* is as dangerous as it gets, and fostering bad coding style too.I got bored of reading. Like ore-sama said, your examples have nothing to do with zero-terminated strings. Some of them even outlaw functions that take const char *. So by your argument that proof is in these pages, if you are going to condemn functions that take mutable zero-terminated strings, you have to do the same for const zero terminated strings. And most of them are talking about buffer overflow attacks, which would be unlikely (maybe impossible? I don't know much about it) with heap-allocated strings, which is what alwaysCopyAndReturnMutableStringz returns. None of this changes my view. And I'm done arguing, take away from this what you will. -SteveI think you have overblown the danger here.And it's not me. http://marc.info/?l=bugtraq&m=105673479925709&q=p4 http://seclists.org/vulnwatch/2007/q1/0069.html http://www.mail-archive.com/popt-devel rpm5.org/msg00072.html http://sourceware.org/ml/gdb-patches/2005-03/msg00237.html http://mailman.mit.edu/pipermail/krbdev/2008-October/007022.html I got bored of pasting. Just google for strcpy sprintf strcat gets. Again: please rethink.
Nov 08 2008
Steven Schveighoffer wrote:"Andrei Alexandrescu" wroteThey do because dealing in mutable char* fosters transporting bare pointers as entire mutable strings. (A slightly safer scenario used in C is to always transport the length along with the mutable char*, or to traffic in char** with an understanding it is heap allocated (as e.g. GNU's readline does).) So the connection is this: you almost never need, even in C routines, a bald pointer to mutable char*. When you do, it's almost always in order to pass to a function that is dangerous and unrecommended to start with. Safe functions take char* and length, which a char[] offers no problem. From all of the above the net result is that toStringzAlwaysCopy is useless.Steven Schveighoffer wrote:OK, it's only the most used D library. Sorry for the unrelated reference. Phobos 1 had the same issue BTW."Andrei Alexandrescu" wroteThat doesn't mean much of anything.Steven Schveighoffer wrote:Nonsense. Tango currently has such a function with D 1.x, and I've never heard of any issues with it.But you probably won't add it. That's ok, I don't use Phobos anyways. I'll be sure to add an appropriate function to Tango while porting it to D2.You may want to rethink before putting dangerous functions in widely-used libraries. Returning a writable zero-terminated char* is as dangerous as it gets, and fostering bad coding style too.I got bored of reading. Like ore-sama said, your examples have nothing to do with zero-terminated strings.I think you have overblown the danger here.And it's not me. http://marc.info/?l=bugtraq&m=105673479925709&q=p4 http://seclists.org/vulnwatch/2007/q1/0069.html http://www.mail-archive.com/popt-devel rpm5.org/msg00072.html http://sourceware.org/ml/gdb-patches/2005-03/msg00237.html http://mailman.mit.edu/pipermail/krbdev/2008-October/007022.html I got bored of pasting. Just google for strcpy sprintf strcat gets. Again: please rethink.Some of them even outlaw functions that take const char *.Yes, the potential format attack. That is an unrelated issue that has to do with tainted vs. untainted strings. (Some proposed languages and language extensions allow "tainted" as a qualifier... a good potential issue to discuss in the future.)So by your argument that proof is in these pages, if you are going to condemn functions that take mutable zero-terminated strings, you have to do the same for const zero terminated strings.That would be a simplistic view of what I meant to say. Const zero-terminated strings may be dangerous when formatting/execution information that they contain leads to erratic behavior elsewhere, as in e.g. printf, scanf, or system. That is a distinct danger from that of not passing the length of a buffer around and running over its length.And most of them are talking about buffer overflow attacks, which would be unlikely (maybe impossible? I don't know much about it) with heap-allocated strings, which is what alwaysCopyAndReturnMutableStringz returns.Buffer overflow is a concrete and spectacular consequence of memory unsafety, but that doesn't make other consequences less bad.None of this changes my view. And I'm done arguing, take away from this what you will.I wasn't in this for arguing, saving face, or having the last word. I simply inferred from your post you did not know the information about the stir on unsafe functions in C and just wanted to share that with you. So I wasn't discussing a view as much as sharing a fact. That fact hamstrings the motivation for toStringzMutable, hence the relevance to the discussion. I confess I was even hoping for the casual "thanks" for going through the trouble of explaining and picking links etc :o). Andrei
Nov 08 2008