throw -> catch
paradigm. When I say "sans-exceptions", I'm referring to the classic return-false-if-something-goes-wrong-I'll-deal-with-it-one-layer-up paradigm. (e.g. if (!myFunc()) cout << "Error" << endl;
)std::exception
or something of the like? Or do you have some specifics in mind that maybe you find helpful?
|
|
|
|
The basic, strong, and no-fail guarantees were originally described in ... with respect to exception safety. They apply to all error handling regardless of the specific method used, and so we will use them to describe error handling safety in general. ... In general, every function should provide the strongest guarantee that it can provide without needlessly penalizing calling code that doesn't need the guarantee.... Ideally, we write functions that always succeed and therefore can provide the no-fail guarantee. Certain functions must always provide the no-fail guarantee, notably destructors, deallocation functions, and swap functions... Most functions, however, can fail. When errors are possible, the safest approach is to ensure that a function supports a transactional behavior: Either it totally succeeds and takes the program from the original valid state to the desired target valid state, or it fails and leaves the program in the state it was before the call - any object's visible state before the failed call is the same after the failed call (e.g., a global int's value won't be changed from 42 to 43) and any action that the calling code would have been able to take before the failed call is still possible with the same meaning after the failed call (e.g., no iterators into containers have been invalidated, performing ++ on the aforementioned global int will yield 43 not 44). This is the strong guarantee. Finally, if providing the strong guarantee is difficult or needlessly expensive, provide the basic guarantee: Either the function totally succeeds and reaches the intended target state, or it does not completely succeed and leaves the program in a state that is valid (preserves the invariants that the function knows about and is responsible for preserving) but not predictable (it might or might not be the original state, and none, some, or all of the postconditions could be met; but note that all invariants must still be reestablished). The design of your application must prepare for handling that state appropriately. That's it; there is no lower level. A failure to meet at least the basic guarantee is always a program bug. Correct programs meet at least the basic guarantee for all functions; even those few correct programs that deliberately leak resources by design, particularly in situations where the program immediately aborts, do so knowing that they will be reclaimed by the operating system. Always structure code so that resources are correctly freed and data is in a consistent state even in the presence of errors, unless the error is so severe that graceful or ungraceful termination is the only option. When deciding which guarantee to support, consider also versioning: It's always easy to strengthen the guarantee in a later release, whereas loosening a guarantee later will break calling code that has come to rely on the stronger guarantee. Remember that "error-unsafe" and "poor design" go hand in hand: If it is difficult to make a piece of code satisfy even the basic guarantee, that almost always is a signal of its poor design. ... |
|
|
Call to config server failed: keyword not found. Can't get config keyword "logFileName" Fatal error: can't initialize program. |
int getTheAnswer(some args);
bool getTheAnswer(some args, int &result);
Not every function should be a firewall. That is, not every function can test its preconditions well enough to ensure that no errors could possibly stop it from meeting its postcondition. The reasons that this will not work vary from program to program and from programmer to programmer. However, for larger programs: [1] The amount of work needed to ensure this notion of ‘‘reliability’’ is too great to be done consistently. [2] The overhead in time and space is too great for the system to run acceptably (there will be a tendency to check for the same errors, such as invalid arguments, over and over again). [3] Functions written in other languages won’t obey the rules. [4] This purely local notion of ‘‘reliability’’ leads to complexities that actually become a burden to overall system reliability. However, separating the program into distinct subsystems that either complete successfully or fail in well-defined ways is essential, feasible, and economical. Thus, major libraries, subsystems, and key interface functions should be designed in this way. Furthermore, in most systems, it is feasible to design every function to ensure that it always either completes successfully or fails in a welldefined manner. |
dhayden wrote: |
---|
If you use exceptions, you'd probably be tempted to through the exception way down in get_config() and catch it way up in main(). In that case you're likely to just get a "keyword not found" message. What good is that??? Which keyword? Where wasn't it found? |
However, separating the program into distinct subsystems that either complete successfully or fail in well-defined ways is essential, feasible, and economical. Thus, major libraries, subsystems, and key interface functions should be designed in this way. Furthermore, in most systems, it is feasible to design every function to ensure that it always either completes successfully or fails in a welldefined manner. |
JLBorges wrote: |
---|
An obvious drawback of textual error information being dynamically accumulated at various points is that it violates a fundamental principle of robust error handling: the process of reporting a failure should itself be fail-safe. Attempting to circumvent this by, say, implementing fail-safe dynamic allocation mechanisms for error information would 'lead to complexities that actually become a burden to overall system reliability' |
Class exception
The class exception defines the base class for the types of objects thrown as exceptions by C ++ standard library components, and certain expressions, to report errors detected during program execution. Each standard library class T that derives from class exception shall have a publicly accessible copy constructor and a publicly accessible copy assignment operator that do not exit with an exception. These member functions shall meet the following postcondition: If two objects lhs and rhs both have dynamic type T and lhs is a copy of rhs, then strcmp(lhs.what(), rhs.what()) shall equal 0. |
Because copying std::exception is not permitted to throw exceptions, this message is typically stored internally as a separately-allocated reference-counted string. This is also why there is no constructor taking std::string&&: it would have to copy the content anyway. http://en.cppreference.com/w/cpp/error/runtime_error |
..... Attempting to circumvent this by, say, implementing fail-safe dynamic allocation mechanisms for error information ..... |
freddie1 wrote: |
---|
It occurs to me now though that an error object would be more useful than a single integral error code. |
std::exception
. Sorry for the confusion.std::exception
to communicate/handle problems.
|
|
freddie1 wrote: |
---|
....... , but I don't see how this as a simplistic model example could fail or increase program complexity in actual application |
DoProcessing
function, but the code doesn't mention an actual object, apart from the Error object.dhayden wrote: |
---|
What we found many years ago was that it's futile to check for out-of-memory conditions. |
Either you'll forget to check somewhere |
Most bugs are in a program's error handling logic, probably because that's the code that's hardest to test. |
SamuelAdams wrote: |
---|
When i think of a large project i'm thinking 20+ programmers |
one day the program crashes, or gives the wrong answer or the computer reboots. How are you going to tell your boss, this is the problem and it will take 8 hours to fix ? |
Building in logs and a way to trigger a crash dump if you hit a certain condition |