5 ms·
I will just pray for the better error handling proposals. Both are fine. For modern code with no global state, especially the multithreaded kind, everything is
by vkazanov 8y ago
I will just pray for the better error handling proposals.
Both are fine. For modern code with no global state, especially the multithreaded kind, everything is better than the errno.h mess.
- jstimpfle 8y agoI don't think "error handling" should be a thing in C. Errors are just data, not a special case. errno.h isn't so much builtin to C. It's just part of the standard libary, and more so of POSIX. It's only used for interfacing with the OS. And it's not that bad, since errno is a thread local variable.
- vkazanov 8y agoC is not a holy cow, is it? It's just a very, very popular language. Error handling is important in programming in general, and having ways to implement it conveniently helps a lot. Yes, errors are just data. And everything is data, including the code itself, right? It a question of abstractions introduced to the language and the program at question. errno.h is bad. It's inconvenient, it's global, even if it's a thread-local something, it's cumbersome to use. AFAIK, there are 2 error-related proposals for inclusion. One is making errors special, the other is a bit more generic. Both would improve on the current situation.
- jstimpfle 8y agoYou don't need to change the language. You need to add new functions to the standard library. There are already quite a few versions of older functions, with an "_r" suffix in their names (from the top of my head, strtok_r(), although that is not a great example) that take an explicit pointer to local state, eliminating global state. You could just make more of them if you want to eliminate errno. > It a question of abstractions introduced to the language and the program at question. I don't think these kind of abstractions are in the spirit of C. As it says in the Charter linked here, "provide only one way to do an operation". A more acceptable way to me would be allowing multiple return values in general. But maybe there are technical reasons why this is not done. Or it has to do with the complexities that such a mechanism would add to the expression syntax.
- vkazanov 8y agoI absolutely agree about the multiple return values in general. Unfortunately the discussion around N2289 is more about providing something a lot like exceptions for both C and C++ but without all the exception problems. The proposal really has some ugly corners related to how one actually handles the errors. You can read the discussion here: https://www.reddit.com/r/cpp/comments/9owiju/exceptions_may_finally_get_fixed/ https://www.reddit.com/r/cpp/comments/9owiju/exceptions_may_...
- Rusky 8y agoThe error handling proposal adds multiple return values- not as records/tuples, but as a sum type so only one can be returned from any given call. This could certainly be made more general, or just done via tagged unions (the way multiple return values can be done via structs), but there are massive advantages to be had by building it into the language and calling convention. The implementation can be far more efficient than any of a) the standard `int f(ret *out)` idiom, b) using errno, or c) some sort of `struct my_tagged_union f()` (which nobody uses anyway because it's a huge pain syntactically). In addition to being cheaper, the proposed built-in version of (c) is also syntactically simple enough to become a standard, shared mechanism.
- jstimpfle 8y ago> The implementation can be far more efficient Really, really hard to believe. Not only since returning unions is already legal (isn't it)? What function do you have in mind where an explit error-return-pointer argument is not close to maximally efficient?
- Rusky 8y agoThe first problem is that the error-return-pointer-argument approach must go through memory, because the caller can request that the return value go anywhere at all. The second problem is that existing calling conventions for `struct { bool tag; union { .. }; }` put everything in memory anyway, using a hidden pointer argument. Further, there's no way to put this type in the standard library because C doesn't have generics. The new implementation can put that single-bit tag in the CPU's carry flag where it has dedicated branch instructions and doesn't interfere with other values. It can leave the actual return value in a register without any kind of union aggregate lowering. So far this is all just calling convention tweaks, and could be done by pattern-matching user-defined tagged unions, but building it into the language a) makes it possible to standardize its semantics and connect it to platforms' C ABIs so other languages can also participate and b) makes it far simpler to implement and use so it's actually likely to be adopted.
- goalieca 8y agoI haven’t read the proposals but I will comment anyways :) I do like how Go does the posix interface where errors are handled as multiple return statements and cleanups using defer.
- unwind 8y agoI'm being a bit nit-picky, but in C code is not a lot like data, no. You cannot take sizeof a function, and you can't copy functions around, for instance. The address of a function is a value ("data"), but not the function itself.
- anticensor 8y agoIf you could sizeof or copy a function by a simple assignment, how would you port it into a machine with separate code and data spaces?
- kazinator 8y agoWith a crapload of special case code, nests of #ifdefs and a dollop of "we should have thought of that possibility at this level of abstraction" refactoring.
- anticensor 8y agoISO/IEC C committee does not think like that. The people considering it portable assembler are long gone away from that committee. The current members consider it a mid-high level programming language with a defined abstract semantics. Conditional compilation is just a compatibility workaround to them.
- vkazanov 8y agoYeah... C is not Lisp, okay. :-) But that only strengthens the point: language features are just abstractions we use to reason about data. One can always strip all the abstractions and end up working with raw bits. If there's a useful way to think about certain kinds of data - it might be useful to codify that way as a language feature. Such as specialised error handling.
- kazinator 8y agoIn Lisps, a function is also just a reference, and not the object itself. You can't take its size or copy it. (You could copy it if your dialect provided a copy-function function, of course. I don't think I've ever seen one. Such a thing could be useful if it provided a frozen copy of a closure's lexical environment, that would be unaffected by mutations when the original copy of the function is invoked.)
- OpenBSD-reich 8y agoError handling is best done with asserts which check for off-domain inputs and off-range outputs, and explode the running program if triggered. I mean, security trumps convenience and uptime, right guys? Y'all do build your functions with domain and range in mind, right guys?
- msla 8y ago> I mean, security trumps convenience and uptime, right guys? Not in a heart-lung machine, no.
- deleted 8y ago[deleted]
- gpderetta 8y agoerrno is a̶n̶ ̶a̶b̶o̶m̶i̶n̶a̶t̶i̶o̶n̶ ̶t̶h̶a̶t̶ ̶s̶h̶o̶u̶l̶d̶ ̶b̶e̶ ̶k̶i̶l̶l̶e̶d̶ ̶w̶i̶t̶h̶ ̶f̶i̶r̶e̶ a failed experiment. That kind of error-reporting-via-side-effect not only is incredibly error prone, it is also problematic for optimizations.
- kazinator 8y agoYou might want to familiarize yourself with how errno works. It is not used for reporting. Rather, various functions in C and POSIX return an indication that some exceptional situation has occurred. Then errno holds a value which classifies the cause of that situation. It doesn't report the error. (In the case of portable ISO C, we can't even depend on that; we must set errno to zero before calling most library functions. If that function fails, then if errno is nonzero, it has a value pertaining to that function.) Some newer functions in POSIX return the errno value, like pthread_mutex_lock. That makes sense when the return value has no other purpose like "bytes processed" or whatever. The errno values are positive, so they combine poorly with in-band signaling. Anyway, optimizing is possible based on the return value: like putting the expected success case into the hot code path or whatever.
- gpderetta 8y agoThank you, I'm aware how errno works. C has been able to return structs for the last 30 years, so returning a pair of value+error code is no big issue even in C. Optimizing errno is so easy that many compilers by default do not even update errno for many math functions because it hopelessly destroy any chance of vectorizing and aggressive optimizations.
- kazinator 8y agoYour idea would seriously uglify code. If we want the function's result and the error, we have to capture the return value in an object. If we just do if (library_function(arg).result < 0) .. we have lost the errno. For the complete analysis, we must now do: struct error; error = library_function(arg); // test error.result; if bad, work with error.num Yes we have had struct returns (proper, on-stack ones) for more than 30 years, which means we could have had API's like this for 30 years. I agree that errno for math functions isn't such a great fit. It's much better suited for I/O and resource management.
- rwmj 8y agoUnlikely to happen. This isn't "how do we rethink C", there are plenty of other, better places for that to happen. It's how do we make incremental improvements that benefit us, but remain in the spirit of C and compatible with the enormous existing body of code. In fact there's a Charter describing it better than I can: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2086.htm http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2086.htm
- vkazanov 8y agoI read the Charter, yes. Now, how do those error-related proposals violate it? Such as: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2289.pdf http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2289.pdf It seems to be very delicate about being backwards-compatible.
- vkazanov 8y agoIn fact, I think WG14 was in favour if the N2289 proposal.
- kazinator 8y agoerrno is multithreaded. It's just a macro for a function like: #define errno (*__get_errno_location()) This is equivalent to having getter-setter functions, like in Microsoft's Win32 API, which has GetLastError and SetLastError.
- naasking 8y agoAnd it's terrible for performance, so much so that it can't be used everywhere errors may need to be returned.