2

I have implemented a simple ostream and streambuf class. For some reason, it crashes when I try to instantiate my AndroidLogOStream object.

Note: I have stlport_static in my Application.mk

class AndroidLogStreamBuf : public std::streambuf
    {
    public:
        inline AndroidLogStreamBuf() : std::streambuf()
        {
            //std::cout << "asdfg";

        }

        inline ~AndroidLogStreamBuf()
        {

        }



    };

    class AndroidLogOStream : public std::ostream
    {
    public:
        inline AndroidLogOStream() : std::ostream(&mBuf)
        {

        }

        inline ~AndroidLogOStream()
        {

        }

    private:
        AndroidLogStreamBuf mBuf;
    };

It's barebones, and it runs fine on windows. It compiles fine on android, but it crashes for some reason. The last line it tries to execute is in _streambuf.c:46:

template <class _CharT, class _Traits>
locale
basic_streambuf<_CharT, _Traits>::pubimbue(const locale& __loc) {
  this->imbue(__loc);          <---- crash
  locale __tmp = _M_locale;
  _M_locale = __loc;
  return __tmp;
}

Granted I am still quite confused on iostreams, but it must be something wrong with the constructor, I suppose it is not valid?

KaiserJohaan
  • 9,028
  • 20
  • 112
  • 199

2 Answers2

5

In a constructor, the base class is initialized first, followed by all of the members. When you call the base class constructor std::ostream, you're passing it the address of mBuf, which has not yet been constructed. Accessing an object that hasn't yet been constructed has undefined behaviour.

To get around this, you could redesign your classes as follows:

class AndroidLogStreamBuf : public std::streambuf
{
public:
    AndroidLogStreamBuf() : std::streambuf()
    { }

    ~AndroidLogStreamBuf()
    { }
};

class AndroidLogOStream : public std::ostream
{
public:
    AndroidLogOStream(AndroidLogStreamBuf *buf) :
        std::ostream(buf),
        mBuf(buf)
    { }

    ~AndroidLogOStream()
    { }

private:
    AndroidLogStreamBuf *mBuf;
};

class AndroidLogOStreamWithBuf
{
private:
    AndroidLogStreamBuf mBuf;
    AndroidLogOStream mStream;

public:
    AndroidLogOStreamWithBuf() :
        mBuf(&mStream),
        mStream()
    { }

    virtual ~AndroidLogOStreamWithBuf()
    { }

    AndroidLogOStream& getOStream()
    {
        return mStream;
    }
};

Notice the order I've declared mBuf and mStream in AndroidLogOStreamWithBuf: the two fields will be initialized in that order, regardless of the order they appear in the constructor initializer list. As an aside, marking the member functions as inline in your original code was superfluous: when you define a member function within the class definition, it's automatically marked as inlinable.

Whether this is a sensible design for your system depends on how you're intending to use these classes, but the answer is probably "no".

Dan Hulme
  • 14,779
  • 3
  • 46
  • 95
  • Ahhhh! that is a good point! One thing though: why does it run fine on windows? – KaiserJohaan Feb 28 '12 at 13:20
  • One possible result of undefined behaviour is "running fine" unfortunately. – uesp Feb 28 '12 at 13:27
  • how would I go about solving this? I mean I need to pass the buffer as a refererence in the ostream constructor! – KaiserJohaan Feb 28 '12 at 15:47
  • There, I've updated my answer with some more practical advice. I suspect the best solution for your situation will be different, depending on the design of the surrounding code. – Dan Hulme Feb 29 '12 at 10:53
1

As was pointed out, the base class is constructed first and from the looks of it, the base class constructor seems to do something. I don't think it is meant to but the base class destructor would also create a problem and that will call pubsync() on the stream buffer.

Of course, this explains the problem but doesn't provide a solution: the solution to this initialization problem is to make the the stream buffer (or a custom class containing the stream buffer as member) a virtual base class:

class oandroidligstream:
    virtual AndroidLogStream,
    public std::ostringstream {
        ...
    }
};

the reason the base has to be virtual is that the stream buffer is an argument to the virtual base std::ios. To make sure your stream buffer is initialized first it has to be the left-most virtual base.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • if I declare it virtual, it says 'mBuf is undefined' in my AndroidLogOStream () constructor, and I have to use mBuf in the constructor. How does one solve that? – KaiserJohaan Feb 28 '12 at 15:40
  • Well it isn't a member anymore but a base class! You could use `this`. Alternatively you put it into a custom struct and use this a vitrual base. – Dietmar Kühl Feb 28 '12 at 16:56