Yes its me. There is an error on the web page that I have already reported. My name is Vicente J. Botet Escriba. Vicente J. is my fisrt name and Botet Escriba is my last name.On Monday, May 18, 2015 at 6:30:38 PM UTC-4, Jeffrey Yasskin wrote:On Mon, May 18, 2015 at 2:57 PM, Nicol Bolas <jmck...@gmail.com> wrote:
> I just want to see the "sto*" functions get string_view ASAP. We shouldn't
> wait on `expected` just to accomplish that.
That seems like a straightforward paper to get through the committee.
If you write it, I'll try to prevent the group from creeping its
scope.
I don't share the FileSystem design to report errors. I have used it in Boost.Chrono, and it really doesn't scale. It is difficult to say it is prior art when we include it in the FS TS. For me, it is the temporary C++ standard way of defining interfaces that report errors without using exceptions until we have a better way.I think at this point, we should focus on getting the core feature: parsing strings via string_view. And those many malign the FileSystem TS solution, it is prior art on dealing with error codes in standard library C++.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
On Wed, May 20, 2015 at 11:08 AM, Vicente J. Botet Escriba <vicent...@wanadoo.fr> wrote:Le 19/05/15 03:01, Nicol Bolas a écrit :
Yes its me. There is an error on the web page that I have already reported. My name is Vicente J. Botet Escriba. Vicente J. is my fisrt name and Botet Escriba is my last name.On Monday, May 18, 2015 at 6:30:38 PM UTC-4, Jeffrey Yasskin wrote:On Mon, May 18, 2015 at 2:57 PM, Nicol Bolas <jmck...@gmail.com> wrote:
> I just want to see the "sto*" functions get string_view ASAP. We shouldn't
> wait on `expected` just to accomplish that.
That seems like a straightforward paper to get through the committee.
If you write it, I'll try to prevent the group from creeping its
scope.
The expected proposal was blocked as the expected proposal was proposing an alternative way to report errors and the standard C++ has already one: exceptions. A study of all the alternative ways to reporting errors was requested so we can take a decision on which ones could be used once the advantages and liabilities of each approach are detailed. I'm not a paper writer and writing such a paper needs to be done carefully and it would takes time. If there are any volunteers to write it, please do it, it will be a pleasure to help. Of course, I will be very happy is this paper unblocks the expected proposal.
I don't share the FileSystem design to report errors. I have used it in Boost.Chrono, and it really doesn't scale. It is difficult to say it is prior art when we include it in the FS TS. For me, it is the temporary C++ standard way of defining interfaces that report errors without using exceptions until we have a better way.I think at this point, we should focus on getting the core feature: parsing strings via string_view. And those many malign the FileSystem TS solution, it is prior art on dealing with error codes in standard library C++.
VicenteYes I really think we should use FS as an example of "there must be a better way to do this". To me that is either:- just use exceptionsor- something like expected<>The FS should have half as many functions as it currently has. I hope that is fixed before standardization.
On Wednesday, May 20, 2015 at 11:09:01 AM UTC-4, Vicente J. Botet Escriba wrote:
Yes its me. There is an error on the web page that I have already reported. My name is Vicente J. Botet Escriba. Vicente J. is my fisrt name and Botet Escriba is my last name.
The expected proposal was blocked as the expected proposal was proposing an alternative way to report errors and the standard C++ has already one: exceptions. A study of all the alternative ways to reporting errors was requested so we can take a decision on which ones could be used once the advantages and liabilities of each approach are detailed. I'm not a paper writer and writing such a paper needs to be done carefully and it would takes time. If there are any volunteers to write it, please do it, it will be a pleasure to help. Of course, I will be very happy is this paper unblocks the expected proposal.
On Wednesday 20 May 2015 11:09:20 Nicol Bolas wrote:
> But one thing I'm adamantly against is being forced to use error codes.
> They should be *optional*, not mandatory. In C++, exceptions are the
> standard way of signaling errors; I shouldn't have to deal with error codes
> unless I choose to.
True, and on the other hand I'm hoping that the committee realises that
there's a world without exceptions. There are many environments where
exceptions cannot be used and it's irrelevant whether those reasons are good
or bad. Making APIs that work for those environments increases their use-base.
On Wednesday, May 20, 2015 at 11:09:01 AM UTC-4, Vicente J. Botet Escriba wrote:
Yes its me. There is an error on the web page that I have already reported. My name is Vicente J. Botet Escriba. Vicente J. is my fisrt name and Botet Escriba is my last name.
The expected proposal was blocked as the expected proposal was proposing an alternative way to report errors and the standard C++ has already one: exceptions. A study of all the alternative ways to reporting errors was requested so we can take a decision on which ones could be used once the advantages and liabilities of each approach are detailed. I'm not a paper writer and writing such a paper needs to be done carefully and it would takes time. If there are any volunteers to write it, please do it, it will be a pleasure to help. Of course, I will be very happy is this paper unblocks the expected proposal.
The string_view to int paper could be a good candidate for such a study.
On Wednesday, May 20, 2015 at 2:29:47 PM UTC-4, Thiago Macieira wrote:On Wednesday 20 May 2015 11:09:20 Nicol Bolas wrote:
> But one thing I'm adamantly against is being forced to use error codes.
> They should be *optional*, not mandatory. In C++, exceptions are the
> standard way of signaling errors; I shouldn't have to deal with error codes
> unless I choose to.
True, and on the other hand I'm hoping that the committee realises that
there's a world without exceptions. There are many environments where
exceptions cannot be used and it's irrelevant whether those reasons are good
or bad. Making APIs that work for those environments increases their use-base.
I agree with Thiago.
Unfortunately, exceptions cannot be the standard C++ way.
A good example of this is the cppformat library which is a modern printf replacement. This library throws exceptions if you don't match the number of parameters with the number of "{}" in your format string. The first thing I did was disable exceptions here. Imagine your entire system crashing just because someone forgot to format a log message correctly?
I've been in plenty of environments where a crash at the wrong time in production could result in huge unrecoverable losses.
On Wednesday, May 20, 2015 at 2:49:59 PM UTC-4, Matthew Fioravante wrote:A good example of this is the cppformat library which is a modern printf replacement. This library throws exceptions if you don't match the number of parameters with the number of "{}" in your format string. The first thing I did was disable exceptions here. Imagine your entire system crashing just because someone forgot to format a log message correctly?
This is actually a perfect example of why you shouldn't turn off exceptions like this. As well as illustrating the limitations of `expected`. Why?
Because by turning off exceptions, you allow bugs to exist.
If the user passes the wrong parameters to the logging function, that is a bug. And because you turned off exceptions, you won't know its there unless you check your log.
And even then, you might simply miss it (it's not clear what happens in the event of such an error). Not to mention, there's the question of how `cppformat` behaves when you turn off exception handling; does it do something sane, like not emit a string, or does it start walking and/or trashing random memory?
`expected` can't fix that, since there's no return value from logging. So not only is there an error, but you have no means of communicating that error. Oh, you could return an `expected<void, E>`, but who checks a return value from a function that doesn't have one?
It should also be noted that the exception behavior is much better than, say, printf's behavior when given the wrong set of parameters. That behavior being undefined, which will almost certainly involve walking and/or trashing random memory. A guaranteed exception/crash is better than a "maybe crash, maybe overwrite the balance on someone's account."
I've been in plenty of environments where a crash at the wrong time in production could result in huge unrecoverable losses.
Arbitrarily trashing memory at the wrong time is just as bad, if not worse.
Exceptions aren't bad; generating errors is what is bad. Exceptions are just telling you that you generated one and forcing you to actually deal with it, rather than pretending it didn't happen.
It should also be noted that, if you enter a scenario where crashing via exception is not an option, you can always bracket it in a catch(...) clause. In that case, it's basically about cleanup for something fantastically bad that happened; just restore things to a sane state and proceed.
I can live with an incorrect line in a log file (and an additional message in the log file to tell me there was an error with cppformat that I need to fix).
I cannot live with a crash in production for something benign which could result in more losses for my organization than my yearly income.
On Wednesday, May 20, 2015 at 2:49:59 PM UTC-4, Matthew Fioravante wrote:A good example of this is the cppformat library which is a modern printf replacement. This library throws exceptions if you don't match the number of parameters with the number of "{}" in your format string. The first thing I did was disable exceptions here. Imagine your entire system crashing just because someone forgot to format a log message correctly?
This is actually a perfect example of why you shouldn't turn off exceptions like this. As well as illustrating the limitations of `expected`. Why?
Because by turning off exceptions, you allow bugs to exist. If the user passes the wrong parameters to the logging function, that is a bug. And because you turned off exceptions, you won't know its there unless you check your log. And even then, you might simply miss it (it's not clear what happens in the event of such an error). Not to mention, there's the question of how `cppformat` behaves when you turn off exception handling; does it do something sane, like not emit a string, or does it start walking and/or trashing random memory?
`expected` can't fix that, since there's no return value from logging. So not only is there an error, but you have no means of communicating that error. Oh, you could return an `expected<void, E>`, but who checks a return value from a function that doesn't have one?
It should also be noted that the exception behavior is much better than, say, printf's behavior when given the wrong set of parameters. That behavior being undefined, which will almost certainly involve walking and/or trashing random memory. A guaranteed exception/crash is better than a "maybe crash, maybe overwrite the balance on someone's account."
But one thing I'm adamantly against is being forced to use error codes. They should be optional, not mandatory. In C++, exceptions are the standard way of signaling errors; I shouldn't have to deal with error codes unless I choose to.
On 20 May 2015 at 13:09, Nicol Bolas <jmck...@gmail.com> wrote:But one thing I'm adamantly against is being forced to use error codes. They should be optional, not mandatory. In C++, exceptions are the standard way of signaling errors; I shouldn't have to deal with error codes unless I choose to.But that is not strictly true.For instance, set::insert(value_type const&) returns a pair whose second element tells you whether or not the insertion succeeded.
It's more about how unusual the failure is, whether it is likely to be handled locally, etc.
On 20 May 2015 at 13:09, Nicol Bolas <jmck...@gmail.com> wrote:But one thing I'm adamantly against is being forced to use error codes. They should be optional, not mandatory. In C++, exceptions are the standard way of signaling errors; I shouldn't have to deal with error codes unless I choose to.But that is not strictly true.For instance, set::insert(value_type const&) returns a pair whose second element tells you whether or not the insertion succeeded.It's more about how unusual the failure is, whether it is likely to be handled locally, etc.
On 2015-05-20 16:52, 'Matt Calabrese' via ISO C++ Standard - Future
Proposals wrote:
> Discriminated union (treated as "expected" with N unexpected cases):
> Cons:
> Errors can be silently missed if the return value is not checked (common if
> return is void or there are other side-effects)
I'd like to point out here that this doesn't really apply. If you don't
return a meaningful result, but might return an error, and you *need* to
enforce that the caller checks if an error occurred, then the proper way
to do that is *not* std::expected or something like it, but rather a
class that may-or-may-not contain an error, that keeps an internal state
noting if it has been checked or not, and throws on destruction if it
has not been checked.
On 2015-05-20 11:08, Vicente J. Botet Escriba wrote:
> The expected proposal was blocked as the expected proposal was proposing
> an alternative way to report errors and the standard C++ has already
> one: exceptions.
I'd have to reiterate the position taken by others here... exceptions
are horrible. Plenty of code pretends that exceptions don't exist and/or
is built with exceptions disabled.
The cognative overhead of catching
exceptions vs. just checking something like a std::expected is high.
The
performance cost of [error handling using] exceptions vs. something like
std::expected is high.
expected<std::unique_ptr<Mesh>, std::string> readMesh(const std::string& file_name) {
try {
// geometry library returns mesh by pointer, throws exception on error.
return expected<std::unique_ptr<Mesh>, std::string>{std::unique_ptr<Mesh>{geometry::readMesh(file_name)}};
} catch (std::exception& exc) {
return make_unexpected<std::unique_ptr<Mesh>>(exc.what());
} catch (...) {
return make_unexpected<std::unique_ptr<Mesh>>("unknown exception, cannot load mesh from file: " + file_name);
}
}
On Wednesday 20 May 2015 12:02:03 Nicol Bolas wrote:It is the standard way. The question is whether we should also, in some cases, have an alternative. And what that alternative should be.Sorry, I didn't mean to start a discussion about the merits of exceptions again. I'm just going to say that I agree with Nicol's paragraph above. The C++ standard has made a choice for exceptions, that's fine. All I'm asking is that there should be some alternative in some cases.
On Wednesday, May 20, 2015 at 2:05:31 PM UTC-4, Tony V E wrote:
On Wed, May 20, 2015 at 11:08 AM, Vicente J. Botet Escriba <vicent...@wanadoo.fr> wrote:
Le 19/05/15 03:01, Nicol Bolas a écrit :
I don't share the FileSystem design to report errors. I have used it in Boost.Chrono, and it really doesn't scale. It is difficult to say it is prior art when we include it in the FS TS. For me, it is the temporary C++ standard way of defining interfaces that report errors without using exceptions until we have a better way.I think at this point, we should focus on getting the core feature: parsing strings via string_view. And those many malign the FileSystem TS solution, it is prior art on dealing with error codes in standard library C++.
Vicente
Yes I really think we should use FS as an example of "there must be a better way to do this". To me that is either:
- just use exceptions
or
- something like expected<>
The FS should have half as many functions as it currently has. I hope that is fixed before standardization.
I personally have no complaints about the filesystem's method of doing error codes. But I also wouldn't be adverse to seeing `expected` used instead of error code parameter outputs.
But one thing I'm adamantly against is being forced to use error codes. They should be optional, not mandatory. In C++, exceptions are the standard way of signaling errors; I shouldn't have to deal with error codes unless I choose to.
So I would say that filesystem should keep the same number of functions. Either that, or it shouldn't support error codes at all.
But one thing I'm adamantly against is being forced to use error codes. They should be optional, not mandatory. In C++, exceptions are the standard way of signaling errors; I shouldn't have to deal with error codes unless I choose to.
On Wednesday, May 20, 2015 at 8:09:20 PM UTC+2, Nicol Bolas wrote:But one thing I'm adamantly against is being forced to use error codes. They should be optional, not mandatory. In C++, exceptions are the standard way of signaling errors; I shouldn't have to deal with error codes unless I choose to.Others are against being forced to use exceptions..
Are exceptions the standard for errors?
This reminds me of something I saw recently (not sure if it was this
group) about not spending resources on malicious actions... but that's
exactly what exceptions do; turn the case of bad input into the most
expensive code path.
> Not to mention the fact that you pay the `expected` cost all the time.
> Every return value gets bigger. When you get the return value, you have to
> copy/move it out of the `expected`; not unless you plan on storing it. So
> you can forget about eliding return values when you use `expected`; you
> have to do a copy/move. Not unless you're going to use the value in-situ.
auto x = foo().value();
auto exp = foo();
if(!exp) {
//handle error
}
auto& val = exp.value();
On 2015-05-20 20:22, Nicol Bolas wrote:
> On Wednesday, May 20, 2015 at 7:30:06 PM UTC-4, Matthew Woehlke wrote:
>> The cognative overhead of catching
>> exceptions vs. just checking something like a std::expected is high.
>
> ... how, exactly?
auto result = parse(input);
if (result)
do_something(*result);
do_something_else();
if (result)
do_something_again(*result);
- vs -
T result; // grr, can't use auto
auto is_valid = false;
try {
result = parse(input);
is_valid = true;
}
catch(/* what goes here? */) { /* don't care */ }
if (is_valid)
do_something(result);
do_something_else();
if (is_valid)
do_something_again(*result);
try
{
auto result = parse(input);
do_something(result);
do_something_else();
do_something_again(result);
}
catch(<insert exception here>)
{
do_something_else();
}
So... a simple if() vs. a try-catch that requires me to know what I am
supposed to be catching and potentially messes up my scope and/or
control flow.
> You can't possibly talk about the performance of something without cover
> the actual circumstances of the specific instance. It doesn't matter if the
> cost of catching an exception was even 100x the cost of a failed
> `expected`... if the exception is only thrown one out of every 10,000
> executions.
Yes, but what if failure is frequent? In file parsing, this might not be
unusual. For that matter, what if I'm parsing a file whose fields may or
may not be numbers, and the way I determine that is by trying to parse
the field as a number and seeing if it succeeds?
This reminds me of something I saw recently (not sure if it was this
group) about not spending resources on malicious actions... but that's
exactly what exceptions do; turn the case of bad input into the most
expensive code path.
> Not to mention the fact that you pay the `expected` cost all the time.
> Every return value gets bigger. When you get the return value, you have to
> copy/move it out of the `expected`; not unless you plan on storing it. So
> you can forget about eliding return values when you use `expected`; you
> have to do a copy/move. Not unless you're going to use the value in-situ.
>
> So I'd say that your statement requires some actual evidence.
I'd say that for your statement also. I could easily write a highly
optimized `expected<T, exception_ptr>` whose size is only sizeof(T) +
sizeof(void*) and whose move ctor consists of a POD assignment of a T
and a void*, and a clear of a void*.
(And the compiler ought to be able
to optimize away the dtor of the old instance.) *And* you're asserting
that RVO is not possible for that? (Why?)
I can write an even better `expected<T, ErrorEnum>`... it's standard
layout and trivially copyable with size sizeof(T) + sizeof(ErrorEnum).
> The one case where I've seen exceptions really shine is when you have an
> all or nothing situation where you want to try a bunch of steps and just
> bail out completely the same way if any one of them fails. For example you
> want to do a bunch of file IO routines, or parse a text file, or do a
> series of system calls, etc..
...so just don't check if your std::expected is valid :-).
This (not talking to you - Matthew F. - specifically any more) is why I
don't understand why the exception-worshippers object so strongly to
std::expected.
If you prefer exceptions, *you can have exceptions*,
almost trivially. They just aren't *forced* on you.
Conversely, the burden of forcing exceptions on those of us that don't
want exceptions is much heavier.
"expected", or discriminated unions in general, provide many benefits, and some drawbacks, over exceptions:==========Feel free to correct anything that is wrong in the above assessment or to add to it. I am not an expert in this domain and these are just my personal thoughts.
Not that the following has any chance of changing the language, but my personal thoughts have been that C++ exceptions are good, but would ideally be much more like a discriminated union in terms of implementation. By that I mean they still would not affect the return type of the function, as is already the case with current exceptions though not with "expected", but they would deal with a closed set of types rather than an open set. You would be able to query at compile time the set of possible exceptions and append to that set when making higher level functions (important for generic code -- this is also analogous to using noexcept to determine if an expression can throw or not). You'd always be able to effectively make an open set of exception types by using some kind of a polymorphic type (I.E. a type-erased object with a large small-object optimization), though you would lose some of the functionality of C++ exceptions as they are in the language. Exception propagation would continue to work as-is, as would accessing of the normal result of a function. Constructors would also work as easily as they do right now.This would basically just be C++ exceptions, only implemented on top of a discriminated union instead of an open set of types.
On Thursday 21 May 2015 16:52:09 Tony V E wrote:
> There are problems with a closed set of exceptions, which have been felt
> with C++ throw(a,b,c) clauses and java checked exceptions, etc.
> In particular, I can't call pointer-to-functions or virtual functions, etc,
> because I can't know the set of possible exceptions.
Which is a good thing. You need to know what the function you're calling can
throw, regardless of whether you're doing a direct call, indirect or virtual.
The reimplementation of the function cannot suddenly start throwing unexpected
errors. The catch handler may not know how to handle those. New exception
types need to be caught and handled at a lower level, never leak.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358
There are problems with a closed set of exceptions, which have been felt with C++ throw(a,b,c) clauses and java checked exceptions, etc.In particular, I can't call pointer-to-functions or virtual functions, etc, because I can't know the set of possible exceptions.Sure I could use polymorphic exceptions then, but I suspect we'd end up using them almost everywhere, leaving us where we are now.
I don't think that everyone would use polymorphic exceptions everywhere since, in true C++ fashion, we would probably have a way to refer to the list of exception types that an exception could through
On Thu, May 21, 2015 at 5:11 PM, Thiago Macieira <thi...@macieira.org> wrote:On Thursday 21 May 2015 16:52:09 Tony V E wrote:
> There are problems with a closed set of exceptions, which have been felt
> with C++ throw(a,b,c) clauses and java checked exceptions, etc.
> In particular, I can't call pointer-to-functions or virtual functions, etc,
> because I can't know the set of possible exceptions.
Which is a good thing. You need to know what the function you're calling can
throw, regardless of whether you're doing a direct call, indirect or virtual.
The reimplementation of the function cannot suddenly start throwing unexpected
errors. The catch handler may not know how to handle those. New exception
types need to be caught and handled at a lower level, never leak.
So why are java developers moving away from checked exceptions, and C++ has deprecated throws clauses?Lack of Discipline?
"exact match": catching a base class type of the thrown type does not success. IOW, no hierarchy traversing takes place on each catch() construct. This requires minimum (static) typeinfo data and O(1) matching algorithm.
Le 19/05/15 03:01, Nicol Bolas a écrit :
Yes its me. There is an error on the web page that I have already reported. My name is Vicente J. Botet Escriba. Vicente J. is my fisrt name and Botet Escriba is my last name.On Monday, May 18, 2015 at 6:30:38 PM UTC-4, Jeffrey Yasskin wrote:On Mon, May 18, 2015 at 2:57 PM, Nicol Bolas <jmck...@gmail.com> wrote:
> I just want to see the "sto*" functions get string_view ASAP. We shouldn't
> wait on `expected` just to accomplish that.
That seems like a straightforward paper to get through the committee.
If you write it, I'll try to prevent the group from creeping its
scope.
The expected proposal was blocked as the expected proposal was proposing an alternative way to report errors and the standard C++ has already one: exceptions. A study of all the alternative ways to reporting errors was requested so we can take a decision on which ones could be used once the advantages and liabilities of each approach are detailed. I'm not a paper writer and writing such a paper needs to be done carefully and it would takes time. If there are any volunteers to write it, please do it, it will be a pleasure to help. Of course, I will be very happy is this paper unblocks the expected proposal.
> What error reporting mechanisms have I missed?
You haven't mentioned errno; although I'm not sure it counts as a separate mechanism, it's odd not to see it mentioned. While it's largely an adjunct to "special return value", it can also function as an out of band error code:
> For some system calls and library functions (e.g., getpriority(2)),
> -1 is a valid return on success. In such cases, a successful return
> can be distinguished from an error return by setting errno to zero
> before the call, and then, if the call returns a status that
> indicates that an error may have occurred, checking to see if errno
> has a nonzero value.
- http://man7.org/linux/man-pages/man3/errno.3.html
Another important mechanism, though without support in modern languages, is FORTRAN-style alternate return - passing in (an) alternative program location(s) to return to. Dijkstra disliked it, of course, along with multiple entry, but with appropriate syntactic structure I think it could actually work in C++. See http://programmers.stackexchange.com/a/118793
Also, how would you classify setjmp/longjmp? I'm fairly sure there are still some C libraries that use it as their preferred error handling technique. Clearly it's similar to exceptions in that both are stack based non local return, but there's plenty of differences, unwinding being just one
auto exp_value = await TargetFunc(...);
The await operator is telling the compiler that the result of the
expression contains possibly an error that needs some specific
handling (See the resumable functions proposal). Even if the main
case of the resumable functions proposal is to manage asynchronous
cases, there is no reason to don't use it synchronously. Gor has
already show this cases in his proposal. auto exp_value = await TargetFunc(...);
is transformed to something like auto exp_value = TargetFunc(...);
if(!exp_value)
{
return exp_value.error();
}
Gor has already show this cases in his proposal.
Even if more complex, the idea is that the function must return some wrapped type W<T>.
The await expression
is transformed to something likeauto exp_value = await TargetFunc(...);
auto exp_value = TargetFunc(...); if(!exp_value) { return exp_value.error(); }
and the yield expression
yield a;
makes use in some way of the explicit constructor of the return type.
return W<T>(a);
IMO, the major advantage to returning a variant type is that we can add syntactic sugar on top of this general interface.
If the resumable functions proposal were adopted by the C++ standard, we will see a lot of libraries that will adapt their types to this new mechanism. I don't see how the standard library would not do the same.
On Friday, May 29, 2015 at 1:41:29 AM UTC-4, Vicente J. Botet Escriba wrote:
Le 23/05/15 19:07, Nicol Bolas a écrit :
On Wednesday, May 20, 2015 at 11:09:01 AM UTC-4, Vicente J. Botet Escriba wrote:Le 19/05/15 03:01, Nicol Bolas a écrit :
Hi and thanks for writing this report.
There is a specialization of the variant return type that makes easier the error propagation. This needs some language changes as e.g. the proposed await/yield operator. This syntactic sugar approach helps on error propagation (non-local error resolution) in an almost transparent way, so the user doesn't need to check directly if there is an error or not in order to propagate it. All it needs is to say using the await operator if the expression can return a error or not.
The await operator is telling the compiler that the result of the expression contains possibly an error that needs some specific handling (See the resumable functions proposal). Even if the main case of the resumable functions proposal is to manage asynchronous cases, there is no reason to don't use it synchronously.auto exp_value = await TargetFunc(...);
I admit that my knowledge of this feature is limited to a presentation Herb made well over a year ago, when talking about Visual Studio having a preliminary implementation of "async/await". Obviously, things have progressed in a rather different direction, since one of the features (async) isn't there anymore and something else is (yield). So feel free to correct me if I'm wrong.
But isn't issuing some form of coroutine at least marginally expensive? Compared to a regular function call, that is.
It works with coroutines, but not only. See below.
I mean, let's take the case of a generalized "parse" function, to which you pass a generalized file IO system. So you're parsing from a file.
The code using the parse function will `await` on it. But the parse function should also `await` on the file IO system it's been given, since that can error too. Any errors it gets are propagated to the caller. So that's two `await` overheads, all just to propagate the error.
By contrast, the overhead for exceptions is paid for with `try` blocks, at the site where you're actually resolving the error. And the runtime cost is paid only when you actually throw, not simply because you might throw. And you don't have to stick `await` in every place that might result in an error.
auto exp_value = await TargetFunc(...);
auto exp_value := TargetFunc(...);
Note that I'm not proposing anything here.
I don't really see the advantage over exceptions here.
Gor has already show this cases in his proposal.
Even if more complex, the idea is that the function must return some wrapped type W<T>.
The await expression
is transformed to something likeauto exp_value = await TargetFunc(...);
auto exp_value = TargetFunc(...); if(!exp_value) { return exp_value.error(); }
and the yield expression
yield a;
makes use in some way of the explicit constructor of the return type.
return W<T>(a);
IMO, the major advantage to returning a variant type is that we can add syntactic sugar on top of this general interface.
Is that what is actually part of that proposal, or is that what you're suggesting should be done?
If it's the latter, I would be very much against that.
You shouldn't try to slap unrelated constructs into other proposals just because they might be able to work there.
Resumable functions is about functions that can be suspended and resumed; giving them special properties, such that the compiler actually generates code that has certain expectations on the user (like the return type of the current function) feels really arbitrary.
Especially when we already have an adequate solution to the problem of returning errors to locations in code defined by the caller. One that requires zero local syntax.
Also, if you can't get people to let `return {x};` use explicit constructors, I fail to see how you can get them to agree that `yield x;` will. That'd be remarkably inconsistent.
If the resumable functions proposal were adopted by the C++ standard, we will see a lot of libraries that will adapt their types to this new mechanism. I don't see how the standard library would not do the same.
As I understand what you're discussing, the await/yield syntax you're suggesting
is a form of syntactic sugar which is useful for propagating an error to a higher level with less apparent code. So while the direct syntactic burden at the local level is lessened (no need for an explicit conditional), the rest of it is still there.
So what you're suggesting would only be widely adopted if the reason everyone was avoiding `expected` beforehand was due to having to explicitly test the conditional, just to propagate the error to the higher level.
I don't think that's the case. I'm rather sure that `expected` will be widely adopted or not based on its own merits, not this specific scenario.
expected<int> ok() { return 42; }
expected<int> bad() { return{ error, 5 }; }
expected<int> f() {
auto x = await ok();
auto y = await ok();
return x + y;
}
expected<int> g() {
auto x = await ok(); // unpacks result of OK into x
auto y = await bad(); // will propagate the error as the result of g()
return x + y;
}
namespace std { namespace experimental {
template <typename T, typename... Whatever>
struct coroutine_traits<expected<T>, Whatever...> {
struct promise_type {
expected<T>* r;
void return_value(T val) { r->val = val; r->err = 0; }void return_value(error_t, int err) { r->err = err; }
expected<T> get_return_object(expected<T>* x) { r = x; return expected<T>{}; }
};
};
} }
Note, this works with private version of compiler with a few tweaks that are not part of P0057R0/R1.
--
namespace std { namespace experimental {
template <typename T> struct is_suspendable : true_type {}; // awaiting on anything can result in suspension
}}
namespace std { namespace experimental {
template <typename T> struct is_suspendable<expected<T>> : false_type {}; // but not on expected
}}
template <typename Awaitable>
enable_if<is_suspendable<Awaitable>::value> await_transform(Awaitable const&) {
static_assert(false, "expected<T> does not support awaiting on suspendable types");
}
Please see attached file
Here are a usage example:expected<int> ok() { return 42; }
expected<int> bad() { return{ error, 5 }; }
expected<int> f() {
auto x = await ok();
auto y = await ok();
return x + y;
}
expected<int> g() {
auto x = await ok(); // unpacks result of OK into x
auto y = await bad(); // will propagate the error as the result of g()
return x + y;
}
Coroutine promise for the function returning expected<T> is:namespace std { namespace experimental {
template <typename T, typename... Whatever>
struct coroutine_traits<expected<T>, Whatever...> {
struct promise_type {
expected<T>* r;
void return_value(T val) { r->val = val; r->err = 0; }void return_value(error_t, int err) { r->err = err; }
expected<T> get_return_object(expected<T>* x) { r = x; return expected<T>{}; }
};
};
} }
Note, this works with private version of compiler with a few tweaks that are not part of P0057R0/R1.
template <typename T, typename P>
void await_suspend(expected<T> & val, std::experimental::coroutine_handle<P> h) {
h.promise().return_value(error, val.error());
h.destroy();
}
For some reason, google group keeps deleting my messages :-) . Let's see if replying via e-mail help.
So the function destroys the coroutine_handle. Notably, the handle for the function that it's currently executing within. After all, until the suspend-resume-point is reached, we're still in the body of that function (though to be fair, we'll hit that immediately after executing that statement). Isn't that a bit dangerous?
Coroutine is considered suspended before the call to await_suspend, thus calling any resumption member function such as resume() or destroy() are legal from await_suspend. P0057R0 was vague about that. It was clarified in P0057R1 which should be out this week in post-Kona mailing.
Also, as I understand it, the coroutine_handle holds the function's stack. Isn't the promise object on the stack? So wouldn't destroying it automatically destroy every object on the stack, including the promise? So how does the caller get a return value?
Due to RVO, return value is in the caller frame, it is not part of the coroutine frame.
coroutine_trait like you mentioned. That way, the await_suspend function doesn't have to do that kind of dangerous handle destruction.
Possibly. This code is a day old. Maybe there are better ways of doing it. I will be also curious to see what clang guys come up with as this scenario is of interest to them as well.
future<expected<T, E>> foo()
{
auto data = await long_process(); //Schedules execution for later.
auto val = await data.something(); //Retrieves an `expected` and propagates the error
return val; //Should initialize the `T` part of the `expected`.
}