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?
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.
ReplyDeleteTo 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".
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.
ReplyDeleteOf 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.