Discussion:
VS: operator <<(...const std::string&) does not call ->rdbuf()->sp
(too old to reply)
Paul
2009-11-17 14:46:08 UTC
Permalink
As I understand from the C++ Standard, operator

std::ostream& operator <<(std::ostream& os, const std::string& s);

should call os.rdbuf()->sputn() but from what I see in the code, it does
not. Is there a way to make it happen? I use a custom buffer derived from
std::streambuf, have overriden xsputn() hoping under certain circumstances to
bypass the buffer altogether and write immediately to the destination but
without sputn() being called, this will obviously not work.

Thank you.

Paul
Victor Bazarov
2009-11-17 15:09:44 UTC
Permalink
Post by Paul
As I understand from the C++ Standard, operator
std::ostream& operator <<(std::ostream& os, const std::string& s);
should call os.rdbuf()->sputn()
I don't see the Standard saying that it "should". I see "inserts
characters as if by calling os.rdbuf()->sputn", nothing about "should"
or "shall" or some other requirement.
Post by Paul
but from what I see in the code, it does
not. Is there a way to make it happen? I use a custom buffer derived from
std::streambuf, have overriden xsputn() hoping under certain circumstances to
bypass the buffer altogether and write immediately to the destination but
without sputn() being called, this will obviously not work.
Do you think you can share the actual code? Just extract enough from
your program to demonstrate the problem you're describing...

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Paul
2009-11-17 16:19:01 UTC
Permalink
Post by Victor Bazarov
Post by Paul
As I understand from the C++ Standard, operator
std::ostream& operator <<(std::ostream& os, const std::string& s);
should call os.rdbuf()->sputn()
I don't see the Standard saying that it "should". I see "inserts
characters as if by calling os.rdbuf()->sputn", nothing about "should"
or "shall" or some other requirement.
But to me this is as good as saying that the effect should be as if
"os.rdbuf()->sputn(str.data(), n)" is called and not ->sputc().
Post by Victor Bazarov
Do you think you can share the actual code? Just extract enough from
your program to demonstrate the problem you're describing...
#include <streambuf>

template<class Ch> class BasicBuf : public std::basic_streambuf<Ch> {
public:
BasicBuf();
protected:
//...
int_type overflow(int_type c = traits_type::eof());
std::streamsize xsputn(const Ch* p, std::streamsize n);
private:
int receive(Ch* buf, int len);
int send(const Ch* buf, int len);

//...

Ch m_buffer[max];
};

template<class Ch> std::streamsize Gi::Basic_SocketBuf<Ch>::xsputn(const Ch*
p, std::streamsize n)
{
std::streamsize tot = 0;

for (std::streamsize len; n > 0; p += len, n -= len, tot += len) {
const std::ptrdiff_t avail = epptr() - pptr();

if (!avail) { // buffer full
if (traits_type::eq_int_type(traits_type::eof(),
overflow(traits_type::to_int_type(*p))))
break;

len = 1;
}
else if (n <= avail || pbase() < pptr()) { // n fits in space available in
buffer or buffer is not empty
len = n < avail ? n : avail;

traits_type::copy(pptr(), p, len);
pbump(static_cast<int>(len));
}
else {
len = send(p, static_cast<int>(n));
}
}

return tot;
}

int main()
{
const std::string str(<something exceeding buffer size>, 'a');

BasicBuf bb;
std::ostream os(&bb);
os << str; // How can I make operator << call sputn()?
}
Paul
2009-11-17 16:30:02 UTC
Permalink
Post by Paul
int main()
{
const std::string str(<something exceeding buffer size>, 'a');
BasicBuf bb;
std::ostream os(&bb);
os << str; // How can I make operator << call sputn()?
}
"something exceeding buffer size" - this is to cause send() inside xputn()
to be called which would happen if operator << for std::string called sputn().
Victor Bazarov
2009-11-17 17:58:47 UTC
Permalink
Post by Paul
Post by Victor Bazarov
Post by Paul
As I understand from the C++ Standard, operator
std::ostream& operator <<(std::ostream& os, const std::string& s);
should call os.rdbuf()->sputn()
I don't see the Standard saying that it "should". I see "inserts
characters as if by calling os.rdbuf()->sputn", nothing about "should"
or "shall" or some other requirement.
But to me this is as good as saying that the effect should be as if
"os.rdbuf()->sputn(str.data(), n)" is called and not ->sputc().
[..]
Maybe there is a defect in the Standard, or a defect in Dinkumware's
implementation (based on their interpretation). What would be the
[observable] difference between calling 'sputn' and 'sputc'
(repeatedly)? I'm guessing you need your virtual function called (as
specified in the behaviour of 'sputn')

If you think it's a defect in the implementation, contact Dinkumware or
Microsoft and see what they have to say about that. If you think it's a
defect in the Standard (and the expected wording needs to be stronger),
post to 'comp.std.c++'.

Is there a different approach available to you? 'sputc' is specified to
check the availability of the room in the buffer using the pointers
(xnext and xend), and call 'overflow' where you're supposed to dump the
characters (send or whatever), and change the pointers (not sure what
the right code is). What book are you reading, BTW? I remember hearing
that Angelika Langer's "Standard C++ Iostreams" was very good. Also,
Josuttis' C++ Standard Library book is highly praised.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Paul
2009-11-18 10:38:01 UTC
Permalink
Post by Victor Bazarov
Maybe there is a defect in the Standard, or a defect in Dinkumware's
implementation (based on their interpretation). What would be the
[observable] difference between calling 'sputn' and 'sputc'
(repeatedly)? I'm guessing you need your virtual function called (as
specified in the behaviour of 'sputn')
sputn() indeed calls xsputn() and the latter can be overridden in a derived
class. The idea was optimisation: rather than calling sputc() to write one
character at a time potentially with checks on each output, you can call
sputn() to write more than one character in one go. I further noticed that if
it makes sense, the buffer in a class derived from std::streambuf can be
completely dispensed with, so rather than writing to the buffer as temporary
storage, you can send characters to the ultimate destination without copying.
Post by Victor Bazarov
If you think it's a defect in the implementation, contact Dinkumware or
Microsoft and see what they have to say about that. If you think it's a
defect in the Standard (and the expected wording needs to be stronger),
post to 'comp.std.c++'.
I think it is a defect in the implementation but, based on past experience,
I seriously doubt Microsoft will do anything about it.
Post by Victor Bazarov
Is there a different approach available to you? 'sputc' is specified to
check the availability of the room in the buffer using the pointers
(xnext and xend), and call 'overflow' where you're supposed to dump the
characters (send or whatever), and change the pointers (not sure what
the right code is). What book are you reading, BTW? I remember hearing
that Angelika Langer's "Standard C++ Iostreams" was very good. Also,
Josuttis' C++ Standard Library book is highly praised.
The workaround seems to be to call sputn() directly, so instead of

extern std::ostream& os;

os << std::string(255, 'a');

I can write

extern std::ostream& os;

const std::string s(255, 'a');
os.rdbuf()->sputn(s.data(), s.size());

I do not like Angelika Langer or Josuttis (I have the former book), if
forgivable (thank you for the recommendation: this is not in any way to
detract from it). I like Steve Teale's book on classic iostreams and
Plauger's Draft C++ Standard, even if outdated.
Bo Persson
2009-11-17 18:17:58 UTC
Permalink
Post by Paul
Post by Victor Bazarov
Post by Paul
As I understand from the C++ Standard, operator
std::ostream& operator <<(std::ostream& os, const std::string& s);
should call os.rdbuf()->sputn()
I don't see the Standard saying that it "should". I see "inserts
characters as if by calling os.rdbuf()->sputn", nothing about
"should" or "shall" or some other requirement.
But to me this is as good as saying that the effect should be as if
"os.rdbuf()->sputn(str.data(), n)" is called and not ->sputc().
On the other hand, sputn() is required to call xsputn(), which in turn
behaves "as-if" it calls sputc(). So, why not call sputc() directly?


Bo Persson
Paul
2009-11-18 10:41:02 UTC
Permalink
Post by Bo Persson
On the other hand, sputn() is required to call xsputn(), which in turn
behaves "as-if" it calls sputc(). So, why not call sputc() directly?
The idea behind sputn() which indeed should call xsputn() was optimisation
to avoid copying one character at a time. This is why developers may provide
an optimised version of xsputn() overriding the default implementation.
Loading...