Replacing MIOFILE/MFILE with iostreams

2 views
Skip to first unread message

Nicolas Alvarez

unread,
Feb 26, 2009, 6:13:07 PM2/26/09
to synecdo...@googlegroups.com
MIOFILE and MFILE seem to come from the initial commit of BOINC into CVS, so
I don't know about its exact origins. It provides a single printf-based
interface to write to either a FILE* or to a char buffer (via MFILE). The
reason is claimed to be that on Windows you can't wrap a socket in a FILE*.
Instead of calling fprintf on a FILE* from a socket (which they apparently
can't do), they use a MIOFILE with an MFILE, buffering all the data. Then
it's written to a socket in one go.

But formatted output to a string in C++ should be done with stringstream.

BOINC doesn't use iostreams anywhere, it uses printf and variants instead.
iostreams already provide an unified interface that could work with a file,
a string, or custom i/o.

In addition, MFILE has very error-prone buffer ownership behavior. The
buffer is passed back and forth with MIOFILE; I don't know/remember the
details. There has been actually a memory leak bug in the GUI RPC code for
authentication because of this.

The combination of MIOFILE and MFILE is essentially a stringstream (with
strange buffer ownership passing), and should be replaced with
istringstream or ostringstream as appropriate. Likewise, MIOFILE used along
with a FILE* should be replaced with std::ifstream or std::ofstream as
appropriate.

A challenge is changing the code progressively. Functions using MIOFILE
usually call other functions, passing it the MIOFILE. If function A calls
function B passing a MIOFILE, then if A is changed, B has to be changed
too. But if C also uses B, then C has to be changed too. And if C
calls D...

So we have to either modify all functions at the same time for it to work,
or write some sort of adapter class. Be it a MIOFILE that wraps a stream,
a streambuf that writes from / reads to a MIOFILE, or both.

Another issue: The most common use of MIOFILEs involves XML generation.
MIOFILE uses printf, while streams use the formatting operator.

os << "<tag>" << value << "</tag>"

may be considered less readable than

mf.printf("<tag>%s</tag>", value)

especially when it's a huge chunk of XML with two dozen tags (currently done
in a single printf call!). But I'm not sure about it. Probably the removal
of ugly custom classes that do the same as standard library features is
worth the increase in angle brackets per line :)


Nicolas Alvarez

unread,
Sep 25, 2009, 4:30:19 PM9/25/09
to synecdo...@googlegroups.com
Nicolas Alvarez wrote:
> The combination of MIOFILE and MFILE is essentially a stringstream (with
> strange buffer ownership passing), and should be replaced with
> istringstream or ostringstream as appropriate. Likewise, MIOFILE used
> along with a FILE* should be replaced with std::ifstream or std::ofstream
> as appropriate.

More precisely: a MIOFILE is like an iostream, and an MFILE is like a
stringbuf, which MIOFILE can use. When both are used together, it's like an
ostringstream.

> A challenge is changing the code progressively. Functions using MIOFILE
> usually call other functions, passing it the MIOFILE. If function A calls
> function B passing a MIOFILE, then if A is changed, B has to be changed
> too. But if C also uses B, then C has to be changed too. And if C
> calls D...
>
> So we have to either modify all functions at the same time for it to work,
> or write some sort of adapter class. Be it a MIOFILE that wraps a stream,
> a streambuf that writes from / reads to a MIOFILE, or both.

I wrote and committed an adapter class that lets a writer function using
ostream call another writer function that still uses MIOFILE. It's an
inline class in lib/miofile_wrap.h, called MiofileAdapter. There is doxygen
documentation and unit tests for it.

However, it looks like I will need an adapter in the reverse direction soon;
that is, something to let a function pass a MIOFILE to another function
that expects an ostream. Either that or overload (duplicate!) some
functions to allow both MIOFILE and ostream for a while.

Nicolas Alvarez

unread,
Sep 25, 2009, 4:50:44 PM9/25/09
to synecdo...@googlegroups.com
Nicolas Alvarez wrote:
> I wrote and committed an adapter class that lets a writer function using
> ostream call another writer function that still uses MIOFILE. It's an
> inline class in lib/miofile_wrap.h, called MiofileAdapter. There is
> doxygen documentation and unit tests for it.
>
> However, it looks like I will need an adapter in the reverse direction
> soon; that is, something to let a function pass a MIOFILE to another
> function that expects an ostream. Either that or overload (duplicate!)
> some functions to allow both MIOFILE and ostream for a while.

I'm ignoring reader functions for now, because replacing those with
std::istream would involve modifying the XML parser in almost all cases.


Reply all
Reply to author
Forward
0 new messages