This proposal describes a variation of std::function supporting non-copyable target objects. It removes the copy constructor, and adds in-place construction of target objects.
Here’s a first draft of a non-copyable polymorphic call wrapper proposal: http://bit.ly/uniqfun
Here’s a first draft of a non-copyable polymorphic call wrapper proposal: http://bit.ly/uniqfun
This proposal describes a variation of std::function supporting non-copyable target objects. It removes the copy constructor, and adds in-place construction of target objects.
All comments welcome. Doesn’t look too scary, I just might prototype it.
On the proposal, I think the idea of a `unique_any` could be valuable. I'm not sure if a non-moveable `unique_any` could be useful by someone, but it's possible.
Also, consider making unique_function(unique_function&&) noexcept.
On 19 May 2015 at 12:42, Nicol Bolas <jmck...@gmail.com> wrote:
On the proposal, I think the idea of a `unique_any` could be valuable. I'm not sure if a non-moveable `unique_any` could be useful by someone, but it's possible.Can't you always make unique_any moveable, given that it can allocate in the heap?
Also, consider making unique_function(unique_function&&) noexcept.
Well, the reason it's not noexcept is that `unique_function` is allowed to store non-moveable types. And since the type is erased (unlike, say, non-moveable std::vector), the only way to signal an error when you attempt to move it is to throw an exception.
That being said, I don't think the benefits of being able to store immobile functions outweight the benefits of having noexcept move constructor/assignment.
Well, the reason it's not noexcept is that `unique_function` is allowed to store non-moveable types. And since the type is erased (unlike, say, non-moveable std::vector), the only way to signal an error when you attempt to move it is to throw an exception.
On Tue, May 19, 2015 at 12:40 PM, Nicol Bolas <jmck...@gmail.com> wrote:Well, the reason it's not noexcept is that `unique_function` is allowed to store non-moveable types. And since the type is erased (unlike, say, non-moveable std::vector), the only way to signal an error when you attempt to move it is to throw an exception.
That being said, I don't think the benefits of being able to store immobile functions outweight the benefits of having noexcept move constructor/assignment.Or just don't require invocation of the move constructor since you can, instead, copy the pointer to it if it's in remote storage (and you just force remote storage if the operation isn't noexcept, even if it fits in the small object storage). I don't buy the "what if the move constructor has side effects so we must call it" standpoint, since the language already allows things like copy/move elision for return value and argument passing.
On 2015–05–20, at 1:42 AM, Nicol Bolas <jmck...@gmail.com> wrote:On Tuesday, May 19, 2015 at 12:07:03 PM UTC-4, David Krauss wrote:Here’s a first draft of a non-copyable polymorphic call wrapper proposal: http://bit.ly/uniqfun
It'd probably be a good idea to let someone know if a link goes to Dropbox to download a PDF, or if it's just a HTML page on the web. You know, without having to click the link.
Well, that would help. At least it would let us know the kind of work that would be involved in doing a move-transfer from `function` to `unique_function`. The concern there being the changes to `function`'s implementation that would need to be made.
But I don't like the idea of considering `any`s to be at all equivalent to or transformable between `function`s. While their implementations are very similar, the objects are conceptually quite distinct. Just like `vector` and `string`. As such, I don't think we should explicitly support transforming one into the other.
I see you have `allocate_assign` and `emplace_assign`. It seems to me that you should follow the existing std::function convention. `allocate_assign` is conceptually equivalent to `function::assign`, so you should probably just call it that.
Also, I see no reason why it shouldn't also be able to work like `function::assign`.
Similarly, `emplace_assign` should simply be called `emplace`. These sound more like the standard
I'm going to assume that you intended to add an `operator()` overload in there somewhere. ;) However, unlike Casey Carter, I would not suggest attempting to fix the thread safety problem until the committee has decided how they're going to fix it in `std::function`. We don't want two completely different fixes involved.
That's not to say that something shouldn't be done. It's more to say that this proposal should hold off on committing to any one solution until more is known.
std::function has some rough edges. Its interface is a product of evolution, and not necessarily the expression of a consistent theory. A model is presented to describe what std::function already safely does. Evolutionary, not-immediately-breaking solutions to all the problems presented in N4159 are proposed, plus further extensions.
Specifically, this proposal includes:• Specializations over cv-qualified and ref-qualified signatures• Multiple signatures in a single specialization• Interface unification with std::any from the Library Fundamentals TS• A functor adapter template critical_section for adding thread safety
So, std::unique_function< void() &, void() const &, void() && > would be a handle to a non-copyable function object which discriminates the major access styles.
Lastly, bikeshedding. I'm not sure that `unique` is the right word. Oh, it conjures up thoughts of `unique_ptr`, which makes you think "move only". But `unique_ptr` is about ownership of memory, while `unique_function` is just about being a move-only function wrapper. At the same time, I cannot come up with a more explicit name that doesn't sound silly (like `move_only_function`).
On 2015–05–20, at 1:49 AM, Nevin Liber <ne...@eviloverlord.com> wrote:Can't you always make unique_any moveable, given that it can allocate in the heap?
Also, consider making unique_function(unique_function&&) noexcept.
On 2015–05–20, at 1:49 AM, Nevin Liber <ne...@eviloverlord.com> wrote:Can't you always make unique_any moveable, given that it can allocate in the heap?unique_any is not proposed here, only mentioned in “future directions.”
Also, consider making unique_function(unique_function&&) noexcept.The noexcept qualification of unique_function is the same as for function. Both allow exceptions to propagate from the move constructor when the small function optimization is applied. It’s a rare corner case, but there’s no static information to make any guarantee.
Also, another corner case: the move constructor will allocate memory when the source has a non-POCMA (allocator_traits::propagate_on_container_move_assignment) allocator. I’m not really seeing an exception-safety guarantee being feasible.
On 2015–05–20, at 9:40 AM, Nevin Liber <ne...@eviloverlord.com> wrote:
The noexcept qualification of unique_function is the same as for function. Both allow exceptions to propagate from the move constructor when the small function optimization is applied. It’s a rare corner case, but there’s no static information to make any guarantee.Fine. Should you present this in Kona, I'll bring this up in LEWG. As we like to say, until you present a proposal, it's your time to waste.
On 2015–05–20, at 10:18 AM, Nevin Liber <ne...@eviloverlord.com> wrote:std::vector and std::basic_string have noexcept(true) move constructors. Are you saying they can't use propagate_on_container_move_assignment allocators?
On 2015–05–20, at 10:27 AM, David Krauss <pot...@mac.com> wrote:So far, I think noexcept-movable std::function deserves its own paper.
On Tue, May 19, 2015 at 10:26 AM, Casey Carter <cart...@gmail.com> wrote:
On Tuesday, May 19, 2015 at 11:07:03 AM UTC-5, David Krauss wrote:Here’s a first draft of a non-copyable polymorphic call wrapper proposal: http://bit.ly/uniqfununique_function would probably be more useful if it had operator(). Joking aside, there should probably be three overloads qualified by &, const&, and && respectively. That way you both avoid std::function's threadsafety problems (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4348.html) and enable unique_function to usefully hold "one-shot" functions.Please no, as this would require type erasing all of those. I've been working on a similar effort and my personal thoughts are that the qualification should be specified as a part of the function type, with exactly one signature being erased (and that signature being reflected at the top-level as well):unique_function<void() const> // for operator() constunique_function<void() &&> // for operator() &&
On 2015–06–25, at 3:03 PM, Arthur O'Dwyer <arthur....@gmail.com> wrote:This is exactly N4159 "std::function and beyond", correct?
N4159 strikes me as exactly the reasonable and sensible thing to do (both to fix the thread-safety/const-correctness issue, and to enable std::function-izing of move-only lambdas); can anyone shed some light on its current disposition?
David Krauss, as the author of N4543 (std::unique_function<R(A...)>), do you think it's in any way preferable to N4159 (std::function<R(A...)&&>), as in,- is there a good reason to reject N4159?- if N4159 were adopted, would there still be a good reason to adopt N4543?
P.S.: I just ran into this issue while attempting to write a Scheduler that manipulates Tasks, where a Task might be for example a lambda one of whose captures is a non-copyable UniquePromise. My solution was to implement Task from scratch; but in a perfect world Task would have been a typedef for std::function<void()&&>.
On 2015–06–25, at 3:03 PM, Arthur O'Dwyer <arthur....@gmail.com> wrote:
David Krauss, as the author of N4543 (std::unique_function<R(A...)>), do you think it's in any way preferable to N4159 (std::function<R(A...)&&>), as in,