On 04/28/2013 05:20 PM, Vicente J. Botet Escriba wrote:
Le 28/04/13 16:22, Pierre T. a écrit :
Le 27/04/13 09:39, Pierre T. a écrit :
On 04/26/2013 08:17 PM, Vicente J. Botet Escriba wrote:
Le 26/04/13 14:22, Pierre T. a écrit : I like this proposition but at first, it seems a bit "overkill". I would like to have more opinions about it. Through it seems to be less compatible with the "visitor" idea. The visitor resolves on types and most of the error code have a same type. So it would visit only one type. Right. But this independent on whether you use a wrapper exception or not, isn't it? Yes, it gives a good point to the traits.
* Default Constructor or constructor from nullexpect What is the advantage of having a expected instance that doesn't have neither a value nor an exception? How would the user manages with this possibility? Are you looking to make expect movable?
Basically, I noticed that classes without default constructor (or default state) are burdensome to use. Indeed, you cannot store an expected in a class as a member if not initialized in the constructor. Or doing something like:
expected<int> e; if(…) else(…) return e;
Through, I removed the default constructor because I found it unclear. I use a nullexcept because it was a good idea in Boost.Optional with nullopt. Yes it was one. But the definition of optional is there to allow a state that means value not present. expected<> is designed to have a value or an exception. Could you answer to the question How would the user manages with this possibility? By testing == nullexcept (operator== not in the proposal, sorry), however you are right, expected must contains an exception or a value. On the other hand, it's nice to provide a default constructor, so an idea could be to add a method "unitialized_error()" in the
On 04/27/2013 03:30 PM, Vicente J. Botet Escriba wrote: trait class. Why do you need a default constructor. Because it can be useful for the following situation:
expected<T> func(bool b) { expected<T> e; if(b) { e = f(); // use e } else { e = g(); // use e } return e; } Some programmers prefer to have only one return statement instead of having: expected<T> func(bool b) { if(b) { expected<T> e = f(); // use e return e; } else { expected<T> e = g(); // use e return e; } }
Finally, I'm not sure to understand how it's related to the movable ability of Boost.Expected.
I let you try to define move move semantics for expected and you will see why this is related. After thinking more on this the moved object could present its exception_ptr if it has one, so move semantics doesn't force to have an uninitialized expected<> Not sure to get it,
expected(expected&& e) // nothing in the initializer list because we don't know if we must move T or Error. { if(has_value) // move value else // move error } In case T or Error haven't a default constructor, the previous code can't work, right ? Could we add a dummy field in the union such as: union{ T value, Error error, bool internal_uninitialized}; But I'm not sure it's what you meant.
* then/otherwise issues
But the design error is not on the otherwise function but on the then function.
Resuming, I'm not more for the 'then' function.
I'm totally agree with you. But I think that I misnamed the "otherwise" method. I think the "then" method is really useful, it's like an automaton. With e.then().then().then(), the treatment is stopped anytime when an error occurs in a then method.
How would this be stopped? throwing and exception? It's not stopped but the "then" method launch the function only if expected contains a value. And what return if it not contains a value?
Also, I think that the function taken by then should return void And which value would you pass to the second then call? The same. Sorry I don't follow. What The do you mean by the same? or an expected.
In fact the otherwise method is the error visitor. A common usage would be to chain it in the end:
e.then(...).then(...).then(...).visit_error(error_visitor);
visit_error is called if any "then" return an error. I found it wonderfully useful. This could be possible; It would need that then() returns an expression template storing all the functions on the chain and only
Maybe. But if we don't know how it is stopped chaining them has no sens. When the call to the function is asynchronous it is easy to stop it but synchronously it implies an exception which is against the goal of expected. the call to the visit_error would evaluate the chain. I don't think it is worth proving this lazy evaluation. To complete what I've said, the visit_error() would launch the visitor only if the expected contains an error. No lazy evaluation is needed. Ah I think I see now what was the initial intention. I suspect that you mean that as the result of e.then(f) would transport the exception stored on e if e is not valid, then the exception would be relayed until there is a call to visit_error and no call to any function will be done. Yes this is quite similar if we had exceptions and we had a try/catch block
f0().then(f1).then(f2).then(f3).visit_error(error_visitor);
try { f3(f2(f1(f0()))); } catch(...) { error_visitor(); }
It would be great if we could have the equivalent for try { h(f(), g()); } catch(...) { error_visitor(); }
when_all(f(), g()).then(h).visit_error(error_visitor);
when_all could return a expected
> or something more specific so that then would extract the members of the tuple to call h with two parameters. This start to look more and more like the proposal for futures (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3558.pdf)
Sorry for the poor explanations about it, but you guessed correctly. I read the futures proposal and the ideas in there are really interesting. However because expected is not related to the time, I think we could just do the following: e.then(f, g).then(h).inspect(error_inspector) where "then" takes an unlimited number of functions. In this case it would return a tuple-wrapper class specific to the expected class (in case users would like to store tuple inside expected). About the "inspect" method, I try to find another name instead of visit_error because it makes me think to the pattern visitor which is too specific to hierarchy classes. I found that inspect was nice because it's like in a factory where the chief "inspects" that everything has been alright.
I'll try to send it before tomorrow. I will update it with the trait classes and the others comments you made. I just have a question about the proposal, should I use the Boost macro for noexcept,… or is it better (for a proposal) to write it with the standard tools ? I use to document using c++11 without any reference to the emulations.
Best, Vicente
Thanks, Pierre T.