C++ Logo


Advanced search

Re: Re-purposing the function exception specification

From: Henry Miller <hank_at_[hidden]>
Date: Tue, 30 Jun 2020 21:06:05 -0500
Group 1 wants to continue because bad state might not be that bad. Consider a game where the requirements are support up to 8 monsters. It is a contract violation if the game file has more than 8, but probably nothing will break because the obvious implementation is some sort of list that can handle any number (up to system limits). You would want to log this violation in case something breaks later, but if the game designers want to try something it isn't an error if it works. They may change the requirements and you change the contract.

 Henry Miller
On Tue, Jun 30, 2020, at 12:14, andrei--- via Std-Proposals wrote:
> Good afternoon,
> Thanks for the VERY useful insight into the programming-by-contract work.
> I don't see why the "group #1" wants to continue after a contract violation - it's like trying to call "double sqrt(double x) require: x >= 0 { ... }" as "sqrt(-1)" - the obvious choice would be to throw something like "bad_contract" on a violation (which should then never be "checked"). I'll read up on this subject further, so thanks again.
> Perhaps I wasn't clear in my original proposal: what I'm thinking about is that
> ============================================================
> void foo() throws (a,b) { ... }
> ============================================================
> should mean "foo() can throw anything, but a and b must be handled explicitly and/or propagated".
> And you're correct in that a RAM-only implementation of e.g. an input stream where "int read() throw(io_exception)" never actually throws is an overkill... sometimes. Imagine we're writing a TEXT stream that reads ASCII-7 characters only - suddenly even an in-RAM implementation may throw "bad_data : public io_exception" if it encounters a byte that is >= 128!
> Cheers,
>  Andrey Kapustin
> On 30/06/2020 12:28, Henry Miller via Std-Proposals wrote:
>> There are serious proposals to add programing by contract to C++. In fact it was in C++20 until the last minute when it was released some important details were not worked out yet and so removed so the rest of the standard could ship. SG21 officially exists to figure out how to get programing by contracts into C++. Nobody can guarantee it will happen, but it is an official effort. 
>> Contracts in C++ are trying to work out the differences between 3 groups (note I'm not deep into contracts, but this is my understanding, so if someone says I'm wrong they are probably right) . Group 1 wants runtime control of what happens on breaking the contract : they may just log an error and continue in some cases. Group 2 wants to use whole program analysis to figure out if the program is correct, they believe they can find more errors with contracts, and are willing to let their analysis run for days. Group 3 wants the compiler to optimize around the contract if the input contract is no nullprt, they will remove nullprt checks found later. You can see group one and two are at odds, since if you can continue beyond a contract violation the optimizer can't assume the contract for optimization. 
>> Note that each of the above groups is perfectly valid in isolation. Different people have different priorities based on their domain and so for different people different views win as most important, but all are valid. 
>> Exception specifications are in my mind just another contract. A function won't return nullprt, a functions return is always greater than the input (implies something near INT_MAX). A function will only throw network error. The above 3 groups give us ways of thinking about what if we are wrong. 
>> I think unifying with contracts is good because it gives you a framework for talking about what Happens if I'm wrong. Obviously if you annotate everything that can't happen because it is trivial for a compiler to figure out where you failed to catch an exception. However if you are just starting to add annotations contracts give us the framework to reason about what we want to about violations. 
>> One issue with exception speciations as you propose them is that not all possible exceptions apply in some specific cases. Read() could throw a network error if you are reading from the network, but I know I'm an embedded system with no network interface and so why should I catch a network error just to make the exception specifiers happy that I don't throw it after calling read... Hopefully contracts let us say this is an axiom in my environment, without adding more new syntax. 
>> I hope that helps. 
>> On Mon, Jun 29, 2020, at 21:19, andrei--- via Std-Proposals wrote:
>>> Good morning,
>>> I'll have to think on what you wrote, and I thank you for the feedback. Nevertheless, I'm not sure I understand your
reasoning entirely (unless I'm missing something) - so, please, elaborate.
To explain: to me a "programming-by-contract" always meant the perception of functions (methods) as "services" which "require"
something from the caller and, in return, "ensure" something on behalf of a callee. If I were to use a HYPOTHETICAL P-B-C syntax
in a C/C++ case, I could write:
char * strchr(const char * str, int ch)
  require: str != nullptr;
  require: ch != 0;
  ensure: (result == nullptr) || (result >= str && result < (str + strlen(str)) && *result == ch);
{ /* implementation */ }
Maybe this is influenced by me knowing only Eiffel as a language that has a direct support for "programming-by-contract"
(I have developed another C-level one, but that's not important in this context). This, by the way, is a feature that I would ALSO 
like to see becoming a part of C++ standard, but I see no way of it ever becoming accepted by the C++ committee.
What's the difference from the "checked exceptions" enforcement? I say it is:
  * In a correctly written program no precondition/postcondition (contract) or assetrion violations
    would ever be triggered. Any of these violations mean a bug in the program.
  * In a correctly written program THAT RUNS IN A PERFECT WORLD no exception would ever be thrown.
    There'll be no "database_exception"s to signal that the DB engine is down, no "bad_alloc"s to signal
    that we're out of free store, etc.
  * What I'm proposing is to specialise the handling of exceptions that can occur in a correctly written
    program running in an imperfect world. Can "io_exception" happen? Yes, due to a genuine CRC error.
    Can a "network_exception" happen? Yes, if the line is down. Can a "bad_cast" happen? Yes, if the program
    is badly written, so "bad_cast" should never be "checked".
So, to recapitulate, I would want, in a perfect world, for the C++ to offer both programming-by-contract and
checked-exceptions; but the two have a different purpose:
  * P-B-C is a tool that can aid in program verification (at compile-(!) and run-time), while
  * If "exception handling" if a mechanism to make sure some errors "cannot be ignored", then "checked exceptions"
    is a tool to ensure some of these errors "cannot be ignored at compile time".
Just an example of when I may need both:
class db_exception { ... };
class cursor { ... };
class database
  cursor get_first_record(void * rec) throw(db_exeption)   // throws "
>>> db_exeption" on an I/O error
    require: rec != null;        
>>> // if, on entry, "rec" is "nullptr" we have a bug in the code
  { ... }
  bool get_next_record(cursor & c, void * rec)
>>>  throw(db_exeption) 
>>> // throws "
>>> db_exeption" on an I/O error
    require: c != null;           // if, on entry, "c" is "nullptr", it's a bug in the code
>>>     require: rec != null;   
>>>       // 
>>> if, on entry, "rec" is "nullptr" we have a bug in the code
    ensure: (result == false) ||  // either the call failed...
            (old(c) > c);         // ...or, if it succeeded, the cursor has advanced
>>>   { ... }
>>> };
>>> =========================================================================
Incedentally, if I could get the C++ community interested in a "programming-by-contract", I would also
consider myself happy - but that would require an extra syntax to the C++, which I have no chance of getting
past the C++ committees. Thirty-five years of practical experience with two dozen languages are, naturally,
no match for a bunch of well-meaning one-language experts.
Thanks again for your response, and especially for talking about programming-by-contract - I did not
dare to raise that subject in a C++ world for fear of being ridiculed.
    Andrey Kapustin
>>> On 29/06/2020 14:49, Henry Miller via Std-Proposals wrote:
>>>> On Fri, Jun 26, 2020, at 22:12, andrei--- via Std-Proposals wrote:
>>>>> Good morning,
>>>>> You have, indeed, convinced me that the "int foo() throws(auto)" is a no-go and if it will never make it into the C++ language, the better for that; your few well-chosen examples have been informative.
>>>>> However, I'm still adamand that C++ needs to allow both un-checked and checked exceptions for its practitioners, depending on their needs:
>>>>>  * If I'm a writer of a shared library heavy on templates, then I'll never use this weird new feature, and my code will always be a valid C++.
>>>>>  * If I'm a writer on a project where safety means all, I want checked exceptions even more than I want programming-by-contract.
>>>> After thinking about this for a few days I think this second statement is wrong: I think you want programing-by-contract with exceptions as one contract type. (post condition) By making exceptions a contract you unify with another part of the language instead of needing new syntax - this alone makes it more likely you can get someplace with your want. Contracts also give you more/better options for what if the exception happens anyway. Without going with contracts, checked exceptions are all or nothing which means almost nobody will retrofit them: they niche that only a few will use and thus not worth adding to the standard. With contracts you can start adding checked exceptions incrementally with choices for how to find out you are wrong, and choices for how to handle being wrong. 
>>>> I don't follow contracts closely (though I feel like I should), but I think joining them and improving their papers to address exceptions is a better step for you. 
>>> -- 
>>> Std-Proposals mailing list
>>> Std-Proposals_at_[hidden]
>>> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
> -- 
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals

Received on 2020-06-30 21:09:42