Adam said:
You're going to have to clarify what you mean by WFC. I don't think
it means what you think it means. If you mean the .NET runtime, then
you can't use that in C++, so it's somewhat irrelevant. C++/CLI is
not C++.
Interesting statement. To be considered seriously, however, it requires much
better support than a simple assertion.
With an exception, inheriting from an STL one if you must inherit from
one (since it is 'always' used), though your problem honestly makes
little sense.
I do not think this statement is relevant to my point. The facts of life are:
a. C++ allows using exceptions not inherited from std::exception.
b. Many significant libraries use such exceptions.
c. Not using such libraries while writing your library or app is often not an
option.
d. Even when not using such libraries is an option in a particular case, it is
almost never wise to assume that this library or app *will never be used* with
such a library.
When you write your own library that uses WFC -- and of course STL how shall you
expose your library's errors and where should you catch std::exception (which
are fortunately rare) and guys from System::Exception hierarchy (which are much
more often)?
In general, if you have to catch std::exception or System::Exception
outside of the entry point of your program[1][2],
First of all, this does not make sense: the whole point of exceptions is that
you can catch them in more than one place.
Regardless, it is irrelevant: even if the above were true, it would still result
in duplicating code to implement app- or library- specific error handling policy
for STL's std::exception and other library's exceptions not derived from
std::exception.
you've done
something pretty wrong. I think you're trying to create artificial
problems.
Ad hominem? So far I have refrained from conveying my impression from this
discussion: that you are either in denial or have had little experience with
maintaining a useful library or application.
Surely, write a catch block?
Oh, sure. For example, if your error processing policy requires assigning
severity level to every error before logging it, you will surely be happy to
write a catch block where the severity is best known to be assigned, won't you?
Now think where in the code this place could possibly be. A little hint:
"generally" it's not in main(), regardless of whether you are handling
std::exception or other exception (unless your app/library is so small that you
"generally" call functions from other libraries in main()).
You seem to be confusing the response to
a condition with the condition itself,
As you are not providing any support for your statement, we should just believe
it, correct?
but I don't see anything here
that requires "special" handling or creates a problem.
It seems to me you cannot "see anything here" because you are carefully avoiding
actually reading my posts.
Nevermind that
I'm not quite sure how you'd mix the 3 libraries you mentioned in the
first place.
Any two of them would be enough, as my post clearly said -- but you chose not to
read it before answering, correct?
Huh? The only way I can observe an exception from a given library is
if I'm actually calling functions within the library. As such,
needing to handle the exception can't possibly make me more dependent
on the library!
Yes, it can. Do you really believe that dependency on a library is a boolean
yes/no variable?
Then just wait till your business manager asks you how much time it will take to
migrate an app from library XYZ to library ABC and answer him "No" (or "Yes" if
you are a yes-man; the outcome will probably be same if he or she is an
effective manager).
You're going to need to provide an actual example of
what you're talking about.
I already all but provided you the example of the code you will have to write to
encapsulate the dependency on exceptions and on error codes. As you chose not to
understand that, I do not have any reason to believe that providing even more
details will serve to any purpose but your attempts on nit-picking.
If you're writing exception handlers to catch exceptions from
libraries you do not call (directly or indirectly),
You really did not read my post before answering it, did you? Otherwise, how
could you possibly mistake "the library calls your application uses" for
"libraries that you do not call"?
then you're doing
something completely and totally wrong.
then.. of course. It is so convenient to blame others for things they did not
do, isn't it?
No, the code that responds to an error condition really shouldn't care
how you received the condition initially:
Sure it should even if because it syntactically has to. If you received the
condition via std::exception, you have to write "catch(...[std::]exception..)"
and if it was via AbcException that is not derived from std::exception, you have
to write catch(...AbcException..).
if you're going to log a
message in response to an error, the logging code doesn't care if you
got an exception, bungled about with return codes, or just made things
up. The same goes for displaying dialogs and just about every other
conceivable response.
Vague "logging code" and "any other conceivable response" may not care (you like
vagueness, don't you?), but the more specific code "calling logging function" or
"filling up dialogue fields" has to care, because otherwise:
a. It won't be called at the first place, because a specific exception type
won't be caught.
b. Even if it were somehow auto-magically called, it often would not have enough
data to fill up the logging function's arguments or the dialogue fields without
cracking the exception or error code.
The entire point of exception handling is that if you cannot (or need
not) respond to the condition, /you pretend that it does not exist/
[3].
I would not put it this way but, yes, the whole point of exception handling is
to facilitate handling error conditions at desired points in code. I am glad we
agree on this. I am sure you remember that I specifically stated that the
exceptions should not be thrown across library boundaries, not in general.
"Kicking can down the road" to your own code is ok. Kicking it across library
boundary is IMHO as irresponsible and morally objectionable as the
loved-by-USA-politicians kicking national debt to the next generation. Why are
you sure your library user will enjoy letting your stray std::exception to
terminate his or her program or catching it somewhere at his main() where he
only expects exceptions from his own functions or none at all?
If you're doing something else, then you're designing your code
entirely incorrectly. Such behavior is the height of
irresponsibility, because it is a fool's errand.
You love denouncing actions you just made up, don't you? In conjunction with
your aptitude for kicking the can down the road, this makes me wonder: are you a
politician by any chance?
C++ libraries need
not declare every exception they throw, so without walking every line
of code of every library you use (directly or indirectly), one cannot
even accomplish what you say should be done.
Exactly. Now you are at last talking about the subject of my post. If one of
your requirements is to log every error in a particular manner, it is
significantly more labor-consuming to do that "walking ever line of code" (which
you will have to do, no matter how much you hate the idea) with the
exception-based API than with error code-based API. First, the error code
dispatching is easier to formalize and then drive from configuration. Second,
error code-based error processing methods usually include some error types in
API function prototypes (as opposed to exceptions) so you will always have a hint.
It is instructive that in some libraries designed for critical applications by
serious ISPs (e.g. IBM's MQ Series C API), the error codes were returned in
output parameters. A programmer using such API does not have a chance to fully
ignore error conditions because s/he needs to define a variable (sometimes more
than one) to hold error codes. IBM's APIs are a good example because no other
company has had more experience with maintaining huge enterprise-level
applications depending on multiple libraries. It's also instructive that some
'newer-style' and best-usable POSIX C API calls have been re-designed same way
(think of strtol() that all but replaced atol()).
The above paragraph is not talking about exceptions but its subject is directly
related to the topic. Namely, if we arrange APIs error exposure design decisions
in the order from less to more prominent manifestations of possible error
condition by API functions, our order will look approximately as follows:
1. errno, GetLastError(), global variables, C++ functions without exception
specifications that throw and the likes. A programmer is not reminded about
possible error conditions in any way except possibly for the documentation.
2. C++ functions with exception specifications. A programmer is reminded about
possible error conditions if s/he has function prototype in front of him or her
while coding the call.
3. Error return codes, A programmer is reminded about error condition as in #2
plus they may be able to turn on some "unused function return value" compiler
warning or the like.
4. Errors are encoded in output parameters of API. A programmer cannot
unwillingly ignore an error condition.
The code to capture error codes is simpler,
shorter and faster than that needed to capture exceptions.
I don't see how it can ever be less code than exception handling[4].
- It is simpler because error codes are easier tabulated for their
classification or another mapping (potentially driven by the rules that are
configured externally to the program and that are to be applied to programs in
other languages than C++) to the entities required by the application error
handling policy.
- it is shorter (especially with error-code-based or errno-based approach to
error propagation) because well-encapsulated error-checking code is notably
shorter than equally well-encapsulated try {} catch{} code. E.g. compare:
if (!handleError(apiCall(args..)))
return;
or
if (!apiCall(args..) && !handleError(errno))
return;
to
try {
apiCall(args..);
} catch(XxxException &e) {
if (!handleError(e))
throw;
}
/* if apiCall does not encapsulate exceptions from *its library's* underlying
libraries, the above code would be even longer */
- it is fater because compiler can optimize nothrow code. In a (probably futile)
attempt to prevent pointless objections to this statement I am referring you to
the Ultimate Authority of the Standard, see a footnote to 17.4.4.8-2 in ISO/IEC
14882:2003(E) or 17.6.5.12-3 in ISO/IEC 14882:2011(E) about the reason why C
library functions shall not throw:
".. This allows implementations to make performance optimizations based on the
absence of exceptions at runtime."
At best, it can be equal, and in practice, it must necessarily be more
because you're explicitly doing things that are done implicitly with
exception handling.
Whom do you mean when you say "you're"? Me a library writer or me a library
user? Me a library writer is supposed to go extra mile to better serve a library
user. Me a library user will have to do more and more complex things when I use
exception-based API which my this and previous and previous-before-previous
posts explain at monotonously increasing and unnecessary for a reasonable reader
level of details.
Hardly. Just returning error codes doesn't suddenly let you mark
everything nothrow,
It allows you to "mark everything nothrow" without writing try-catch blocks.
Marking functions nothrow was not counted as part of the cost because it had to
be done regardless of the type of the underlying API if the decision to write
nothrow functions has been made.
due to the problem I mentioned above.
I fail to see how calling a method marked nothrow increases my
productivity at all.
As the rest of the paragraph explained, in increases your productivity by
reducing the time you need to read and understand the code with only explicit
exit paths. Oh, I forgot, you are not spending much time reading code.. this "on
top of it" advantage is probably not for you then.
It doesn't reduce what I need to be worried or
concerned about unless the code actually provides the nothrow safety
guarantee, which is considerably difficult.
Really? Valid C++ code that does not throw any exceptions itself and calls only
functions with an empty exception specification is guaranteed not to throw an
exception. Incidentally not throwing an exception *is* the definition of nothrow
exception safety guarantee.
Merely not throwing exceptions is of very little benefit to the
programmer.
I am not sure about that vague "merely not throwing exceptions" but exposing
only no-throw functions from a library API (which was my original and only
point) brings all the benefits I claimed.
Funny, I spend more time trying to figure out what was written in all
these technical papers I'm supposed to implement than looking at
anyone's source code. YMMV I guess.
Unfortunately, the equation is changed, but based on what you've said
I think you did the calculation completely incorrectly in the first
place. Actually supporting your position with real code,which I
strongly encourage you to do, is going to be rather difficult.
I provided more than enough use cases (and a little code snippet above, in this
post) to support my position whereas you chose to provide no use cases, let
alone the code, to support yours.
Adam
[1] Or the entry point to a thread or similiar concurrency construct
[2] Though one should almost certainly 'catch(...)' in these contexts.
[3] Handling it here includes cleaning up resources and then kicking
the can down the road.
[4] The occasional syntax foible aside.
-Pavel