| D1095R0/N2xxx draft 4: Zero overhead deterministic failure - A unified mechanism for C and C++ | Niall Douglas | 08/08/18 12:56 | Please find attached draft 4 of the previous C _Either(A, B) paper, with this edition completely rearchitected to meet WG14 feedback. It is very different from the C _Either proposal sent here earlier. Changes since draft 3:
I'm hoping that this draft strongly resembles the final paper to be submitted for Pittsburgh and San Diego, but we'll see what you make of it. I'm also going to send this draft to the Austin Working Group for feedback, as I promised WG14 that I would do. Thanks in advance for any comments. Niall |
| Re: D1095R0/N2xxx draft 4: Zero overhead deterministic failure - A unified mechanism for C and C++ | Niall Douglas | 10/08/18 01:34 | Just to keep everybody on WG21 up to date, WG14 likes this proposal a lot, and apart from the .left, .right and .positive being replaced with _EitherValue(), _EitherError() and _EitherHasValue(), the above is essentially the paper to be published. Obviously if you have any additional feedback, please send it. Niall |
| Re: [std-proposals] Re: D1095R0/N2xxx draft 4: Zero overhead deterministic failure - A unified mechanism for C and C++ | Jake Arkinstall | 10/08/18 02:49 | The version you posted is the most elegant that I have seen. My only concern was what the C guys would think about the errno discussion, but that was obviously misplaced. However, I think the new syntax is uglier than the struct approach. Can you provide a code example? I get why the prefix is wanted, but that prefix is screwing up the connection between name and meaning. If I said to you "either value", it will make you think theres some kind of selection between two values. Same with error and hasvalue. I think this is going to confuse the hell out of fresh eyes. -- |
| Re: D1095R0/N2xxx draft 4: Zero overhead deterministic failure - A unified mechanism for C and C++ | Dimitrij Mijoski | 10/08/18 03:22 | From C++ point of view this seems to me like an implementation detail for the zero-overhead deterministic exceptions, rather than actual language specification. I'd rather see an actual implementation, than a specification that we don't know if it works. Only when you try to implement something, you will notice all the details you are probably missing now. From C point of view, you are proposing a new feature, a new way of error handing in C, that is also very similar to the zero-overhead deterministic exceptions. Then you propose some compatibility between your C proposal and the C++ zero-overhead deterministic exception. The question is, why would you want to bring in a C++ feature into C? Should we maybe try to map C++ classes and polymorphism and templates into C features? Compatibility comes in two ways, calling C++ code from C, and calling C code from C++. Lets look at them.
|
| Re: [std-proposals] Re: D1095R0/N2xxx draft 4: Zero overhead deterministic failure - A unified mechanism for C and C++ | Jake Arkinstall | 10/08/18 03:55 |
The question is, as far as I'm concerned, Why wouldn't you? We are both facing a problem with error handling. The C approach doesn't scale, and the C++ approach is inefficient. So it is a known problem in both languages, for different reasons. If you have to work with some C library in a C++ setting (e.g. sockets), you notice that your code becomes rather messy. Half of your code is just in trying to catch errors in C and convert them into a format suitable for a C++ user (the other half is declaring would-be-temporaries just so that C functions can accept them as pointers). C handling errors in a sane way would easily cut my signalling codebase in half. We can't have exactly the same syntax, particularly because we have access to nicer syntax and want to keep it that way. We also have different conventions, and I don't want to be adopting C's. We also have a lot of shared functionality. Our math libraries, for example, are (though implementation defined) identical except for namespacing, and the errno setting in C prevents constexpr math. We could either branch off and make the two languages incompatible, or unify the approach to allow the communities to work together. A unified approach is easier for both to work with, and makes it easier on C++ programmers who want to interface with C. Low latency developers in particular will love that - and that's why SG14 is involved. |
| Re: [std-proposals] Re: D1095R0/N2xxx draft 4: Zero overhead deterministic failure - A unified mechanism for C and C++ | Dimitrij Mijoski | 10/08/18 04:12 | Error handing is not the only problem in C. It has a weak type
system, so maybe try to bring the C++ type system into C? C memory
management is all manual, so maybe bring RAII into C? Or just stop using C and start using C++. In your example, just start using a C++ socket library in the first place. There are plenty of them.
|
| Re: D1095R0/N2xxx draft 4: Zero overhead deterministic failure - A unified mechanism for C and C++ | Bengt Gustafsson | 10/08/18 04:12 | Some small comments: 1. The names left and right are not descriptive for their uses. I would prefer expected and unexpected or value and error or something like that. 2. The abs() example has a few flaws. a) The value returned when errno is set is 0 for the original code and INT_MIN for the rewritten version which is confusing. b) The proposed rewrite from a setting of errno + a return statement to a return _Failure() seems scary as the programmer may insert some other code between the errno setting and the return statement. c) The code that shows the call site for the abs() example is not equivalent to the original as the function may set errno to 0, which is taken as no error by the original code but is still not "positive" by the new means. Upon further inspection it may be that this feature is just not explained clearly enough. I think your intention is that within a _Fails(errno) marked function errno is a local variable. If it is non-zero when a return statement is hit the transformation occurs. This seems to work better than my initial understanding that it is the code pattern of setting errno followed by return that is transformed. d) It is unclear to me what happens if errno is already != 0 when abs() is called. To get backwards compatibility it seems logical that the local errno gets set from the threadlocal errno when abs is entered. If this is the case, please show how the expression: abs(a) + abs(b) is translated (with the problem being that if abs(a) lazily returns an error then abs(b)'s initing of its errno would use a stale value). 3. In your last C example it is unclear if the code is the user written code or the compiler rewrite, and in the latter case if the user had a errno = 0 statement at all. |
| Re: [std-proposals] Re: D1095R0/N2xxx draft 4: Zero overhead deterministic failure - A unified mechanism for C and C++ | Jake Arkinstall | 10/08/18 04:46 |
And they wrap the C sockets library provided by the operating system. Sure, the wrappers provide a friendlier interface, but while the error handling approaches remain inconsistent there is still SOMEONE putting in all the effort to handle C with C++. If you're in an environment where latency correlates with financial loss, you are unlikely to use boost::asio (fantastic as a library as it is). I'd love it if there was a widely used operating system designed specifically for C++-like access to low level functionality, but while we have Unix-like systems and windows, we interface with the OS in C. (Though I do realise the main gotcha here - those low level C libraries won't be changing any time soon) |
| Re: D1095R0/N2xxx draft 4: Zero overhead deterministic failure - A unified mechanism for C and C++ | inkwizyt...@gmail.com | 10/08/18 04:49 | If C++ provide some interface for C, then C could
start and end C++ exceptions. This could be useful when C do not want
propagate exception in some cases. Each exception will need its own
function that do it. From C++ perspective implementation will be simple:
`(*ptr).~T();` How `std::uncaught_exceptions`
will exactly work with value exception? You mentioned overhead of cold
cache, but it will be implemented in this or this is thing you want
avoid? Maybe give programmers direct access to this? 99% of code could live with this overhead other probably not. It would see 3 functions: Each value exception need call `inc` in constructor and `dec` in destructor of not moved out object. Or you set `set_abort` (per thread) that will prevent incorrect use of `uncaught_exceptions` in scope guards. With this allow you to skip `inc` and `dec` in your value exceptions. This could be done locally. Rest of code will correctly use `uncaught_exceptions` and some part where performance is required you prevent use of this. It could work other way around, you disable this globally but enable it back for some external library calls that use `uncaught_exceptions` internally. |
| Re: [std-proposals] Re: D1095R0/N2xxx draft 4: Zero overhead deterministic failure - A unified mechanism for C and C++ | Pedro Alves | 10/08/18 09:32 | On 08/10/2018 09:34 AM, Niall Douglas wrote:I understand you're aiming at absolute minimum to improve chances of success, but I wonder whether there is any plan to later on add an stdbool.h-like header that gives these new symbols prettier non-_Uppercase (non-implementation-reserved) names, like: #define fails _Fails #define try _Try #define catch _Catch #define failure _Failure #define either _Either ... etc. ? It feels like if the proposal succeeds, this new failure mechanism will be used pervasively, and people will quickly get bored with typing all that _Uppercasing for such a core feature, and projects will grow such defines themselves. With that, for mixed C and C++ codebases, and for ease of codebase migration from C to C++, it'd be ideal if C code using the prettified "try" and "catch" could compile with a C++ compiler. I'm not sure that would be possible. Seems worth it to me to aim at making this path down the road possible. I was a bit surprised to not see this discussed in the paper. Your example in the paper would read instead: #include <stdwhatever.h> int some_function(int x) fails(float) { return (x != 0) ? 5 : failure(2.0); } const char *some_other_function(int x) fails(float) { int v = try(some_function(x)); return (v == 5) ? "Yes" : "No"; } int main(int argc, char *argv[]) { if(argc < 2) abort(); either(const char *, float) v = catch(some_other_function(atoi(argv[1]))); if(v.positive) { printf("v is a successful %s\n", v.left); } else { printf("v is failure %f\n", v.right); } return 0; } Thanks, Pedro Alves |
| Re: D1095R0/N2xxx draft 4: Zero overhead deterministic failure - A unified mechanism for C and C++ | Niall Douglas | 12/08/18 11:50 | On Friday, August 10, 2018 at 11:22:56 AM UTC+1, Dimitrij Mijoski wrote: A major compiler vendor intends to implement P0709 by early 2019. The papers forming this discussion is to set the general approach any prototype should take. Regarding better type interop between C and C++, well baby steps first. Let's solve the exception throwing interop problem, and move on from there. I would also add that there are languages which only speak C, but have much better interop with C++'s richer type system. Niall |
| Re: D1095R0/N2xxx draft 4: Zero overhead deterministic failure - A unified mechanism for C and C++ | Niall Douglas | 12/08/18 15:12 |
Already changed to _EitherValue() and _EitherError().
Optimisation already significantly rewrites the programmer's code. Everything is "as if" nowadays.
Correct, real errno becomes exclusively the compiler's responsibility when calling a _FailsWithErrno function.
If you declare your function _FailsWithErrno, it needs to be ported to the new system. Specifically, that you can't read errno. Trying to do so fails the compile. For code which calls a _FailsWithErrno function, we know such functions never read errno, but they as-if may set it. The compiler optimises accordingly.
I will clarify the wording and spell out why and how statements were elided by the compiler due to having no possible side effect. Niall |
| Re: D1095R0/N2xxx draft 4: Zero overhead deterministic failure - A unified mechanism for C and C++ | Niall Douglas | 12/08/18 15:19 |
Me personally, I'd like std::uncaught_exceptions to apply to type-based exception throws only, and not drag down all value-based exception throws with latency just for the 0.01% of users which ever use std::uncaught_exceptions. It could maybe be enabled with a compiler option. But assuming WG21 won't go for that, my proposal is that the TLS reference counting only apply to std::error, it is not available under Freestanding C++, and can be disabled at the command line. Niall |
| Re: [std-proposals] Re: D1095R0/N2xxx draft 4: Zero overhead deterministic failure - A unified mechanism for C and C++ | Niall Douglas | 12/08/18 15:20 | I understand you're aiming at absolute minimum to improve chances WG14 already has bikeshedded on such a suite of said macros. So tldr yes that's exactly what would happen, if this proposal gets any steam. Nobody would be writing underscoreCapital anything. Niall |