
On 04/28/2013 07:30 PM, Vicente J. Botet Escriba wrote:
Le 28/04/13 18:27, Pierre T. a écrit :
On 04/28/2013 05:20 PM, Vicente J. Botet Escriba wrote:
Le 28/04/13 16:22, Pierre T. a écrit :
On 04/27/2013 03:30 PM, Vicente J. Botet Escriba wrote:
Le 27/04/13 09:39, Pierre T. a écrit :
On 04/26/2013 08:17 PM, Vicente J. Botet Escriba wrote: > * 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 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; } }
Humm, I don't know if this is enough. I'll drop the default constructor for the moment. When an implementation will be available, feedback from users will help to find relevant use-cases.
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 ? You would need to move construct using placement new
new (&value_) T(forward<T>(val)) ;
BTW, exception_ptr move is equivalent to a copy, isn't it?
It is, but expected may store any Error types and not only exception. Adding a trait method "default_error" would help if the Error hasn't a default constructor.
Could we add a dummy field in the union such as:
union{ T value, Error error, bool internal_uninitialized}; I don't think this is a good ideea.
But I'm not sure it's what you meant.
> *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). Humm, in the example I gave above f an g are not continuations but expected<> producers. Anyway, the variadic version of then could have its uses. I like it, but it is orthogonal to the when_all feature.
What do you think about letting the continuation returning an expected<>? It has the advantage to allowing the continuation to transport exceptions also without throwing.
I forgot to claim the change but in my "then" version, the function passed to "then" must return an expected (or void). So all functions are expected producers. It's strange to return something else because it would always be a good value into an expected. The "then" chaining could not return error cases. I don't think there are much differences between when_all and then(f,g). However, I propose to let this feature out of the GSoC proposal. But of course, we wouldn't let it out of the summer, it's just because time is lacking and adding features in a hurry could break the proposal consistency.
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.
The name should work independently of the name of the 'then()' function. Oh nice remark.
on_exception? if_exception? catch_all? capture?
e.catch_all(eh);
And maybe catching/capturing an exception type at a time.
e.catch_one<E1>(e1).catch_one<E2>(e2).catch_all<EA>(eh);
e.capture<E1>(e1).capture<E2>(e2).capture<all>(eh);
But a C++ try/catch is not worst, or is it?
try { v=e.get(); } catch(E1 & ex) { e1(ex); } catch(E2 & ex) { e2(ex); } catch(...) {}
I would say "on_error", so it would be a generic name for error type that aren't exception. Thanks again, Pierre T.