Re: Why is RAII called RAII?
"Goran Pusic" <goranp@cse-semaphore.com>
<quote>
(Working with CFile)
1.
CFile f;
if (!f.Open(params))
return cant_open; // Incomplete. (why error? what file?). Bad code.
use(f);
2.
CFile f;
CFileException e;
if (!f.Open(params, e))
return error_info_form(e); // Better, but complicated.
use(f);
3. (correct code)
CFile f(params);
use(f);
</quote>
Certainly all the snippets are correct. The last one is just your
preference.
First two simply do not scale wrt complexity of error reporting.
Come on. :) Exceptions are king when you report far upwards. They kinda
suck if you must try{} -wrap a single line. As a rule of thumb I'd say if
you have more than a few hits on 'try' there are problems with something's
design.
First
off, you need at least variant 2, or else, you have crap error info.
Second, "use" might have failures, too (file on network, second write
fails), and these failures vary wildly. How do you plan to pass
quality info to higher layer? If You need error info that can explain
every source of the error, and that is _hard_.
My experience and practice is quite different.
I use the throwing open when the file is expected to be there and
openable -- it was configured, or discovered by a directory scan, or just
written by another component.
I use the nonthrowing form after FileOpenDialog analog, where the user may
provide whatever crap. Detailed error info is rarely needed, the most basic
message box is okay.
No 3. gives you error info to the best of CFile abilities, including
file name, OS error code, and +/- appropriate text for _any_ error
that might happen.
And No 2 gives the very same info -- and you can even throw it after some
more work, if you like (say you offered the user to pick again with ability
to abandon).
You can assemble your use case in a sensible way without cluttering the
program. The File wrappers i wrote always had both throwing and
nonthrowing variants, and all had their uses. For example ReadBytes that
expect to successfully read the requested amount from a formatted file or
throw -- and the usual Read that may read less and report the amount.
Open is also may or may not be expected to succeed normatively, and I
generally reserve exceptions to actual problems. Keeping most functions free
of try {} blocks and be transparent.
And if, by any chance, block that uses the file
must be a no-throw zone, even if you use Open, you still have to use
try-catch.
Didn't we start discussion you claiming less complexity? Indeed you can --
and it's like dropping a piano on one's head. Even in friendly cases.
Actually throwing ctors are not so easy to work around with try in all cases
due to forced blocks, and if you have multiple objects I better not even
start thinking on the resulting code. ;-)
And even the calling code is better off expecting an
exception (because one is possible anyhow).
That depends on the contract, and what uyou expect to recover from locally.
Reusing a file object: log file object that should be closed when you
are not currently writing a line to it.
Who cares? You need to have file name anyhow. So from there, you go:
CFile(params).write(something);
This is easier than having an unusable CFile that lurks somewhere and
repeatedly calling open close, and whatnot.
Maybe easier for your practice, and not easier for other people's. How
about not playing Procrustes?
There is a place, though, where you could improve (MFC specific): you
often want to pass CString to ctor, not LPCTSTR (to avoid touching
heap and string copy that is otherwise hiding behind).
You what? CFile (and btw most MFC) uses LPCSTR in most interface from 1.0.
(switching to LPCTSTR somewhere past 4.2). That works through implicit
conversion from CString. You certainly do not want to create extra CStrings
if have the filename as literal. And even if CFile may create some
CString within its black box, you have no chance to avoid that.