 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Z.L. Guest
|
Posted: Sun Dec 17, 2006 12:00 am Post subject: delimiter for istringstream |
|
|
I have following input string:
char* s = "1.0:2.0:3.0 4.0 5.0".
I want the ":" serve as whitespace delimiter just like blank space,
therefore when excuting
"
istringstream iss(s);
double digit[5];
for (int i=0; i<5; ++i) iss >> digit[i];
",
I can put the double type data into the array.
I remember it seems a function for istream can do this job, but I am
not so sure about that.
What exactly I mean above is whether I can specify the whitespace
delimiter for istringstream. If yes, which function does that job?
Could somebody give me some suggestion on that?
Thanks a lot.
Z.L.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
devabc Guest
|
Posted: Sun Dec 17, 2006 6:22 pm Post subject: Re: delimiter for istringstream |
|
|
[The following submission to comp.lang.c++.moderated has
been rejected because it overquotes the original. Please
resubmit, trimming the unnecessary orignal (including the
moderation banner), and placing your comments after the text
they are commenting. --mod]
perhaps the function is:
istream& istream::get (char* str, streamsize count, char delim)
"Z.L. $B<LF;!'(B
"
| Quote: | I have following input string:
char* s = "1.0:2.0:3.0 4.0 5.0".
I want the ":" serve as whitespace delimiter just like blank space,
therefore when excuting
"
istringstream iss(s);
double digit[5];
for (int i=0; i<5; ++i) iss >> digit[i];
",
I can put the double type data into the array.
I remember it seems a function for istream can do this job, but I am
not so sure about that.
What exactly I mean above is whether I can specify the whitespace
delimiter for istringstream. If yes, which function does that job?
Could somebody give me some suggestion on that?
Thanks a lot.
Z.L.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
|
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
James Kanze Guest
|
Posted: Mon Dec 18, 2006 12:33 am Post subject: Re: delimiter for istringstream |
|
|
Z.L. wrote:
| Quote: | I have following input string:
char* s = "1.0:2.0:3.0 4.0 5.0".
I want the ":" serve as whitespace delimiter just like blank space,
therefore when excuting
istringstream iss(s);
double digit[5];
for (int i=0; i<5; ++i) iss >> digit[i];
",
I can put the double type data into the array.
I remember it seems a function for istream can do this job, but I am
not so sure about that.
What exactly I mean above is whether I can specify the whitespace
delimiter for istringstream. If yes, which function does that job?
|
It's possible to create a ctype facet which would do this.
Creating and installing a facet is far from trivial, however,
and I would consider using facets for this sort of thing
obfuscation as well---while one expects whitespace to depend on
the locale (e.g. 0xA0 is whitespace in an ISO 8859-1 locale, but
not in an ASCII locale), one expects it to be whitespace, and
not a ':', or other punctuation.
The simplest solution is just to define a manipulator, along the
lines of std::ws, which handles this correctly, and write:
for ( i = 0 ; i < 5 ; ++ i ) {
if ( i != 0 ) {
iss >> separator ;
}
iss >> digit[ i ] ;
}
The manipulator might look something like:
std::ostream&
separator( std::ostream& s )
{
std::ws( s ) ;
if ( s && s.peek() == ':' ) {
s.get() ;
std::ws( s ) ;
}
return s ;
}
--
James Kanze (Gabi Software) email: james.kanze (AT) gmail (DOT) com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
devabc Guest
|
Posted: Mon Dec 18, 2006 8:53 am Post subject: Re: delimiter for istringstream |
|
|
{ Top-posting corrected; please don't top-post in this group. -mod }
"James Kanze дµÀ£º
"> The manipulator might look something like:
| Quote: |
std::ostream&
separator( std::ostream& s )
{
std::ws( s ) ;
if ( s && s.peek() == ':' ) {
s.get() ;
std::ws( s ) ;
}
return s ;
|
why is using ws()?
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
James Kanze Guest
|
Posted: Wed Dec 20, 2006 12:06 am Post subject: Re: delimiter for istringstream |
|
|
Z.L. wrote:
| Quote: | Except customized manipulator, is there other simple method in C++ for
this task?
|
I don't quite get the question: except for the idiomatic simple
method, is there any other simple method? Why don't you want to
use the idiomatic method?
| Quote: | Because I can use strtok() of C to separate the string into substrings
according specified delimiters, I wonder whether more convenient or
simpler method in C++ can handle this task.
|
Well, you can and you can't. First, of course, there's no way
you can use strtok() on an input stream. Nor will it convert
the doubles for you. And of course, you can't really use it if
you expect the resulting code to be in any way maintainable or
reliable.
One alternative solution to a custom manipulator (are there any
other kinds that you really use?) is to read line by line, using
getline, and use boost::regex to split the line up into fields,
and then istringstream to convert each field into a double.
It's a bit wordier than the custom manipulator, largely because
you need to pass through the istringstream again, but in many
contexts, it is preferrable, for two reasons:
-- it provides a convenient resynchronization point in case of
an error (and in fact, provides better error detection), and
-- it allows a lot more freedom with regards to how you specify
fields and their separators.
An alternative to boost::regex for splitting the line into
fields would be my FieldArray classes (in the Text components at
http://kanze.james.neuf.fr/code-en.html); they're probably less
flexible that boost::regex in general, but very work well when
you have an unknown number of fields, with the same separator
between each field. In your case, a
RegularExpressionSeparatedFields would fit the bill nicely, with
"[[:space:]]+|[[:space:]]*:[[:space:]]*" as the regular
expression. (Regretfully, using this class implies installing
and using all of my library, as it depends on a number of other
things in the library.)
--
James Kanze (GABI Software) email:james.kanze (AT) gmail (DOT) com
Conseils en informatique orientie objet/
Beratung in objektorientierter Datenverarbeitung
9 place Simard, 78210 St.-Cyr-l'Icole, France, +33 (0)1 30 23 00 34
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
Denise Kleingeist Guest
|
Posted: Wed Dec 20, 2006 3:40 am Post subject: Re: delimiter for istringstream |
|
|
Hello Z.L.!
Z.L. wrote:
| Quote: | I have following input string:
char* s = "1.0:2.0:3.0 4.0 5.0".
I want the ":" serve as whitespace delimiter just like blank space,
|
You can use a custom locale with a modified ctype facet which uses
colon as whitespace. Dietmar Kuehl showed this in the past although
at this time the delimiter was comma. Have a look at
<http://groups.google.de/group/comp.lang.c++/msg/4de0be2e4eb8e0ba?output=gplain>
Good luck! Denise
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
James Kanze Guest
|
Posted: Wed Dec 20, 2006 7:44 pm Post subject: Re: delimiter for istringstream |
|
|
Denise Kleingeist wrote:
| Quote: | Hello Z.L.!
Z.L. wrote:
I have following input string:
char* s = "1.0:2.0:3.0 4.0 5.0".
I want the ":" serve as whitespace delimiter just like blank space,
You can use a custom locale with a modified ctype facet which uses
colon as whitespace. Dietmar Kuehl showed this in the past although
at this time the delimiter was comma. Have a look at
http://groups.google.de/group/comp.lang.c++/msg/4de0be2e4eb8e0ba?output=gpl |
ain>
On the other hand, Dietmar himself admitted that this is pushing
very close to abuse (at least IIRC). How close is, of course, a
question of context---imbuing std::cin with such a locale, and
then leaving it, is very, very unfriendly, whereas I'd be
somewhat tolerant of using it on an istringstream whose lifetime
was only a couple of lines anyway.
You chould also install a filtering streambuf which converted
':' characters to ' '. Again, I'd be very leary with regards to
readability. My own feeling here (but Z.L. hasn't really made
it clear) is that we're talking about how some specific lines
are formatted. And IMHO, that's the job of manipulators, and
not locales or filtering streambuf's, which (both) are designed
to handle things at a larger scale: the whole file, or at least
significant blocks of it.
On the other hand, both the solution using locale and the
filtering streambuf permit using an istream_iterator<double> to
read the file. In the case of a filtering streambuf, it's also
possible to define an end of the block of doubles, so that the
filtering streambuf detects it, and returns EOF, and the
istream_iterator stops. At which point, you remove the
filtering streambuf, and continue normally.
I still think, too, that we really don't have enough
information to decide. It's not clear to me, for example,
whether "::" should be treated as a single delimiter, or as two
delimiters, with an empty field between them. As formulated, he
literally says that the delimiter can be either a single ':', or
one or more whitespace, but this is based on some very subtle
interpretation of English, and I rather suspect that English is
not his native language, and that he may not master such
subtleties. Before choosing any particular solution, I would
want a detailed specification of the format, including what is
and what is not acceptable in the input.
--
James Kanze (GABI Software) email:james.kanze (AT) gmail (DOT) com
Conseils en informatique orientie objet/
Beratung in objektorientierter Datenverarbeitung
9 place Simard, 78210 St.-Cyr-l'Icole, France, +33 (0)1 30 23 00 34
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
Denise Kleingeist Guest
|
Posted: Fri Dec 22, 2006 3:13 am Post subject: Re: delimiter for istringstream |
|
|
Hi Z.L.!
Z.L. wrote:
| Quote: | P.S. I am very curious about "filtering streambuf" scheme you
mentioned. Could you explain it for me? Thank you very much.
|
I'm obviously not James but I still can describe filtering stream
buffers:
basically, the idea is to have a stream buffer which just delegates the
actual I/O operations to an underlying stream buffer after doing some
processing on its own. For example, a filtering stream buffer could
read a buffer full of data from an underlying stream buffer,
std::transform() all colons into spaces and supply these in its own
buffer. All this processing would be done in the filtering stream
buffer's
underflow() method. This would look something like this:
class filterbuf: public std::streambuf {
public:
filterbuf(std::streambuf* stream): myStream(stream) {}
private:
int_type underflow() {
if (this->gptr() == this->egptr()) {
std::streamsize size = this->myStream->sgetn(this->myBuffer,
1024);
this->setg(this->myBuffer, this->myBuffer,
std::transform(this->myBuffer + 0,
this->myBuffer + size, ':', ' '));
}
return this->gptr() != this->egptr()?
traits_type::to_int_type(this->gptr(): traits_type::eof();
}
std::streambuf* myStream;
char myBuffer[1024];
};
Good luck, Denise!
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
James Kanze Guest
|
Posted: Fri Dec 22, 2006 8:34 pm Post subject: Re: delimiter for istringstream |
|
|
Z.L. wrote:
| Quote: | P.S. I am very curious about "filtering streambuf" scheme you
mentioned. Could you explain it for me? Thank you very much.
|
It's a technique that Dietmar Kuehl and I invented, many years
back. There are two articles about it at my web site:
http://kanze.james.neuf.fr/articles-en.html. There's also an
implementation in the library there. It's probably worth
looking at, since the code is very, very close to that in the
article. But for all practical use, I think I'd go with
boost::iostream, which offers very good support not only for
filtering streambuf's, but also for a number of other things.
--
James Kanze (Gabi Software) email: james.kanze (AT) gmail (DOT) com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
James Kanze Guest
|
Posted: Fri Dec 22, 2006 8:38 pm Post subject: Re: delimiter for istringstream |
|
|
Denise Kleingeist wrote:
| Quote: | Z.L. wrote:
P.S. I am very curious about "filtering streambuf" scheme you
mentioned. Could you explain it for me? Thank you very much.
I'm obviously not James but I still can describe filtering stream
buffers:
basically, the idea is to have a stream buffer which just delegates the
actual I/O operations to an underlying stream buffer after doing some
processing on its own. For example, a filtering stream buffer could
read a buffer full of data from an underlying stream buffer,
std::transform() all colons into spaces and supply these in its own
buffer. All this processing would be done in the filtering stream
buffer's
underflow() method. This would look something like this:
class filterbuf: public std::streambuf {
public:
filterbuf(std::streambuf* stream): myStream(stream) {}
private:
int_type underflow() {
if (this->gptr() == this->egptr()) {
std::streamsize size = this->myStream->sgetn(this->myBuffer,
1024);
this->setg(this->myBuffer, this->myBuffer,
std::transform(this->myBuffer + 0,
this->myBuffer + size, ':', ' '));
|
Now that's clever, hiding the transformation in an argument to
setg, so that the reader won't see it unless he carefully
analyses each step in detail. I'll have to remember it next
time I'm trying to obfuscate code.
| Quote: | }
return this->gptr() != this->egptr()?
traits_type::to_int_type(this->gptr(): traits_type::eof();
}
std::streambuf* myStream;
char myBuffer[1024];
};
|
In general, unless there is a definite reason for doing
otherwise, I tend to avoid bufferisation in a filtering
streambuf. It makes synchronization between the filtering
streambuf and the filtered streambuf more complicated. (This is
typically not a problem if you are reading the entire stream
through the filtering streambuf, but I often insert then just
for a part of the stream.) And of course, buffering is also
more complicated if the transformation is not one to one, which
is often the case (although not the case here).
--
James Kanze (GABI Software) email:james.kanze (AT) gmail (DOT) com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
Denise Kleingeist Guest
|
Posted: Sat Dec 23, 2006 4:43 am Post subject: Re: delimiter for istringstream |
|
|
Hello James!
James Kanze wrote:
| Quote: | Denise Kleingeist wrote:
int_type underflow() {
if (this->gptr() == this->egptr()) {
std::streamsize size = this->myStream->sgetn(this->myBuffer,
1024);
this->setg(this->myBuffer, this->myBuffer,
std::transform(this->myBuffer + 0,
this->myBuffer + size, ':', ' '));
Now that's clever,
|
Yes, I think so, too :-)
| Quote: | hiding the transformation in an argument to
setg, so that the reader won't see it unless he carefully
analyses each step in detail.
|
Well, the transformation is on a line for its own! Yes, it is still the
argument
of a function but then, this is typical use in functional programming
and with
template meta programming I'm quite used to this style of programming.
There is another things in template programming which makes function
chaining a good idiom: often, the return type from a function is not at
all
easy to spell and sometimes it is actually outright impossible!
Delegating
immediately to another function is the best you can do in these cases.
Also, I got used to fact that everything in a program which isn't
highlighted
in the color of a comment is important. ... and the few lines are
pretty easy
to oversee, anyway. In fact, the body of the if-statement should
actually
be written as
this->setg(this->myBuffer, this->myBuffer,
std::transform(this->myBuffer + 0,
this->myBuffer +
this->sgetn(this->myBuffer, 1024),
':', ' '));
avoiding the need to capture the return value from sgetn() in some more
or less obscure type and also making the excess curly braces
unnecessary. Code obfuscation? Well, what is readable to some may
be unreadable to others. Not having to spell out types tends to make
implementations more stable against changes and generalization. Sure,
neither is the case or the need here (although the filter can
reasonably
easy extended to cope with arbitrary character types) but general
practice in template programming.
| Quote: | In general, unless there is a definite reason for doing
otherwise, I tend to avoid bufferisation in a filtering
streambuf.
|
Wow! Your background must be rather different than mine! I typically
need to get at least decent performance out of stream buffers and
unbuffered stream buffers are even slow with segmentation unaware
library implementations but really unacceptable compared to algorithms
taking segmentation into account. Chopping even inner loops into tiny
basic blocks by having a virtual call for each character makes
unbuffered
stream buffer rather expensive. However, I'm admittedly sticking with
one
filter once it is in place and normally don't use stream buffers at
different
levels: in this case, buffering indeed becomes a problem. However,
since
I generally need the performance I tend to make my stream buffers
buffered (seems to be an oxymoron anyway to have unbuffered [stream]
buffers). In fact:
| Quote: | And of course, buffering is also
more complicated if the transformation is not one to one, which
is often the case (although not the case here).
|
Dealing with n-to-m transformations with n != 1 || m != 1 for
unbuffered
stream buffers is definitely a pain and having a buffer around
definitely
makes handling these transformations easier! Again, needing the
current position for whatever reason (e.g. because the underlying
stream
buffer is used independently or because seeking is used) makes the
buffering somewhat harder to use. In fact, in many n-to-m
transformations
seeking or independent use of the underlying stream buffer may be
impossible anyway...
Good luck, Denise!
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
Guest
|
Posted: Sun Dec 24, 2006 1:17 am Post subject: Re: delimiter for istringstream |
|
|
Z.L. wrote:
| Quote: | In my problem, I wish the delimiter can be SPECIFIED for istringstream.
So the number of ':' appearing in string is not important, because
istringstream will regard ':' or something specified as whitespace.
|
Given that the construction std::istringstream(s) will make a copy of
the buffer s, we assume that a solution that will also copy the buffer
-- the ENTIRE buffer -- is fine. We don't need to read from files in
this application.
So lets copy the buffer, replace all ':' with a space, and make a
pretty wrapper, and were done!!
class copyreplacebuf
: public std::streambuf
{
private:
struct cmp{ //syntax helper for comparator
char const DELIM=':';
bool operator()(char _arg)const{
return std::streambuf::traits_type::eq(_arg,DELIM);
}
};
std::string _buf; //place to hold the copy
public:
//two pointers saves an O(n) call to strlen
copyreplacebuf(char const*_beg, char const*_end)
:_buf(_beg,_end)
//make copy just like istringstream would have done
{
// replace offending occurences
std::replace_if(_buf.begin(),_buf.end(),cmp(),' ');
// _buf is private and never modified after construction
// and relying on the fact that std::string is always contiguous
// lets get some internals
char* Buf_beg=const_cast<char*>(_buf.data());
char* Buf_end=Buf_beg+_buf.size();
//make our string guts the get area
setg(Buf_beg,Buf_beg,Buf_end);
}
};
int main(int ,char**)
{
char s[] = "1.0:2.0:3.0 4.0 5.0";
copyreplacebuf filbuf(s,s+sizeof(s)-1);
std::istream iss(&filbuf);
double digit[5];
for (int i=0; i<5; ++i)
iss >> digit[i];
return 0;
}
I will leave is as an exercize to the reader on how to make DELIM a run
time argument. If the input sequence itself is modifyable then you can
just modify it in place with the replace_if statement, and plug it
directly into setg.
Regaring the very interesting filtering streambuf approach, a question
for the posters: Why did you favor this approach over codecvt? If the
problem is extended to cover reading files in, then cin or ifstream
would be used, and the codecvt facet would be invoked.
It seems -- with the proviso that you have a working codecvt
implementation that you can play with -- that writing a codecvt that
did a replace of every ':' with ' ' would be very straightforward.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
Carl Barron Guest
|
Posted: Sun Dec 24, 2006 5:06 pm Post subject: Re: delimiter for istringstream |
|
|
<lancediduck (AT) nyc (DOT) rr.com> wrote:
| Quote: | So lets copy the buffer, replace all ':' with a space, and make a
pretty wrapper, and were done!!
class copyreplacebuf
: public std::streambuf
{
private:
struct cmp{ //syntax helper for comparator
char const DELIM=':';
bool operator()(char _arg)const{
return std::streambuf::traits_type::eq(_arg,DELIM);
}
};
std::string _buf; //place to hold the copy
public:
//two pointers saves an O(n) call to strlen
copyreplacebuf(char const*_beg, char const*_end)
:_buf(_beg,_end)
//make copy just like istringstream would have done
{
// replace offending occurences
std::replace_if(_buf.begin(),_buf.end(),cmp(),' ');
// _buf is private and never modified after construction
// and relying on the fact that std::string is always contiguous
// lets get some internals
char* Buf_beg=const_cast<char*>(_buf.data());
char* Buf_end=Buf_beg+_buf.size();
//make our string guts the get area
setg(Buf_beg,Buf_beg,Buf_end);
}
};
|
simpler yet:
struct replace_copy_buf:std::streambuf
{
replace_copy_buf(char *begin,char *end,char delim = ':')
{
std::replace(begin,end,delim,' ');
setg(begin,begin,end);
}
};
since you are going to modify the data there is no need for it to be
constant just use char arrays, vector<char> ,boost/tr1 array<char> etc.
not const char *p = "some literal";
for a filtering streambuf on a 'real device' I'd do it unbuffered
since it is easy and only translates what needs to be read, and is
1-1 with the orighinal file so I can change the streambuf back, or
forward seeks to the underlying streambuf etc.
a simple filter might be
class filetered_buf:public std:::streambuf
{
std::streambuf *sb;
int uflow()
{
int c = sb->sbumpc();
return c== ':' ? ' ':c;
}
int underflow()
{
int c = sb->sgetc();
return c == ':'? ' ':c;
}
/* if needed pbackfail, xsgetn, seekpos,seekoff can eassily be
written forwarding the operations to sb. */
public:
filtered_buf(std::streambuf *a):sb(a){}
};
Exercise to reader to allow any char to replace ':' above.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
James Kanze Guest
|
Posted: Sun Dec 31, 2006 10:10 am Post subject: Re: delimiter for istringstream |
|
|
Denise Kleingeist wrote:
| Quote: | James Kanze wrote:
Denise Kleingeist wrote:
int_type underflow() {
if (this->gptr() == this->egptr()) {
std::streamsize size = this->myStream->sgetn(this->myBuffer,
1024);
this->setg(this->myBuffer, this->myBuffer,
std::transform(this->myBuffer + 0,
this->myBuffer + size, ':', ' '));
Now that's clever,
Yes, I think so, too :-)
hiding the transformation in an argument to
setg, so that the reader won't see it unless he carefully
analyses each step in detail.
Well, the transformation is on a line for its own! Yes, it is still the
argument
of a function but then, this is typical use in functional programming
|
I know, and if the context was one of functional programming, I
wouldn't have said anything. I actually often write C++
functions using a functional style, with only a single return
statement in the function. Had this been the case here, I might
even use the return value of std::transform myself. (Weighing
against this is the fact that many programmers don't realize
that it has a return value. Like me, for example, until I saw
your code.)
| Quote: | and with
template meta programming I'm quite used to this style of programming.
|
Certainly. In template meta programming, it's all that is
available. And even in non template meta programming, it has a
lot to recommend it. But the example here doesn't use a
functional programming style---both transform and setg are
called not for their values, but for their side effects. And
generally, a single statement should have a single effect:
change a single variable, etc.
Note too that function chaining is definitly NOT in the STL
style; the STL goes out of its way to make it difficult and
unnatural.
| Quote: | There is another things in template programming which makes function
chaining a good idiom: often, the return type from a function is not at
all
easy to spell and sometimes it is actually outright impossible!
|
Agreed, and in such cases, you have to weigh the obfuscation of
multiple side effects against the added complexity of specifying
a complicated and non-intuitive return type. Depending on the
case, the balance may shift one way or the other. But it's
never a case of saying: this is good. It's simply choosing the
lesser of two evils.
In this case, the return type is char*, so the argument really
doesn't hold.
[...]
| Quote: | Not having to spell out types tends to make
implementations more stable against changes and generalization. Sure,
neither is the case or the need here (although the filter can
reasonably
easy extended to cope with arbitrary character types) but general
practice in template programming.
|
It's a necessary evil in some forms of template programming.
Such template programs are not noted for the readability,
however, at least not by anyone I've talked to. Most of the
time, admiration is reserved for the fact that it could be done
at all, and not for the clarity of the way it was done.
Don't make a virtue out of a necessary evil. Also: C++ is a
multiparadigm language. Don't apply standard idioms from one
paradigm in a different paradigm; it will only create confusion.
| Quote: | In general, unless there is a definite reason for doing
otherwise, I tend to avoid bufferisation in a filtering
streambuf.
Wow! Your background must be rather different than mine! I typically
need to get at least decent performance out of stream buffers and
unbuffered stream buffers are even slow with segmentation unaware
library implementations but really unacceptable compared to algorithms
taking segmentation into account.
|
I've never found the lack of bufferization in a *filtering*
streambuf to make a measurable difference. You definitly want
buffering at the lowest level, before going to the system, but
an extra virtual function call or two per character typically
isn't enough above the level of noise to be measurable.
| Quote: | Chopping even inner loops into tiny
basic blocks by having a virtual call for each character makes
unbuffered
stream buffer rather expensive. However, I'm admittedly sticking with
one
filter once it is in place and normally don't use stream buffers at
different
levels: in this case, buffering indeed becomes a problem. However,
since
I generally need the performance I tend to make my stream buffers
buffered (seems to be an oxymoron anyway to have unbuffered [stream]
buffers).
|
The name is suggestive:-). But then, what name in streambuf's
is well choosen---sgetc leaves the character in the sequence,
for example. And using separate buffering a stringbuf will slow
things down.
If I ever ran into a performance problem, I wouldn't hesitate to
buffer. But it seems very much like pre-mature optimization.
And it does restrict flexibility. One typical trick: a comment
stripping filtering streambuf which doesn't know about quoted
strings. When you encounter the quote character, you go behind
the filtering streambuf, and read from the original source until
the end of the quote. Other times, I'll just filter subsets of
a stream (or add in an additional filter for a subset). In the
initial case here, what is to prevent the block of data from
being just part of a larger stream---in such a case, the obvious
solution is a filtering streambuf which also knows how to detect
the end of the block, and returns EOF when it does. He can then
use an istream_iterator to read just the relevent part of the
file, and resume reading the rest after the istream_iterator has
seen EOF.
(Obvoiusly, both techiques fall into the category of "clever",
and would definitely require heavy commenting:-). But depending
on the application, I've found both very useful at times.)
| Quote: | In fact:
And of course, buffering is also
more complicated if the transformation is not one to one, which
is often the case (although not the case here).
Dealing with n-to-m transformations with n != 1 || m != 1 for
unbuffered
stream buffers is definitely a pain and having a buffer around
definitely
makes handling these transformations easier!
|
I'm not sure I see how. If you'll look at my web site
(http://kanze.james.neuf.fr/code-en.html---you'll have to browse
into the code, in Util/IO/FilteringInputStream/examples/gb and
Util/IO/FilteringOutputStream/examples/gb), there are a number
of filters with n-to-m mappings, all of which are very simple,
and none of which use a buffer. More recently, I have had to
implement a UTF-8/ISO 8859-n mapping in a streambuf. In this
case, performance considerations do suggest buffering, and while
the buffer won't be installed until the first couple of lines
have been read, once installed, it won't be removed. And I
found supporting the buffering added complexity. (Lucky I don't
need to support seeking!)
| Quote: | Again, needing the
current position for whatever reason (e.g. because the underlying
stream
buffer is used independently or because seeking is used) makes the
buffering somewhat harder to use. In fact, in many n-to-m
transformations
seeking or independent use of the underlying stream buffer may be
impossible anyway...
|
Quite. My filtering streambuf's don't generally support
seeking. Of course, my filtering streambuf's are generally
designed for use with text streams, and seeking in a text stream
isn't that easy to begin with. (Isn't "seeking" in a "stream" a
bit of an oxymoron, anyway:-)?)
--
James Kanze (Gabi Software) email: james.kanze (AT) gmail (DOT) com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|