What will happen to error handling in C ++ 2a
3r? 3580. 3r3-31. 3r33537. 3r33540. 3r33545. 3r? 3580. 3r33537. A couple of weeks ago, the main conference was held in the C ++ world - CPPCON 3r33535. . 3r33545. 3r? 3580. For five days in a row, from 8 am to 10 pm there were reports. Programmers of all denominations discussed the future of C ++, baited bikes and thought how to make C ++ easier. 3r33540. 3r33545. 3r? 3580. 3r33537. Surprisingly, many reports were devoted to error handling. Well-established approaches do not allow to achieve maximum performance or may generate code sheets. 3r33545. 3r? 3580. What innovations await us in C ++ 2a? 3r33540.
3r3566. 3r33545. 3r? 3580.
A bit of theory
3r33545. 3r? 3580. 3r33537. Conventionally, all erroneous situations in the program can be divided into 2 large groups: 3r34040. 3r33545. 3r? 3580. 3r3333391. 3r? 3580. 3r33535. Fatal errors. 3r? 3567. 3r? 3580. 3r33535. Not fatal, or expected errors. 3r? 3567. 3r? 3580. 3r31616. 3r33545. 3r? 3580. 3r3342. Fatal errors
3r33545. 3r? 3580. 3r33537. After them, it makes no sense to continue execution. 3r33545. 3r? 3580. For example, it is dereferencing a null pointer, passing through memory, dividing by ? or violating other invariants in the code. All that needs to be done when they occur is to communicate as much information as possible about the problem and complete the program. 3r33540. 3r33545. 3r? 3580. 3r33537. In C ++,
too much 3r3354. there are already enough ways to complete the program: 3r33540. 3r33545. 3r? 3580. 3r3333391. 3r? 3580. 3r33535. 3r361. std :: terminate 3r? 3567. 3r? 3580. 3r33535. 3r366. std :: abort
3r? 3567. 3r? 3580. 3r33535. std :: exit 3r? 3567. 3r? 3580. 3r33535. 3r376. std :: quick_exit
3r? 3567. 3r? 3580. 3r31616. 3r33545. 3r? 3580. 3r33537. Even libraries are beginning to appear to collect data on crashes (3r3384. 1 3r3-33566., [leech=https://www.boost.org/doc/libs/1_65_1/doc/html/stacktrace.html] 2 3r33566., 3r3-3888. 3 3r3-33566.). 3r33540. 3r33545. 3r? 3580. 3r33939. Not fatal errors
3r33545. 3r? 3580. 3r33537. These are errors of appearance which are provided by the logic of the program. For example, errors when working with the network, converting an invalid string into a number, etc. The appearance of such errors in the program in the order of things. For their processing, there are several generally accepted tactics in C ++. 3r33545. 3r? 3580. We will talk about them in more detail with a simple example: 3r33540. 3r33545. 3r? 3580. 3r33537. Let's try to write function void addTwo ()
using different approaches to error handling. 3r33545. 3r? 3580. The function should read 2 lines, convert them to 3r33528. int and print the amount. It is necessary to handle IO errors, overflow and conversion to a number. I will omit uninteresting implementation details. We will consider 3 main approaches. 3r33540. 3r33545. 3r? 3580. 3r3113. 1. Exceptions 3r34444. 3r33545. 3r? 3580.
3r33528. //Reads a string from the console
//On error, IO throws out std :: runtime_error
std :: string readLine (); 3r? 3580. 3r? 3580. //Converts the string to int
//In case of an error, it throws out std :: invalid_argument
int parseInt (const std :: string & str); 3r? 3580. 3r? 3580. //Add a and b
//in case of overflow it throws out std :: overflow_error
int safeAdd (int a, int b); 3r? 3580. 3r? 3580. void addTwo () {
try {3r38080. std :: string aStr = readLine (); 3r? 3580. std :: string bStr = readLine (); 3r? 3580. int a = parseInt (aStr); 3r? 3580. int b = parseInt (bStr); 3r? 3580. std :: cout safeAdd (a, b) std :: endl; 3r? 3580.} catch (const std :: exeption & e) {
std :: cout e.what () std :: endl; 3r? 3580.} 3r33580.} 3r33529. 3r? 3516. 3r33545. 3r? 3580. 3r33537. Exceptions in C ++ allow you to handle errors centrally without an extra 3r33528. noodles in the code 3r33529. , 3r34545. 3r? 3580. but you have to pay for it with a whole bunch of problems. 3r33540. 3r33545. 3r? 3580. 3r3333391. 3r? 3580. 3r33535. the overhead of handling exceptions is quite large; you cannot often throw exceptions. 3r? 3567. 3r? 3580. 3r33535. it is better not to throw exceptions from constructors /destructors and observe RAII. 3r? 3567. 3r? 3580. 3r33535. by the signature of the function, it is impossible to understand what exception can be removed from the function. 3r? 3567. 3r? 3580. 3r33535. the size of the binary file increases due to the additional code of support for exceptions. 3r? 3567. 3r? 3580. 3r31616. 3r33545. 3r? 3580. 3r33170. 2. Return codes 3r34444. 3r33545. 3r? 3580. 3r33537. The classical approach inherited from C. 3r33540. 3r33545. 3r? 3580.
3r33528. bool readLine (std :: string & str); 3r? 3580. bool parseInt (const std :: string & str, int & result); 3r? 3580. bool safeAdd (int a, int b, int & result); 3r? 3580. void processError (); 3r? 3580. 3r? 3580. void addTwo () {
std :: string aStr; 3r? 3580. int ok = readLine (aStr); 3r? 3580. if (! ok) {
processError (); 3r? 3580. return; 3r? 3580.} 3r33580. 3r? 3580. std :: string bStr; 3r? 3580. ok = readLine (bStr); 3r? 3580. if (! ok) {
processError (); 3r? 3580. return; 3r? 3580.} 3r33580. 3r? 3580. int a = 0; 3r? 3580. ok = parseInt (aStr, a); 3r? 3580. if (! ok) {
processError (); 3r? 3580. return; 3r? 3580.} 3r33580. 3r? 3580. int b = 0; 3r? 3580. ok = parseInt (bStr, b); 3r? 3580. if (! ok) {
processError (); 3r? 3580. return; 3r? 3580.} 3r33580. 3r? 3580. int result = 0; 3r? 3580. ok = safeAdd (a, b, result); 3r? 3580. if (! ok) {
processError (); 3r? 3580. return; 3r? 3580.} 3r33580. 3r? 3580. std :: cout result std :: endl; 3r? 3580.} 3r33529. 3r? 3516. 3r33545. 3r? 3580. 3r33537. Doesn't look so good? 3r33540. 3r33545. 3r? 3580. 3r33547. 3r? 3580. 3r33535. Cannot return the actual value of the function. 3r? 3567. 3r? 3580. 3r33535. It's very easy to forget to handle the error (when was the last time you checked the return code from printf?). 3r? 3567. 3r? 3580. 3r33535. You have to write error handling code next to each function. Such code is harder to read. 3r33545. 3r? 3580. With C ++ 17 and C ++ 2a we will consistently fix all these problems. 3r? 3567. 3r? 3580. 3r? 3569. 3r33545. 3r? 3580.
3. C ++ 17 and nodiscard
3r33545. 3r? 3580. 3r33537. In C ++, 17 r3r3251. 3r33528. The attribute nodiscard appeared. 3r3566. . 3r33545. 3r? 3580. If you specify it before declaring a function, the lack of validation of the return value will cause a compiler warning. 3r33540. 3r33545. 3r? 3580.3r33528.[[nodiscard]]bool doStuff (); 3r? 3580. /* * /
doStuff (); //Compiler warning! 3r? 3580. bool ok = doStuff (); //OK. 3r? 3529. 3r? 3516. 3r33545. 3r? 3580. 3r33537. Also 3r33528. nodiscard can be specified for a class, structure or enum class. 3r33545. 3r? 3580. In this case, the effect of the attribute will be extended to all functions that return values of the type marked 3r-3528. nodiscard . 3r33540. 3r33545. 3r? 3580.
3r33528. enum class[[nodiscard]]ErrorCode {
Exists, 3r38080. PermissionDenied
}; 3r? 3580. 3r? 3580. ErrorCode createDir (); 3r? 3580. 3r? 3580. /* * /
3r? 3580. createDir (); 3r? 3529. 3r? 3516. 3r33545. 3r? 3580. 3r33537. I will not give the code from 3r33528. nodiscard . 3r33540. 3r33545. 3r? 3580. 3r33300. C ++ 17 std :: optional 3r33545. 3r? 3580. 3r33537. In C ++ 1? 3r3305 appeared. 3r33528. std :: optional 3r? 3529. 3r3566. . 3r33545. 3r? 3580. Let's see how the code looks now. 3r33540. 3r33545. 3r? 3580.
3r33528. std :: optional readLine (); 3r? 3580. std :: optional parseInt (const std :: string & str); 3r? 3580. std :: optional safeAdd (int a, int b); 3r? 3580. 3r? 3580. void addTwo () {
std :: optional aStr = readLine (); 3r? 3580. std :: optional bStr = readLine (); 3r? 3580. 3r? 3580. if (aStr == std :: nullopt || bStr == std :: nullopt) {
std :: cerr "Some input error" std :: endl; 3r? 3580. return; 3r? 3580.} 3r33580. 3r? 3580. std :: optional a = parseInt (* aStr); 3r? 3580. std :: optional b = parseInt (* bStr); 3r? 3580. 3r? 3580. if (! a ||! b) {3r33580. std :: cerr "Some parse error" std :: endl; 3r? 3580. return; 3r? 3580.} 3r33580. 3r? 3580. std :: optional result = safeAdd (a, b); 3r? 3580. if (! result) {
std :: cerr "Integer overflow" std :: endl; 3r? 3580. return; 3r? 3580.} 3r33580. 3r? 3580. std :: cout * result std :: endl; 3r? 3580.} 3r33529. 3r? 3516. 3r33545. 3r? 3580. 3r33537. You can remove in-out arguments from functions and the code will become cleaner. 3r33545. 3r? 3580. However, we lose information about the error. It became unclear when and what went wrong. 3r33545. 3r? 3580. You can replace 3r33528. std :: optional at 3r33528. std :: variant 3r? 3529. 3r3566. . 3r33545. 3r? 3580. The code will turn out to be the same as with 3r33528. std :: optional but more cumbersome. 3r33540. 3r33545. 3r? 3580.
C ++ 2a and std :: expected
3r33545. 3r? 3580. 3r33537. 3r33528. std :: expected 3r? 3529. -It may be interesting
weber
Author19-10-2018, 02:17
Publication DateDevelopment / Programming
Category- Comments: 0
- Views: 323