Re: Exception Specification Compromise
On Feb 4, 9:41 am, DeMarcus <use_my_alias_h...@hotmail.com> wrote:
1. That's IMO really hard to do in practice. In calls to existing code
without source, compiler has no idea whether it will throw or not,
unless it's compiled with same flags. Then, innocuous calls like
operator new can be overriden to throw or not, at runtime. that would
be impossible to detect.
Yes, you are completely right. However, the idea is that the compiler
flag should only give a warning, and only when set. Therefore, when the
compiler gives me a warning about someFunction() throw() that uses
another function without throw(), I can choose to embrace it with
try/catch(...). Now I now that at least someFunction() won't throw and
crash on me when the exception reaches the caller that "must never throw".
This way we can make the our own code provide complete no-throw
guarantees, but not without help from the compiler's warnings.
Yes, that seems possible. Note, however that something innocuous like
std::string s("well now") might throw (bad_alloc is my favorite when
talking exceptions ;-)). Or, many calls to STL might throw. And
standard library does not use exception specification, nor do many
others. So one innocuous library call somewhere and your own spec
becomes invalid (think maintenance). (I guess there are reasons why
standard and other libraries don't use throw() and that this is
related to the spec of throw() and history.)
2. often, a no-throw guarantee is not at the function boundary, but
rather just a couple of lines in an existing one. So that makes your
idea not-far-reaching-enough (e.g. something like "nothrow {}" would
be even more useful. For example, recently someone asked here "what if
creation of scope guard throws" and the answer to that is "your job to
make sure it does not"; no-one in their right mind would put scope
guard creation in a function.
I think that particular someone was me. ;) I try understand all this
about exception safety, so please excuse my lack of experience.
Anyway, you write "your job is to make sure it does not" (throw). It is
my job, yes, but if the compiler could do the job for me, or at least
warn about a potential meltdown, my job would so much less stressful.
Let's take a good example. Nowhere can I find proper documentation
whether std::vector's pop_back() throws or not.
True, you don't see it. Practical answer: it's a nothrow operation.
Practical thinking that relates to this, and applies to library
writer: poping is cleanup and cleanup must not throw. I know of no
implementation where that could throw, and would consider it
implementation quality issue if it could. For example, do you know
that pop_back in a vector never changes allocated storage? Downside of
that is that once you reach some high-mark of elements, you don't
never free. But the alternative is worse: if pop-back tried to
reallocate, it would have a failure mode; we don't want that.
On the contrary, if we
look at the synopsis for boost::shared_ptr
(http://www.boost.org/doc/libs/1_42_0/libs/smart_ptr/shared_ptr.htm)
we see that most functions are documented with "// never throws"
How do we know that? Just because they write it? Or do they actually do
a try/catch(...) in the code, and if so, why not declare those functions
with throw()?
Just because they write it. Doing a try/catch would be ludicrous from
performance/code size point, and could also hide a bug in boost,
however unlikely that might be.
BTW, don't you trust e.g. standard lib/booost implementors to do the
right thing? 'cause they do, much more often than I. Their code is
better than mine if nothing else because there's so many eyes on their
code, and so little on mine ;-).
When you write "your job is to make sure it does not" (throw), I claim
that with the lack of documentation and compiler help, this is not an
easy task!
That's strange. I claim that it is. Why do you think it is not? In
fact, most of your trouble stems from this uneasy lack-of-safety-
feeling of yours, doesn't it? I honestly don't have that feeling, and
here's why: I find that nothrow pieces are __extremely__ rare in any
code. And that's __EXTREMELY__. Because they are rare, not a lot of
effort goes into making sure they stay nothrow, and one indeed needs
to make a serious error to break that. Finally, due to their very
nature (low level ops, cleanup), I find them easy to spot, too.
You also write "no-one in their right mind would put scope guard
creation in a function". I don't think I understand what you meant. I
would appreciate if you explained that.
I was thinking: would you use scope guard if you had to do this all
the time:
ScopeGuard MakeGuardForX(...) throw() { return MakeGuard(...); }
and then
void f(...)
{
//...
ScopeGuard g = MakeGuardForX();
//...
}
(that is, if you had to have a throw() function for every one of
them?)
I sure wouldn't.
Goran.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]