Re: memstream discussion (was Re: strstream reinstatement)
werasm ha scritto:
With your proposed interface, I would need to copy the data out of the
memstream and into ls.s.
Not necessarily. See code below:
<snip>
Considering this code, memstream can now hold the reference to the
array:
template <int N>
class memstream
{
//...
char (&rarray_)[N+1];
};
This would work without any unecessary copying.
This code is quite different from the one you posted earlier. The
previous seemed to own the buffer, while this is just keeping a
reference. It has now become much more similar to my design. Just a
couple of remarks on yours:
1) the need to introduce a new type (array<N>) seems an unnecessary
complication to me;
2) I still fail to see the advantage of passing the size of the buffer
as a template parameter. In fact I find it a hindrance, making the
memstream useless in this real-life scenario:
// this function is called by some legacy framework,
// it's expected to fill the given buffer with something
void legacy_callback(char* buf, size_t buflen)
{
omemstream formatter(buf, buflen);
/* use formatter to fill the buffer and check overflow */
}
I disagree that nulls should have a special treatment. That would make
the interface more complex and, in the end, the design would be as
error-prone as not providing the feature at all. For what's worth, jus=
t
look how easily I handled the null terminator in the example above.
IMhO its so easy to forget that little <ends> at the end. If I had
control over the size (which I now do have :-) ), I would add the NULL
by default, always. I agree that sometimes one may not want the NULL.
Those cases could be handled seperately (see below):
template <unsigned N, bool AddNull = true>
struct array
{
enum {SzToAdd = AddNull ? 1 : 0};
array( char (&rarray)[N+SzToAdd] )
: rarray_( rarray ){ }
char (&rarray_)[N+SzToAdd];
};
You've got a point, but I'm still not convinced. Anyway, let's answer
this question first: *when* to add the NUL?
1) The first place I can think of is in the destructor of membuf. This
is IMHO very error prone! Consider the plain typical scenario:
void foo()
{
char buf[BUFLEN];
omemstream formatter(buf, BUFLEN);
formatter << /* something here */; // no ends
/* buffer overflow check here */;
legacy_function(buf); // ops! NUL not yet appended!!!!
} // destructor appends NUL here: too late!
2) Alternatively, you could zero-initialize the buffer on the class
constructor, but then we would fail the goal to be as lightweight as
possible, performing an operation that the user might not want to pay.
3) Last option, we could add a NUL after each output operation. This
would ensure that the buffer is always null-terminated without the
expensive operation of initializing the entire buffer. Of course it
should be made optional. ...I just realized that there's a very nice and
quick way to implement this feature! I could just make the virtual
function membuf::sync() append the NUL without advancing the output
pointer. This is consistent with the description of streambuf::sync().
Given that, to have automatic NUL-termination you can just set flag
ios_base::unitbuf, et voil=E0: the destructor of the output sentry object
will call sync() eventually after each output operation. That would also
make flush() append the NUL without advancing the output pointer, which
could be useful if you need the intermediate results in the buffer but
you need to resume writing at a later time. Nifty!
However, whatever solution you choose, you have to be careful with
seeking and/or the possibility to use the same buffer for both output
and input operations at the same time. In these respects, I still find
solution #3 to be superior.
So, you see, #1 is a no-no, #2 and #3 introduce a non-negligible cost
and potential confusion in certain situations. Moreover I disagree with
you that the ends is so easy to forget. That would explain my resistance
to have null-termination as the default behaviour. However, #3 looks
very promising as it would be optional and I am indeed inclined to
consider it as soon as I find a way to properly handle the overflow. I
have to thank you for making me think about it. However, I guess every
change to the proposal should be postponed after the Portland meeting in
mid October.
I noticed (the link no longer worked), yes I would not mind if you did.
The paper is now available on the C++ committee website at
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/#mailing2006-09>.
The doc number is N2065.
Ganesh
PS: Although the ISO 6429 name for the character '\0' is NULL, the name
NULL in C/C++ usually refers to a null pointer and this might create
confusion. That's why I prefer using the ASCII name NUL (with only one
L) or write null in lowercase.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]