Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

failbit being set with custom streambuf

312 views
Skip to first unread message

Jey Kottalam

unread,
Jul 4, 2005, 10:23:15 PM7/4/05
to
Hello,

I'm trying to use a custom streambuf with an std::ostream, but failbit
is being set and I can't see why.

Here's a testcase that reproduces the problem with comments at the
location of the error:

#include <iostream>
#include <cassert>

struct B : public std::streambuf
{
virtual std::streamsize xsputn(const char *msg, std::streamsize
count)
{
return count;
}
};

struct O : public std::ostream
{
O() :
std::ostream(new B)
{
}

~O()
{
delete rdbuf(0);
}
};

int main()
{
O o;

assert(o << 1); // fails here with icc 8.0 on i686-linux
assert(o << "2");
assert(o << '3');
assert(o << 4.0);
assert(o << std::endl); // fails here with g++ 3.3.2 on i686-linux
}

-Jey Kottalam


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Dietmar Kuehl

unread,
Jul 5, 2005, 6:50:46 AM7/5/05
to
Jey Kottalam wrote:
> struct B : public std::streambuf
> {
> virtual std::streamsize xsputn(const char *msg, std::streamsize
> count)
> {
> return count;
> }
> };

You need to override 'overflow()' and probably also 'sync()' to
make output with a stream buffer work. 'xsputn()' is actually
only used as an optimization (and the standard C++ library
implementation was actually not allowed to call 'xsputn()' directly
or indirectly prior to a fix of the standard). The default
implementation of 'overflow()' return "eof()" and is thus
indicating an error.
--
<mailto:dietma...@yahoo.com> <http://www.dietmar-kuehl.de/>
<http://www.eai-systems.com> - Efficient Artificial Intelligence

ka...@gabi-soft.fr

unread,
Jul 5, 2005, 7:03:49 AM7/5/05
to
Jey Kottalam wrote:

> I'm trying to use a custom streambuf with an std::ostream, but
> failbit is being set and I can't see why.

> Here's a testcase that reproduces the problem with comments at
> the location of the error:

> #include <iostream>
> #include <cassert>

> struct B : public std::streambuf
> {
> virtual std::streamsize xsputn(const char *msg, std::streamsize
> count)
> {
> return count;
> }
> };

> struct O : public std::ostream
> {
> O() :
> std::ostream(new B)
> {
> }

> ~O()
> {
> delete rdbuf(0);
> }
> };

> int main()
> {
> O o;
>
> assert(o << 1); // fails here with icc 8.0 on i686-linux
> assert(o << "2");
> assert(o << '3');
> assert(o << 4.0);
> assert(o << std::endl); // fails here with g++ 3.3.2 on i686-linux
> }


This is simple: you haven't overridden streambuf::overflow. For
an output streambuf, you must override overflow. Overriding
xsputn is optional; doing so may be useful for optimization in
some cases, but it isn't generally necessary. The default
implementation of xsputn calls sputc, which calls overflow if
the buffer is full. (It is up to the derived class to establish
a buffer. Assuming it wants one.) The default implementation
of overflow is to fail.

Where the code will actually fail in this case depends on the
implementation. Apparently, g++ is using a local buffer for
integer conversions, and then calling sputn (which in turn calls
xsputn) to output it; the Intel implementation is apparently
using sputc to output each character individually. Other
implementations might fail at different places. (To be frank,
I'm somewhat surprised that any implementation handling o <<
'3'. Using sputn to output a single character seems somewhat
perverse. But there's doubtlessly some interal logic behind
it.)

If you provide buffering, you will probably also have to provide
is sync. The default implementation succeeds (so won't cause
failbit to be set), but it doesn't do anything.

--
James Kanze GABI Software
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

Maxim Yegorushkin

unread,
Jul 5, 2005, 9:11:12 AM7/5/05
to
On Tue, 05 Jul 2005 06:23:15 +0400, Jey Kottalam <use...@jey.kottalam.net>
wrote:

> I'm trying to use a custom streambuf with an std::ostream, but failbit
> is being set and I can't see why.

You have to override sync() and overflow() as well. Like this:

class mem_ostream : private std::streambuf, public std::ostream
{
public:
mem_ostream(char* buf, size_t size)
: std::ostream(this), buf_(buf), p_(buf), size_(size)
{
// throw std::ios_base::failure on buffer overflow
this->exceptions(ios_base::badbit);
}

private:
typedef std::streambuf::traits_type traits_type;
typedef std::streambuf::char_type char_type;
typedef std::streambuf::int_type int_type;

public:
char* begin() const { return buf_; }
char* end() const { return p_; }
size_t size() const { return p_ - buf_; }
bool empty() const { return p_ == buf_; }

private: // overloads for std::streambuf output interface
std::streamsize xsputn(char_type const* data, std::streamsize size)
{
size_t const n = std::min<size_t>(size_, size);
traits_type::copy(p_, data, n);
p_ += n;
size_ -= n;
return n;
}

int_type overflow(int_type c)
{
if(!traits_type::eq_int_type(c, traits_type::eof()))
{
char_type const t = traits_type::to_char_type(c);
this->xsputn(&t, 1);
}
return !traits_type::eof();
}

int sync() { return 0; }

private:
char* const buf_;
char* p_;
size_t size_;
};

template<size_t buffer_size>
class mem_ostream_n : public mem_ostream
{
private:
char buf_[buffer_size];
public:
mem_ostream_n() : mem_ostream(buf_, buffer_size) {}
};

--
Maxim Yegorushkin
<firstname...@gmail.com>

0 new messages