On 28/08/2015 00:58, Hartmut Kaiser wrote:
Third, the terminology 'consuming' and 'non-consuming' future does not make any sense (to me). More importantly it is by no means Standard- terminology - thus reasoning in terms of those makes it much more difficult for us to talk about the same thing.
future.get() moves the result of the promise from the internal state, thereby consuming it. Further calls to get() or then() fail.
shared_future.get() copies the result of the promise from the internal state, thereby not consuming it. Further calls to get() or then() succeed.
All of this is an irrelevant implementation detail. Nobody forces the future to share its state with a promise. It could very well be a packaged_task, for instance, or any other asynchronous provider.
I find it hard to imagine this being a difficult concept.
I don't see a reason why anybody would do this. If you know (as a user) you need shared ownership you just make it explicit by assigning the boost::future to a boost::shared_future (as you showed in Example A).
His point was that someone might forget and then it becomes a runtime exception, which can be painful to find if it's in a seldom-executed part of the code (perhaps error handling).
If somebody forgets, then an exception is the correct thing to do. Alternatively an assertion could be used in this case as well. BTW, an exception is not difficult to find, even gdb can break on a specific exception being thrown.
future h=async_file("niall.txt"); shared_future h2(h); // Call these continuations when h becomes ready for(size_t n=0; n<100; n++) // Each of these initiates an async read, so queue depth = 100 h2.then(detail::async_read(buffer[n], 1, n*4096));
Sure, but Example A is just fine, as said.
Note that it's not user code calling then() on the futures in most cases -- see the examples. Instead the future is passed as a "precondition" parameter to a wrapper API in the library, which is what actually registers the continuation.
I think Niall's point is that it's harder for the wrapper to know whether it's going to be called a single time or multiple times for a given precondition future, so it's safest if that is accepted only as a shared_future.
BUT that means that there is absolutely no benefit (and some drawbacks) to returning non-shared futures, as a result (there are only potentially some savings if they're never used as preconditions).
I suppose arguably the wrappers could be templated to accept either future or shared_future, which might mitigate that. But perhaps there are reasons why that's undesirable as well.
If an API enforces wrong behavior, then the API itself is wrong. Thus, if the wrappers require for the code to return a shared_future, then the wrappers are wrong in the first place. They force the user to deal with library internals which are irrelevant and misleading. Regards Hartmut --------------- http://boost-spirit.com http://stellar.cct.lsu.edu