Discussion:
[std-proposals] The need for std::unique_function
Ryan McDougall
2018-10-22 18:18:46 UTC
Permalink
“This is motivated by increasing usage of things like executors and the task

queue where it is useful to embed move-only types like a std::promise within

a type erased function. That isn't possible without this version of a type

erased function.” -- Chandler Carruth[1]
Thanks

“std::function and Beyond“ N4159
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4159.pdf>,
“Qualified std::function Signatures” P0045R1
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0045r1.pdf>, and
“A polymorphic wrapper for all Callable objects (rev. 3)” P0288R1
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0288r1.pdf> et
al. for laying the groundwork for this paper. Thank you to David Krauss and
Arthur O’Dwyer for your discussion and feedback. Thank you all authors of
alternative type erased callable containers for your proof and inspiration.
Motivation

std::function models both CopyConstructible and CopyAssignable, and
requires its erased target type to declare a copy constructor. This means
std::function is hopelessly incompatible with std::unique_ptr,
std::promise, or any other move-only type. This is a functional gap felt by
C++ users to the degree that there’s some half-dozen popular and
high-quality implementations available under the name “unique_function”.
[2][3][4][5][6]

C++ has many move-only vocabulary types, and when introduced they impose
tighter constraints on the interface, and can become “viral” -- causing any
previously copyable paths to require move or forwarding operations. Consider

class DbResult {

private:

std::unique_ptr<Blob> data_; // now required!

};

class Reactor {

private:

std::map<std::string, std::function<void()>> reactions_;

};

reactor.on_event(“db-result-ready”, [&] {

reactor.on_event(“has-towel”, [result = std::move(db.result())] {

auto meaning = find(result); // 42

});

});

It is not enough to simply std::move the DbResult into the lambda, as
Reactor::on_event is unable to assign to a move-only lambda as implemented
with std::function.

This is a recurring pattern in much concurrent code, such as work queues,
task graphs, or command buffers. The committee implicitly understood the
need when it created std::packaged_task, also a type-erased polymorphic
container, but that type is tightly bound to std::futures, which may not be
fit for purpose in any code base that doesn’t already rely on them.

If we are developing any kind of asynchronous work queue we need

1.

inheritance based polymorphism (and a mechanism to manage heap allocated
derived objects, such as std::shared_ptr)
2.

type-erased container with small object optimization like std::function
(for copy-only callable types), std::packaged_task+std::future (for
move-only callable types)


However if any facet of our runtime precludes use of std::future -- such as
if it provides its own future type, or does not use futures at all, we are
again left without the ability to use std::unique_ptr or any other
non-copyable type.

auto unique =

std::make_unique<BankTransfer>(“DrEvil”, 1000000000);

auto do_bank_transfer =

[transfer = std::move(unique)] (Bank* to, Bank* from) {

return from->send(transfer, to);

};

ThreadSafeQueue<std::function<int()>> transactions1;

transactions1.emplace(do_bank_transfer); // Compile Error!!

// ...

ThreadSafeQueue<std::packaged_task<int()>> transactions2;

ThreadSafeQueue<int> results

transactions2.emplace(do_bank_transfer);

hpy::async([&] {

while (!transactions2.empty()) {

transactions2.top()();

results.push_back(transactions2.top().get_future()); // ??

}

});

In the above example we simply present wasted human time and computational
time due to an unnecessary synchronization with std::future, however a more
complex system may indeed need their own future implementation which
std::packaged_task cannot interoperate with at all.
Comparison Table

std::promise<Foo> p;

std::unique_ptr<Foo> f;

std::queue<std::function<void()>> a;

std::queue<std::unique_function<void()>> b;

using namespace std;


Before Proposal

After Proposal

1

auto s = make_shared<Foo>(move(f));
a.emplace([s] { s->work(); });


// ...

auto shared = a.top();

shared();

b.emplace([u = move(f)] {

u->work();

});

// ...

auto unique = move(b.top());

unique();

2

a.emplace([r = f.get()] {

r->work();

});

b.emplace([u = move(f)] {

u->work();

});

3

atomic<Foo*> result{nullptr};

a.emplace([&result] {

result = new Foo;

});
// ...
spin_on(result);
result->work();

auto future = p.get_future();

b.emplace([p = move(p)] {

p.set_value(Foo{});

});

// ...

future.get().work();

As you can see, attempts to work around the limitation of std::function
results in unacceptable undermining of uniqueness semantics, life-time
safety, and/or ease of use:

1.

Loss of move-only semantics and extra LoC and a heap allocation per
shared pointer.
2.

Queue lifetime must not exceed enclosing scope lifetime (or dangling
pointer).
3.

Poor usability or unnecessary complexity in search of better performance.

Alternatives

Papers “std::function and Beyond“ N4159
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4159.pdf>, “A
polymorphic wrapper for all Callable objects (rev. 3)” P0288R1
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0288r1.pdf> have
already argued for fixing std::function to support non-copyable types
(among other issues) some time ago. Yet it seems self evident that as long
and as widely as std::function has been in use, any change that breaks the
observable surface area of std::function is a non-starter. The question is
would the use case we outlined herein, if overlaid onto the existing
std::function, cause previously valid code to break, or conversely
previously incorrect code to become easily written?

Let’s assume we have a means of “fixing” std::function to allow mixing of
copy and move semantics. Clearly all existing code would continue to
function as all existing target callable types are CopyConstructible and
CopyAssignable, but what if we mix erased instances with copy and move
semantics? How should the following operations on std::function be defined
in terms of their target callable types?

Let’s temporarily ignore the details of memory management, and consider p
to be the underlying pointer to the erased instance.

std2::function<void()> c = C{}; // Copy-only
std2::function<void()> m = M{}; // Move-only


Operation

Definition

Result

c = std::move(m);

*((M*)c.p) = move(*((M*)m.p));

Move

m = std::move(c);

*((C*)c.p) = move(*((C*)m.p));

Copy

c = m;

*((M*)c.p) = *((M*)m.p);

Throw ???

m = c;

*((C*)c.p) = *((C*)m.p);

Copy

Again we face an unacceptable undermining of uniqueness semantics; in
addition we have changed the observable surface area of assignment by
necessitating a new runtime error reporting mechanism for when erased
targets conflicting behavior, affecting the exception-safety of existing
code.
Shallow v. Deep Const

“Shallow” or “Deep” const in a type erased context means whether the
constness of the erasing type is extended towards the erased type. For our
discussion we consider whether the the erasing container is const callable
if and only if the underlying type is const callable.


It is by now understood that the standard requires const correctness to
imply thread-safety, and if the container operator() is const, the
underlying callable type’s operator() must also be const in order to hope
of satisfying this obligation. So a shallow const container could not admit
a thread-safe call operation in general, and both N4159
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4159.pdf> and
P0045R1
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0045r1.pdf> draw
attention to the unfortunate outcome. The solution presented in those
papers it to include the constness of the callable in signature, and to
have the constness of container operator() be conditional on the signature.

struct Accessor {

void operator()() const;

// ...

} access;

struct Mutator {

void operator()();

// ...

} mutate;

std3::function<void() const> a = access; // ok

std3::function<void()> b = access; // ok

std3::function<void()> c = mutate; // ok

std3::function<void() const> d = mutate; // compile error

a = b; // compile error: target type more restrictive

b = a; // ok

b = c; // ok


This proposal, while in the author’s opinion is highly recommended
improvement, it’s best presented in referenced papers, and for simplicity’s
sake isn’t pursued further here.

Otherwise, without the above but desiring deep const semantics, we would
wish that const containers require const callable targets. However, since
we have erase the constness of target instance, we are left with throwing
when deep const is violated, breaking existing exception-safe code.

const std4::function<void()> a; // deep const

std::function<void()> b = mutate; // non-const target

a = b; // target copied

a(); // now throws!
Necessity and Cost

We can all agree type erased containers should support as much as feasible
the const correctness of its target types, but we began our argument with a
specific asynchronous use case that involved deferred computations, often
invoked on foreign threads. If this pattern as seen “in the wild” makes use
of thread safe queues, is thread safety of the container itself actually
sufficient to justify the costs associated with extra synchronization or
exception safety? Even if we can guarantee const container only calls const
target methods, we still cannot guarantee the target itself makes the
connection between const and thread safety.

Should a proposed std::unique_function emulate “broken” shallow const
correctness of std::function for uniformity, or is “fixing” the
contradiction worth breaking runtime changes? And is std::unique_/function
more like a container or pointer (where const is understood to be shallow),
or is it more like an opaque type (where const is generally considered to
be deep)? Historically we allow container const to vary independently of
parameterized types when they exist, suggesting std::function should remain
a shallow container.

While this question is relevant to the specification of
std::unique_function, it is ultimately orthogonal to the question of the
need for std::unique_function to exist, which is this paper’s main concern.
Conclusion

So long as we have move-only lambdas and type erased callable containers, a
move-only capable std::function is required. As uniqueness semantics of
container are orthogonal to const-correctness of the interface we recommend
not conflating the two, and pursuing P0045R1
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0045r1.pdf> for
const-correctness as an independent feature request.
References

[1] https://reviews.llvm.org/D48349

[2] http://llvm.org/doxygen/FunctionExtras_8h_source.html#l00046

[3]
https://github.com/STEllAR-GROUP/hpx/blob/master/hpx/util/unique_function.hpp

[4]
https://github.com/potswa/cxx_function/blob/master/cxx_function.hpp#L1192

[5]
https://github.com/Naios/function2/blob/master/include/function2/function2.hpp#L1406

[6] https://github.com/facebook/folly/blob/master/folly/Function.h
--
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-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAHn%2BA5NyaUQjAeudtObjn08Trz4iJ%2Bu-GGt2J5VnmtmZJiPVxg%40mail.gmail.com.
Lee Howes
2018-10-23 16:14:34 UTC
Permalink
Hi Ryan,
Are you planning to attend the meeting and argue the case for
unique_function? This is something that came up again this week for us at
Facebook as well, and while we work around it with folly::Function as you
noted, having to do so is not optimal. It would certainly be a good thing
to get a unique_function into the standard soonish.

Lee Howes
Post by Ryan McDougall
“This is motivated by increasing usage of things like executors and the task
queue where it is useful to embed move-only types like a std::promise within
a type erased function. That isn't possible without this version of a type
erased function.” -- Chandler Carruth[1]
Thanks
“std::function and Beyond“ N4159
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4159.pdf>,
“Qualified std::function Signatures” P0045R1
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0045r1.pdf>,
and “A polymorphic wrapper for all Callable objects (rev. 3)” P0288R1
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0288r1.pdf> et
al. for laying the groundwork for this paper. Thank you to David Krauss and
Arthur O’Dwyer for your discussion and feedback. Thank you all authors of
alternative type erased callable containers for your proof and inspiration.
Motivation
std::function models both CopyConstructible and CopyAssignable, and
requires its erased target type to declare a copy constructor. This means
std::function is hopelessly incompatible with std::unique_ptr,
std::promise, or any other move-only type. This is a functional gap felt by
C++ users to the degree that there’s some half-dozen popular and
high-quality implementations available under the name “unique_function”.
[2][3][4][5][6]
C++ has many move-only vocabulary types, and when introduced they impose
tighter constraints on the interface, and can become “viral” -- causing any
previously copyable paths to require move or forwarding operations. Consider
class DbResult {
std::unique_ptr<Blob> data_; // now required!
};
class Reactor {
std::map<std::string, std::function<void()>> reactions_;
};
reactor.on_event(“db-result-ready”, [&] {
reactor.on_event(“has-towel”, [result = std::move(db.result())] {
auto meaning = find(result); // 42
});
});
It is not enough to simply std::move the DbResult into the lambda, as
Reactor::on_event is unable to assign to a move-only lambda as implemented
with std::function.
This is a recurring pattern in much concurrent code, such as work queues,
task graphs, or command buffers. The committee implicitly understood the
need when it created std::packaged_task, also a type-erased polymorphic
container, but that type is tightly bound to std::futures, which may not be
fit for purpose in any code base that doesn’t already rely on them.
If we are developing any kind of asynchronous work queue we need
1.
inheritance based polymorphism (and a mechanism to manage heap
allocated derived objects, such as std::shared_ptr)
2.
type-erased container with small object optimization like
std::function (for copy-only callable types),
std::packaged_task+std::future (for move-only callable types)
However if any facet of our runtime precludes use of std::future -- such
as if it provides its own future type, or does not use futures at all, we
are again left without the ability to use std::unique_ptr or any other
non-copyable type.
auto unique =
std::make_unique<BankTransfer>(“DrEvil”, 1000000000);
auto do_bank_transfer =
[transfer = std::move(unique)] (Bank* to, Bank* from) {
return from->send(transfer, to);
};
ThreadSafeQueue<std::function<int()>> transactions1;
transactions1.emplace(do_bank_transfer); // Compile Error!!
// ...
ThreadSafeQueue<std::packaged_task<int()>> transactions2;
ThreadSafeQueue<int> results
transactions2.emplace(do_bank_transfer);
hpy::async([&] {
while (!transactions2.empty()) {
transactions2.top()();
results.push_back(transactions2.top().get_future()); // ??
}
});
In the above example we simply present wasted human time and computational
time due to an unnecessary synchronization with std::future, however a more
complex system may indeed need their own future implementation which
std::packaged_task cannot interoperate with at all.
Comparison Table
std::promise<Foo> p;
std::unique_ptr<Foo> f;
std::queue<std::function<void()>> a;
std::queue<std::unique_function<void()>> b;
using namespace std;
Before Proposal
After Proposal
1
auto s = make_shared<Foo>(move(f));
a.emplace([s] { s->work(); });
// ...
auto shared = a.top();
shared();
b.emplace([u = move(f)] {
u->work();
});
// ...
auto unique = move(b.top());
unique();
2
a.emplace([r = f.get()] {
r->work();
});
b.emplace([u = move(f)] {
u->work();
});
3
atomic<Foo*> result{nullptr};
a.emplace([&result] {
result = new Foo;
});
// ...
spin_on(result);
result->work();
auto future = p.get_future();
b.emplace([p = move(p)] {
p.set_value(Foo{});
});
// ...
future.get().work();
As you can see, attempts to work around the limitation of std::function
results in unacceptable undermining of uniqueness semantics, life-time
1.
Loss of move-only semantics and extra LoC and a heap allocation per
shared pointer.
2.
Queue lifetime must not exceed enclosing scope lifetime (or dangling
pointer).
3.
Poor usability or unnecessary complexity in search of better performance.
Alternatives
Papers “std::function and Beyond“ N4159
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4159.pdf>, “A
polymorphic wrapper for all Callable objects (rev. 3)” P0288R1
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0288r1.pdf>
have already argued for fixing std::function to support non-copyable types
(among other issues) some time ago. Yet it seems self evident that as long
and as widely as std::function has been in use, any change that breaks the
observable surface area of std::function is a non-starter. The question is
would the use case we outlined herein, if overlaid onto the existing
std::function, cause previously valid code to break, or conversely
previously incorrect code to become easily written?
Let’s assume we have a means of “fixing” std::function to allow mixing of
copy and move semantics. Clearly all existing code would continue to
function as all existing target callable types are CopyConstructible and
CopyAssignable, but what if we mix erased instances with copy and move
semantics? How should the following operations on std::function be defined
in terms of their target callable types?
Let’s temporarily ignore the details of memory management, and consider p
to be the underlying pointer to the erased instance.
std2::function<void()> c = C{}; // Copy-only
std2::function<void()> m = M{}; // Move-only
Operation
Definition
Result
c = std::move(m);
*((M*)c.p) = move(*((M*)m.p));
Move
m = std::move(c);
*((C*)c.p) = move(*((C*)m.p));
Copy
c = m;
*((M*)c.p) = *((M*)m.p);
Throw ???
m = c;
*((C*)c.p) = *((C*)m.p);
Copy
Again we face an unacceptable undermining of uniqueness semantics; in
addition we have changed the observable surface area of assignment by
necessitating a new runtime error reporting mechanism for when erased
targets conflicting behavior, affecting the exception-safety of existing
code.
Shallow v. Deep Const
“Shallow” or “Deep” const in a type erased context means whether the
constness of the erasing type is extended towards the erased type. For our
discussion we consider whether the the erasing container is const callable
if and only if the underlying type is const callable.
It is by now understood that the standard requires const correctness to
imply thread-safety, and if the container operator() is const, the
underlying callable type’s operator() must also be const in order to hope
of satisfying this obligation. So a shallow const container could not admit
a thread-safe call operation in general, and both N4159
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4159.pdf> and
P0045R1
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0045r1.pdf>
draw attention to the unfortunate outcome. The solution presented in those
papers it to include the constness of the callable in signature, and to
have the constness of container operator() be conditional on the signature.
struct Accessor {
void operator()() const;
// ...
} access;
struct Mutator {
void operator()();
// ...
} mutate;
std3::function<void() const> a = access; // ok
std3::function<void()> b = access; // ok
std3::function<void()> c = mutate; // ok
std3::function<void() const> d = mutate; // compile error
a = b; // compile error: target type more restrictive
b = a; // ok
b = c; // ok
This proposal, while in the author’s opinion is highly recommended
improvement, it’s best presented in referenced papers, and for simplicity’s
sake isn’t pursued further here.
Otherwise, without the above but desiring deep const semantics, we would
wish that const containers require const callable targets. However, since
we have erase the constness of target instance, we are left with throwing
when deep const is violated, breaking existing exception-safe code.
const std4::function<void()> a; // deep const
std::function<void()> b = mutate; // non-const target
a = b; // target copied
a(); // now throws!
Necessity and Cost
We can all agree type erased containers should support as much as feasible
the const correctness of its target types, but we began our argument with a
specific asynchronous use case that involved deferred computations, often
invoked on foreign threads. If this pattern as seen “in the wild” makes use
of thread safe queues, is thread safety of the container itself actually
sufficient to justify the costs associated with extra synchronization or
exception safety? Even if we can guarantee const container only calls const
target methods, we still cannot guarantee the target itself makes the
connection between const and thread safety.
Should a proposed std::unique_function emulate “broken” shallow const
correctness of std::function for uniformity, or is “fixing” the
contradiction worth breaking runtime changes? And is std::unique_/function
more like a container or pointer (where const is understood to be shallow),
or is it more like an opaque type (where const is generally considered to
be deep)? Historically we allow container const to vary independently of
parameterized types when they exist, suggesting std::function should remain
a shallow container.
While this question is relevant to the specification of
std::unique_function, it is ultimately orthogonal to the question of the
need for std::unique_function to exist, which is this paper’s main concern.
Conclusion
So long as we have move-only lambdas and type erased callable containers,
a move-only capable std::function is required. As uniqueness semantics of
container are orthogonal to const-correctness of the interface we recommend
not conflating the two, and pursuing P0045R1
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0045r1.pdf> for
const-correctness as an independent feature request.
References
[1] https://reviews.llvm.org/D48349
[2] http://llvm.org/doxygen/FunctionExtras_8h_source.html#l00046
[3]
https://github.com/STEllAR-GROUP/hpx/blob/master/hpx/util/unique_function.hpp
[4]
https://github.com/potswa/cxx_function/blob/master/cxx_function.hpp#L1192
[5]
https://github.com/Naios/function2/blob/master/include/function2/function2.hpp#L1406
[6] https://github.com/facebook/folly/blob/master/folly/Function.h
--
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
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAHn%2BA5NyaUQjAeudtObjn08Trz4iJ%2Bu-GGt2J5VnmtmZJiPVxg%40mail.gmail.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAHn%2BA5NyaUQjAeudtObjn08Trz4iJ%2Bu-GGt2J5VnmtmZJiPVxg%40mail.gmail.com?utm_medium=email&utm_source=footer>
.
--
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-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAJH_FNW1PVw2BeYJCWfKNKN3_67eLY_6wG1vgNJOZx9oQS%2Bs%3DQ%40mail.gmail.com.
Ryan McDougall
2018-10-23 16:17:45 UTC
Permalink
Yes, I'll be at SD in order to push this paper -- thought I don't know if
there's room on anyone schedule. I have hope however since it's relatively
uncontroversial it can yet be fast tracked. I would very much like help
with wording if you're available.

Cheers,
Post by Lee Howes
Hi Ryan,
Are you planning to attend the meeting and argue the case for
unique_function? This is something that came up again this week for us at
Facebook as well, and while we work around it with folly::Function as you
noted, having to do so is not optimal. It would certainly be a good thing
to get a unique_function into the standard soonish.
Lee Howes
Post by Ryan McDougall
“This is motivated by increasing usage of things like executors and the task
queue where it is useful to embed move-only types like a std::promise within
a type erased function. That isn't possible without this version of a type
erased function.” -- Chandler Carruth[1]
Thanks
“std::function and Beyond“ N4159
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4159.pdf>,
“Qualified std::function Signatures” P0045R1
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0045r1.pdf>,
and “A polymorphic wrapper for all Callable objects (rev. 3)” P0288R1
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0288r1.pdf> et
al. for laying the groundwork for this paper. Thank you to David Krauss and
Arthur O’Dwyer for your discussion and feedback. Thank you all authors of
alternative type erased callable containers for your proof and inspiration.
Motivation
std::function models both CopyConstructible and CopyAssignable, and
requires its erased target type to declare a copy constructor. This means
std::function is hopelessly incompatible with std::unique_ptr,
std::promise, or any other move-only type. This is a functional gap felt by
C++ users to the degree that there’s some half-dozen popular and
high-quality implementations available under the name “unique_function”.
[2][3][4][5][6]
C++ has many move-only vocabulary types, and when introduced they impose
tighter constraints on the interface, and can become “viral” -- causing any
previously copyable paths to require move or forwarding operations. Consider
class DbResult {
std::unique_ptr<Blob> data_; // now required!
};
class Reactor {
std::map<std::string, std::function<void()>> reactions_;
};
reactor.on_event(“db-result-ready”, [&] {
reactor.on_event(“has-towel”, [result = std::move(db.result())] {
auto meaning = find(result); // 42
});
});
It is not enough to simply std::move the DbResult into the lambda, as
Reactor::on_event is unable to assign to a move-only lambda as implemented
with std::function.
This is a recurring pattern in much concurrent code, such as work queues,
task graphs, or command buffers. The committee implicitly understood the
need when it created std::packaged_task, also a type-erased polymorphic
container, but that type is tightly bound to std::futures, which may not be
fit for purpose in any code base that doesn’t already rely on them.
If we are developing any kind of asynchronous work queue we need
1.
inheritance based polymorphism (and a mechanism to manage heap
allocated derived objects, such as std::shared_ptr)
2.
type-erased container with small object optimization like
std::function (for copy-only callable types),
std::packaged_task+std::future (for move-only callable types)
However if any facet of our runtime precludes use of std::future -- such
as if it provides its own future type, or does not use futures at all, we
are again left without the ability to use std::unique_ptr or any other
non-copyable type.
auto unique =
std::make_unique<BankTransfer>(“DrEvil”, 1000000000);
auto do_bank_transfer =
[transfer = std::move(unique)] (Bank* to, Bank* from) {
return from->send(transfer, to);
};
ThreadSafeQueue<std::function<int()>> transactions1;
transactions1.emplace(do_bank_transfer); // Compile Error!!
// ...
ThreadSafeQueue<std::packaged_task<int()>> transactions2;
ThreadSafeQueue<int> results
transactions2.emplace(do_bank_transfer);
hpy::async([&] {
while (!transactions2.empty()) {
transactions2.top()();
results.push_back(transactions2.top().get_future()); // ??
}
});
In the above example we simply present wasted human time and
computational time due to an unnecessary synchronization with std::future,
however a more complex system may indeed need their own future
implementation which std::packaged_task cannot interoperate with at all.
Comparison Table
std::promise<Foo> p;
std::unique_ptr<Foo> f;
std::queue<std::function<void()>> a;
std::queue<std::unique_function<void()>> b;
using namespace std;
Before Proposal
After Proposal
1
auto s = make_shared<Foo>(move(f));
a.emplace([s] { s->work(); });
// ...
auto shared = a.top();
shared();
b.emplace([u = move(f)] {
u->work();
});
// ...
auto unique = move(b.top());
unique();
2
a.emplace([r = f.get()] {
r->work();
});
b.emplace([u = move(f)] {
u->work();
});
3
atomic<Foo*> result{nullptr};
a.emplace([&result] {
result = new Foo;
});
// ...
spin_on(result);
result->work();
auto future = p.get_future();
b.emplace([p = move(p)] {
p.set_value(Foo{});
});
// ...
future.get().work();
As you can see, attempts to work around the limitation of std::function
results in unacceptable undermining of uniqueness semantics, life-time
1.
Loss of move-only semantics and extra LoC and a heap allocation per
shared pointer.
2.
Queue lifetime must not exceed enclosing scope lifetime (or dangling
pointer).
3.
Poor usability or unnecessary complexity in search of better performance.
Alternatives
Papers “std::function and Beyond“ N4159
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4159.pdf>, “A
polymorphic wrapper for all Callable objects (rev. 3)” P0288R1
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0288r1.pdf>
have already argued for fixing std::function to support non-copyable types
(among other issues) some time ago. Yet it seems self evident that as long
and as widely as std::function has been in use, any change that breaks the
observable surface area of std::function is a non-starter. The question is
would the use case we outlined herein, if overlaid onto the existing
std::function, cause previously valid code to break, or conversely
previously incorrect code to become easily written?
Let’s assume we have a means of “fixing” std::function to allow mixing of
copy and move semantics. Clearly all existing code would continue to
function as all existing target callable types are CopyConstructible and
CopyAssignable, but what if we mix erased instances with copy and move
semantics? How should the following operations on std::function be defined
in terms of their target callable types?
Let’s temporarily ignore the details of memory management, and consider p
to be the underlying pointer to the erased instance.
std2::function<void()> c = C{}; // Copy-only
std2::function<void()> m = M{}; // Move-only
Operation
Definition
Result
c = std::move(m);
*((M*)c.p) = move(*((M*)m.p));
Move
m = std::move(c);
*((C*)c.p) = move(*((C*)m.p));
Copy
c = m;
*((M*)c.p) = *((M*)m.p);
Throw ???
m = c;
*((C*)c.p) = *((C*)m.p);
Copy
Again we face an unacceptable undermining of uniqueness semantics; in
addition we have changed the observable surface area of assignment by
necessitating a new runtime error reporting mechanism for when erased
targets conflicting behavior, affecting the exception-safety of existing
code.
Shallow v. Deep Const
“Shallow” or “Deep” const in a type erased context means whether the
constness of the erasing type is extended towards the erased type. For our
discussion we consider whether the the erasing container is const callable
if and only if the underlying type is const callable.
It is by now understood that the standard requires const correctness to
imply thread-safety, and if the container operator() is const, the
underlying callable type’s operator() must also be const in order to hope
of satisfying this obligation. So a shallow const container could not admit
a thread-safe call operation in general, and both N4159
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4159.pdf> and
P0045R1
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0045r1.pdf>
draw attention to the unfortunate outcome. The solution presented in those
papers it to include the constness of the callable in signature, and to
have the constness of container operator() be conditional on the signature.
struct Accessor {
void operator()() const;
// ...
} access;
struct Mutator {
void operator()();
// ...
} mutate;
std3::function<void() const> a = access; // ok
std3::function<void()> b = access; // ok
std3::function<void()> c = mutate; // ok
std3::function<void() const> d = mutate; // compile error
a = b; // compile error: target type more restrictive
b = a; // ok
b = c; // ok
This proposal, while in the author’s opinion is highly recommended
improvement, it’s best presented in referenced papers, and for simplicity’s
sake isn’t pursued further here.
Otherwise, without the above but desiring deep const semantics, we would
wish that const containers require const callable targets. However, since
we have erase the constness of target instance, we are left with throwing
when deep const is violated, breaking existing exception-safe code.
const std4::function<void()> a; // deep const
std::function<void()> b = mutate; // non-const target
a = b; // target copied
a(); // now throws!
Necessity and Cost
We can all agree type erased containers should support as much as
feasible the const correctness of its target types, but we began our
argument with a specific asynchronous use case that involved deferred
computations, often invoked on foreign threads. If this pattern as seen “in
the wild” makes use of thread safe queues, is thread safety of the
container itself actually sufficient to justify the costs associated with
extra synchronization or exception safety? Even if we can guarantee const
container only calls const target methods, we still cannot guarantee the
target itself makes the connection between const and thread safety.
Should a proposed std::unique_function emulate “broken” shallow const
correctness of std::function for uniformity, or is “fixing” the
contradiction worth breaking runtime changes? And is std::unique_/function
more like a container or pointer (where const is understood to be shallow),
or is it more like an opaque type (where const is generally considered to
be deep)? Historically we allow container const to vary independently of
parameterized types when they exist, suggesting std::function should remain
a shallow container.
While this question is relevant to the specification of
std::unique_function, it is ultimately orthogonal to the question of the
need for std::unique_function to exist, which is this paper’s main concern.
Conclusion
So long as we have move-only lambdas and type erased callable containers,
a move-only capable std::function is required. As uniqueness semantics of
container are orthogonal to const-correctness of the interface we recommend
not conflating the two, and pursuing P0045R1
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0045r1.pdf>
for const-correctness as an independent feature request.
References
[1] https://reviews.llvm.org/D48349
[2] http://llvm.org/doxygen/FunctionExtras_8h_source.html#l00046
[3]
https://github.com/STEllAR-GROUP/hpx/blob/master/hpx/util/unique_function.hpp
[4]
https://github.com/potswa/cxx_function/blob/master/cxx_function.hpp#L1192
[5]
https://github.com/Naios/function2/blob/master/include/function2/function2.hpp#L1406
[6] https://github.com/facebook/folly/blob/master/folly/Function.h
--
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
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAHn%2BA5NyaUQjAeudtObjn08Trz4iJ%2Bu-GGt2J5VnmtmZJiPVxg%40mail.gmail.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAHn%2BA5NyaUQjAeudtObjn08Trz4iJ%2Bu-GGt2J5VnmtmZJiPVxg%40mail.gmail.com?utm_medium=email&utm_source=footer>
.
--
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
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAJH_FNW1PVw2BeYJCWfKNKN3_67eLY_6wG1vgNJOZx9oQS%2Bs%3DQ%40mail.gmail.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAJH_FNW1PVw2BeYJCWfKNKN3_67eLY_6wG1vgNJOZx9oQS%2Bs%3DQ%40mail.gmail.com?utm_medium=email&utm_source=footer>
.
--
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-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAHn%2BA5NHiEO1rTKfZOryWnW-NTqLGgim6epp-Bt03iU5wd-EEA%40mail.gmail.com.
Lee Howes
2018-10-23 16:26:05 UTC
Permalink
From the minutes from the presentation of P0288 in Toronto it looks like
there was a request for updated wording to make it independent of P0045,
but otherwise little obvious concern. It seems likely that that's the
important change to make.

Given the paper reading load between now and SD we're going to be
overwhelmed, I think. I'd be happy to give some feedback on a wording draft
in the short term.
Post by Ryan McDougall
Yes, I'll be at SD in order to push this paper -- thought I don't know if
there's room on anyone schedule. I have hope however since it's relatively
uncontroversial it can yet be fast tracked. I would very much like help
with wording if you're available.
Cheers,
Post by Lee Howes
Hi Ryan,
Are you planning to attend the meeting and argue the case for
unique_function? This is something that came up again this week for us at
Facebook as well, and while we work around it with folly::Function as you
noted, having to do so is not optimal. It would certainly be a good thing
to get a unique_function into the standard soonish.
Lee Howes
Post by Ryan McDougall
“This is motivated by increasing usage of things like executors and the task
queue where it is useful to embed move-only types like a std::promise within
a type erased function. That isn't possible without this version of a type
erased function.” -- Chandler Carruth[1]
Thanks
“std::function and Beyond“ N4159
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4159.pdf>,
“Qualified std::function Signatures” P0045R1
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0045r1.pdf>,
and “A polymorphic wrapper for all Callable objects (rev. 3)” P0288R1
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0288r1.pdf>
et al. for laying the groundwork for this paper. Thank you to David Krauss
and Arthur O’Dwyer for your discussion and feedback. Thank you all authors
of alternative type erased callable containers for your proof and
inspiration.
Motivation
std::function models both CopyConstructible and CopyAssignable, and
requires its erased target type to declare a copy constructor. This means
std::function is hopelessly incompatible with std::unique_ptr,
std::promise, or any other move-only type. This is a functional gap felt by
C++ users to the degree that there’s some half-dozen popular and
high-quality implementations available under the name “unique_function”.
[2][3][4][5][6]
C++ has many move-only vocabulary types, and when introduced they impose
tighter constraints on the interface, and can become “viral” -- causing any
previously copyable paths to require move or forwarding operations. Consider
class DbResult {
std::unique_ptr<Blob> data_; // now required!
};
class Reactor {
std::map<std::string, std::function<void()>> reactions_;
};
reactor.on_event(“db-result-ready”, [&] {
reactor.on_event(“has-towel”, [result = std::move(db.result())] {
auto meaning = find(result); // 42
});
});
It is not enough to simply std::move the DbResult into the lambda, as
Reactor::on_event is unable to assign to a move-only lambda as implemented
with std::function.
This is a recurring pattern in much concurrent code, such as work
queues, task graphs, or command buffers. The committee implicitly
understood the need when it created std::packaged_task, also a type-erased
polymorphic container, but that type is tightly bound to std::futures,
which may not be fit for purpose in any code base that doesn’t already rely
on them.
If we are developing any kind of asynchronous work queue we need
1.
inheritance based polymorphism (and a mechanism to manage heap
allocated derived objects, such as std::shared_ptr)
2.
type-erased container with small object optimization like
std::function (for copy-only callable types),
std::packaged_task+std::future (for move-only callable types)
However if any facet of our runtime precludes use of std::future -- such
as if it provides its own future type, or does not use futures at all, we
are again left without the ability to use std::unique_ptr or any other
non-copyable type.
auto unique =
std::make_unique<BankTransfer>(“DrEvil”, 1000000000);
auto do_bank_transfer =
[transfer = std::move(unique)] (Bank* to, Bank* from) {
return from->send(transfer, to);
};
ThreadSafeQueue<std::function<int()>> transactions1;
transactions1.emplace(do_bank_transfer); // Compile Error!!
// ...
ThreadSafeQueue<std::packaged_task<int()>> transactions2;
ThreadSafeQueue<int> results
transactions2.emplace(do_bank_transfer);
hpy::async([&] {
while (!transactions2.empty()) {
transactions2.top()();
results.push_back(transactions2.top().get_future()); // ??
}
});
In the above example we simply present wasted human time and
computational time due to an unnecessary synchronization with std::future,
however a more complex system may indeed need their own future
implementation which std::packaged_task cannot interoperate with at all.
Comparison Table
std::promise<Foo> p;
std::unique_ptr<Foo> f;
std::queue<std::function<void()>> a;
std::queue<std::unique_function<void()>> b;
using namespace std;
Before Proposal
After Proposal
1
auto s = make_shared<Foo>(move(f));
a.emplace([s] { s->work(); });
// ...
auto shared = a.top();
shared();
b.emplace([u = move(f)] {
u->work();
});
// ...
auto unique = move(b.top());
unique();
2
a.emplace([r = f.get()] {
r->work();
});
b.emplace([u = move(f)] {
u->work();
});
3
atomic<Foo*> result{nullptr};
a.emplace([&result] {
result = new Foo;
});
// ...
spin_on(result);
result->work();
auto future = p.get_future();
b.emplace([p = move(p)] {
p.set_value(Foo{});
});
// ...
future.get().work();
As you can see, attempts to work around the limitation of std::function
results in unacceptable undermining of uniqueness semantics, life-time
1.
Loss of move-only semantics and extra LoC and a heap allocation per
shared pointer.
2.
Queue lifetime must not exceed enclosing scope lifetime (or dangling
pointer).
3.
Poor usability or unnecessary complexity in search of better performance.
Alternatives
Papers “std::function and Beyond“ N4159
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4159.pdf>, “A
polymorphic wrapper for all Callable objects (rev. 3)” P0288R1
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0288r1.pdf>
have already argued for fixing std::function to support non-copyable types
(among other issues) some time ago. Yet it seems self evident that as long
and as widely as std::function has been in use, any change that breaks the
observable surface area of std::function is a non-starter. The question is
would the use case we outlined herein, if overlaid onto the existing
std::function, cause previously valid code to break, or conversely
previously incorrect code to become easily written?
Let’s assume we have a means of “fixing” std::function to allow mixing
of copy and move semantics. Clearly all existing code would continue to
function as all existing target callable types are CopyConstructible and
CopyAssignable, but what if we mix erased instances with copy and move
semantics? How should the following operations on std::function be defined
in terms of their target callable types?
Let’s temporarily ignore the details of memory management, and consider
p to be the underlying pointer to the erased instance.
std2::function<void()> c = C{}; // Copy-only
std2::function<void()> m = M{}; // Move-only
Operation
Definition
Result
c = std::move(m);
*((M*)c.p) = move(*((M*)m.p));
Move
m = std::move(c);
*((C*)c.p) = move(*((C*)m.p));
Copy
c = m;
*((M*)c.p) = *((M*)m.p);
Throw ???
m = c;
*((C*)c.p) = *((C*)m.p);
Copy
Again we face an unacceptable undermining of uniqueness semantics; in
addition we have changed the observable surface area of assignment by
necessitating a new runtime error reporting mechanism for when erased
targets conflicting behavior, affecting the exception-safety of existing
code.
Shallow v. Deep Const
“Shallow” or “Deep” const in a type erased context means whether the
constness of the erasing type is extended towards the erased type. For our
discussion we consider whether the the erasing container is const callable
if and only if the underlying type is const callable.
It is by now understood that the standard requires const correctness to
imply thread-safety, and if the container operator() is const, the
underlying callable type’s operator() must also be const in order to
hope of satisfying this obligation. So a shallow const container could
not admit a thread-safe call operation in general, and both N4159
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4159.pdf> and
P0045R1
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0045r1.pdf>
draw attention to the unfortunate outcome. The solution presented in those
papers it to include the constness of the callable in signature, and to
have the constness of container operator() be conditional on the signature.
struct Accessor {
void operator()() const;
// ...
} access;
struct Mutator {
void operator()();
// ...
} mutate;
std3::function<void() const> a = access; // ok
std3::function<void()> b = access; // ok
std3::function<void()> c = mutate; // ok
std3::function<void() const> d = mutate; // compile error
a = b; // compile error: target type more restrictive
b = a; // ok
b = c; // ok
This proposal, while in the author’s opinion is highly recommended
improvement, it’s best presented in referenced papers, and for simplicity’s
sake isn’t pursued further here.
Otherwise, without the above but desiring deep const semantics, we would
wish that const containers require const callable targets. However, since
we have erase the constness of target instance, we are left with throwing
when deep const is violated, breaking existing exception-safe code.
const std4::function<void()> a; // deep const
std::function<void()> b = mutate; // non-const target
a = b; // target copied
a(); // now throws!
Necessity and Cost
We can all agree type erased containers should support as much as
feasible the const correctness of its target types, but we began our
argument with a specific asynchronous use case that involved deferred
computations, often invoked on foreign threads. If this pattern as seen “in
the wild” makes use of thread safe queues, is thread safety of the
container itself actually sufficient to justify the costs associated with
extra synchronization or exception safety? Even if we can guarantee const
container only calls const target methods, we still cannot guarantee the
target itself makes the connection between const and thread safety.
Should a proposed std::unique_function emulate “broken” shallow const
correctness of std::function for uniformity, or is “fixing” the
contradiction worth breaking runtime changes? And is std::unique_/function
more like a container or pointer (where const is understood to be shallow),
or is it more like an opaque type (where const is generally considered to
be deep)? Historically we allow container const to vary independently of
parameterized types when they exist, suggesting std::function should remain
a shallow container.
While this question is relevant to the specification of
std::unique_function, it is ultimately orthogonal to the question of
the need for std::unique_function to exist, which is this paper’s main
concern.
Conclusion
So long as we have move-only lambdas and type erased callable
containers, a move-only capable std::function is required. As uniqueness
semantics of container are orthogonal to const-correctness of the interface
we recommend not conflating the two, and pursuing P0045R1
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0045r1.pdf>
for const-correctness as an independent feature request.
References
[1] https://reviews.llvm.org/D48349
[2] http://llvm.org/doxygen/FunctionExtras_8h_source.html#l00046
[3]
https://github.com/STEllAR-GROUP/hpx/blob/master/hpx/util/unique_function.hpp
[4]
https://github.com/potswa/cxx_function/blob/master/cxx_function.hpp#L1192
[5]
https://github.com/Naios/function2/blob/master/include/function2/function2.hpp#L1406
[6] https://github.com/facebook/folly/blob/master/folly/Function.h
--
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
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAHn%2BA5NyaUQjAeudtObjn08Trz4iJ%2Bu-GGt2J5VnmtmZJiPVxg%40mail.gmail.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAHn%2BA5NyaUQjAeudtObjn08Trz4iJ%2Bu-GGt2J5VnmtmZJiPVxg%40mail.gmail.com?utm_medium=email&utm_source=footer>
.
--
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
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAJH_FNW1PVw2BeYJCWfKNKN3_67eLY_6wG1vgNJOZx9oQS%2Bs%3DQ%40mail.gmail.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAJH_FNW1PVw2BeYJCWfKNKN3_67eLY_6wG1vgNJOZx9oQS%2Bs%3DQ%40mail.gmail.com?utm_medium=email&utm_source=footer>
.
--
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
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAHn%2BA5NHiEO1rTKfZOryWnW-NTqLGgim6epp-Bt03iU5wd-EEA%40mail.gmail.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAHn%2BA5NHiEO1rTKfZOryWnW-NTqLGgim6epp-Bt03iU5wd-EEA%40mail.gmail.com?utm_medium=email&utm_source=footer>
.
--
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-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAJH_FNWs-rP4hc6V3pE1vTgQgtTUKfNsJOmOzsxWvwRzsC2ywA%40mail.gmail.com.
Ryan McDougall
2018-10-23 16:32:04 UTC
Permalink
I reference P0045, but my paper is independent. Essentially "we need this,
it's kind of obvious, let's decide if it's like std::function or "fixed" by
P0045".

If you or your team will be at SD we can bang out some wording in an hour I
feel.
Post by Lee Howes
From the minutes from the presentation of P0288 in Toronto it looks like
there was a request for updated wording to make it independent of P0045,
but otherwise little obvious concern. It seems likely that that's the
important change to make.
Given the paper reading load between now and SD we're going to be
overwhelmed, I think. I'd be happy to give some feedback on a wording draft
in the short term.
Post by Ryan McDougall
Yes, I'll be at SD in order to push this paper -- thought I don't know if
there's room on anyone schedule. I have hope however since it's relatively
uncontroversial it can yet be fast tracked. I would very much like help
with wording if you're available.
Cheers,
Post by Lee Howes
Hi Ryan,
Are you planning to attend the meeting and argue the case for
unique_function? This is something that came up again this week for us at
Facebook as well, and while we work around it with folly::Function as you
noted, having to do so is not optimal. It would certainly be a good thing
to get a unique_function into the standard soonish.
Lee Howes
Post by Ryan McDougall
“This is motivated by increasing usage of things like executors and the task
queue where it is useful to embed move-only types like a std::promise within
a type erased function. That isn't possible without this version of a type
erased function.” -- Chandler Carruth[1]
Thanks
“std::function and Beyond“ N4159
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4159.pdf>,
“Qualified std::function Signatures” P0045R1
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0045r1.pdf>,
and “A polymorphic wrapper for all Callable objects (rev. 3)” P0288R1
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0288r1.pdf>
et al. for laying the groundwork for this paper. Thank you to David Krauss
and Arthur O’Dwyer for your discussion and feedback. Thank you all authors
of alternative type erased callable containers for your proof and
inspiration.
Motivation
std::function models both CopyConstructible and CopyAssignable, and
requires its erased target type to declare a copy constructor. This means
std::function is hopelessly incompatible with std::unique_ptr,
std::promise, or any other move-only type. This is a functional gap felt by
C++ users to the degree that there’s some half-dozen popular and
high-quality implementations available under the name “unique_function”.
[2][3][4][5][6]
C++ has many move-only vocabulary types, and when introduced they
impose tighter constraints on the interface, and can become “viral” --
causing any previously copyable paths to require move or forwarding
operations. Consider
class DbResult {
std::unique_ptr<Blob> data_; // now required!
};
class Reactor {
std::map<std::string, std::function<void()>> reactions_;
};
reactor.on_event(“db-result-ready”, [&] {
reactor.on_event(“has-towel”, [result = std::move(db.result())] {
auto meaning = find(result); // 42
});
});
It is not enough to simply std::move the DbResult into the lambda, as
Reactor::on_event is unable to assign to a move-only lambda as implemented
with std::function.
This is a recurring pattern in much concurrent code, such as work
queues, task graphs, or command buffers. The committee implicitly
understood the need when it created std::packaged_task, also a type-erased
polymorphic container, but that type is tightly bound to std::futures,
which may not be fit for purpose in any code base that doesn’t already rely
on them.
If we are developing any kind of asynchronous work queue we need
1.
inheritance based polymorphism (and a mechanism to manage heap
allocated derived objects, such as std::shared_ptr)
2.
type-erased container with small object optimization like
std::function (for copy-only callable types),
std::packaged_task+std::future (for move-only callable types)
However if any facet of our runtime precludes use of std::future --
such as if it provides its own future type, or does not use futures at all,
we are again left without the ability to use std::unique_ptr or any
other non-copyable type.
auto unique =
std::make_unique<BankTransfer>(“DrEvil”, 1000000000);
auto do_bank_transfer =
[transfer = std::move(unique)] (Bank* to, Bank* from) {
return from->send(transfer, to);
};
ThreadSafeQueue<std::function<int()>> transactions1;
transactions1.emplace(do_bank_transfer); // Compile Error!!
// ...
ThreadSafeQueue<std::packaged_task<int()>> transactions2;
ThreadSafeQueue<int> results
transactions2.emplace(do_bank_transfer);
hpy::async([&] {
while (!transactions2.empty()) {
transactions2.top()();
results.push_back(transactions2.top().get_future()); // ??
}
});
In the above example we simply present wasted human time and
computational time due to an unnecessary synchronization with std::future,
however a more complex system may indeed need their own future
implementation which std::packaged_task cannot interoperate with at all.
Comparison Table
std::promise<Foo> p;
std::unique_ptr<Foo> f;
std::queue<std::function<void()>> a;
std::queue<std::unique_function<void()>> b;
using namespace std;
Before Proposal
After Proposal
1
auto s = make_shared<Foo>(move(f));
a.emplace([s] { s->work(); });
// ...
auto shared = a.top();
shared();
b.emplace([u = move(f)] {
u->work();
});
// ...
auto unique = move(b.top());
unique();
2
a.emplace([r = f.get()] {
r->work();
});
b.emplace([u = move(f)] {
u->work();
});
3
atomic<Foo*> result{nullptr};
a.emplace([&result] {
result = new Foo;
});
// ...
spin_on(result);
result->work();
auto future = p.get_future();
b.emplace([p = move(p)] {
p.set_value(Foo{});
});
// ...
future.get().work();
As you can see, attempts to work around the limitation of std::function
results in unacceptable undermining of uniqueness semantics, life-time
1.
Loss of move-only semantics and extra LoC and a heap allocation per
shared pointer.
2.
Queue lifetime must not exceed enclosing scope lifetime (or
dangling pointer).
3.
Poor usability or unnecessary complexity in search of better performance.
Alternatives
Papers “std::function and Beyond“ N4159
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4159.pdf>,
“A polymorphic wrapper for all Callable objects (rev. 3)” P0288R1
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0288r1.pdf>
have already argued for fixing std::function to support non-copyable types
(among other issues) some time ago. Yet it seems self evident that as long
and as widely as std::function has been in use, any change that breaks the
observable surface area of std::function is a non-starter. The question is
would the use case we outlined herein, if overlaid onto the existing
std::function, cause previously valid code to break, or conversely
previously incorrect code to become easily written?
Let’s assume we have a means of “fixing” std::function to allow mixing
of copy and move semantics. Clearly all existing code would continue to
function as all existing target callable types are CopyConstructible and
CopyAssignable, but what if we mix erased instances with copy and move
semantics? How should the following operations on std::function be defined
in terms of their target callable types?
Let’s temporarily ignore the details of memory management, and consider
p to be the underlying pointer to the erased instance.
std2::function<void()> c = C{}; // Copy-only
std2::function<void()> m = M{}; // Move-only
Operation
Definition
Result
c = std::move(m);
*((M*)c.p) = move(*((M*)m.p));
Move
m = std::move(c);
*((C*)c.p) = move(*((C*)m.p));
Copy
c = m;
*((M*)c.p) = *((M*)m.p);
Throw ???
m = c;
*((C*)c.p) = *((C*)m.p);
Copy
Again we face an unacceptable undermining of uniqueness semantics; in
addition we have changed the observable surface area of assignment by
necessitating a new runtime error reporting mechanism for when erased
targets conflicting behavior, affecting the exception-safety of existing
code.
Shallow v. Deep Const
“Shallow” or “Deep” const in a type erased context means whether the
constness of the erasing type is extended towards the erased type. For our
discussion we consider whether the the erasing container is const callable
if and only if the underlying type is const callable.
It is by now understood that the standard requires const correctness to
imply thread-safety, and if the container operator() is const, the
underlying callable type’s operator() must also be const in order to
hope of satisfying this obligation. So a shallow const container could
not admit a thread-safe call operation in general, and both N4159
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4159.pdf>
and P0045R1
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0045r1.pdf>
draw attention to the unfortunate outcome. The solution presented in those
papers it to include the constness of the callable in signature, and to
have the constness of container operator() be conditional on the signature.
struct Accessor {
void operator()() const;
// ...
} access;
struct Mutator {
void operator()();
// ...
} mutate;
std3::function<void() const> a = access; // ok
std3::function<void()> b = access; // ok
std3::function<void()> c = mutate; // ok
std3::function<void() const> d = mutate; // compile error
a = b; // compile error: target type more restrictive
b = a; // ok
b = c; // ok
This proposal, while in the author’s opinion is highly recommended
improvement, it’s best presented in referenced papers, and for simplicity’s
sake isn’t pursued further here.
Otherwise, without the above but desiring deep const semantics, we
would wish that const containers require const callable targets. However,
since we have erase the constness of target instance, we are left with
throwing when deep const is violated, breaking existing exception-safe code.
const std4::function<void()> a; // deep const
std::function<void()> b = mutate; // non-const target
a = b; // target copied
a(); // now throws!
Necessity and Cost
We can all agree type erased containers should support as much as
feasible the const correctness of its target types, but we began our
argument with a specific asynchronous use case that involved deferred
computations, often invoked on foreign threads. If this pattern as seen “in
the wild” makes use of thread safe queues, is thread safety of the
container itself actually sufficient to justify the costs associated with
extra synchronization or exception safety? Even if we can guarantee const
container only calls const target methods, we still cannot guarantee the
target itself makes the connection between const and thread safety.
Should a proposed std::unique_function emulate “broken” shallow const
correctness of std::function for uniformity, or is “fixing” the
contradiction worth breaking runtime changes? And is std::unique_/function
more like a container or pointer (where const is understood to be shallow),
or is it more like an opaque type (where const is generally considered to
be deep)? Historically we allow container const to vary independently of
parameterized types when they exist, suggesting std::function should remain
a shallow container.
While this question is relevant to the specification of
std::unique_function, it is ultimately orthogonal to the question of
the need for std::unique_function to exist, which is this paper’s main
concern.
Conclusion
So long as we have move-only lambdas and type erased callable
containers, a move-only capable std::function is required. As uniqueness
semantics of container are orthogonal to const-correctness of the interface
we recommend not conflating the two, and pursuing P0045R1
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0045r1.pdf>
for const-correctness as an independent feature request.
References
[1] https://reviews.llvm.org/D48349
[2] http://llvm.org/doxygen/FunctionExtras_8h_source.html#l00046
[3]
https://github.com/STEllAR-GROUP/hpx/blob/master/hpx/util/unique_function.hpp
[4]
https://github.com/potswa/cxx_function/blob/master/cxx_function.hpp#L1192
[5]
https://github.com/Naios/function2/blob/master/include/function2/function2.hpp#L1406
[6] https://github.com/facebook/folly/blob/master/folly/Function.h
--
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
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAHn%2BA5NyaUQjAeudtObjn08Trz4iJ%2Bu-GGt2J5VnmtmZJiPVxg%40mail.gmail.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAHn%2BA5NyaUQjAeudtObjn08Trz4iJ%2Bu-GGt2J5VnmtmZJiPVxg%40mail.gmail.com?utm_medium=email&utm_source=footer>
.
--
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
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAJH_FNW1PVw2BeYJCWfKNKN3_67eLY_6wG1vgNJOZx9oQS%2Bs%3DQ%40mail.gmail.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAJH_FNW1PVw2BeYJCWfKNKN3_67eLY_6wG1vgNJOZx9oQS%2Bs%3DQ%40mail.gmail.com?utm_medium=email&utm_source=footer>
.
--
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
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAHn%2BA5NHiEO1rTKfZOryWnW-NTqLGgim6epp-Bt03iU5wd-EEA%40mail.gmail.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAHn%2BA5NHiEO1rTKfZOryWnW-NTqLGgim6epp-Bt03iU5wd-EEA%40mail.gmail.com?utm_medium=email&utm_source=footer>
.
--
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
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAJH_FNWs-rP4hc6V3pE1vTgQgtTUKfNsJOmOzsxWvwRzsC2ywA%40mail.gmail.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAJH_FNWs-rP4hc6V3pE1vTgQgtTUKfNsJOmOzsxWvwRzsC2ywA%40mail.gmail.com?utm_medium=email&utm_source=footer>
.
--
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-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAHn%2BA5PiWBuc33MR6Sr%2BvU4W%3DWtcENvMc4Nf%2BYP-8joBNPnYQw%40mail.gmail.com.
Lee Howes
2018-10-23 16:35:23 UTC
Permalink
A new revision of David Krauss's paper seems like the cleanest approach to
preserve history in discussions, but yes, we can definitely talk in SD.
There will be a few of us there.
--
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-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAJH_FNUBdcsx5tQa6kYLhpPO8aGck_4dG3t18Ytdh%3DfWPHTbsA%40mail.gmail.com.
Ryan McDougall
2018-10-23 16:37:34 UTC
Permalink
My understanding is David doesn't develop C++ anymore. Do you suggest we
re-write/update his paper?
Post by Lee Howes
A new revision of David Krauss's paper seems like the cleanest approach to
preserve history in discussions, but yes, we can definitely talk in SD.
There will be a few of us there.
--
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
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAJH_FNUBdcsx5tQa6kYLhpPO8aGck_4dG3t18Ytdh%3DfWPHTbsA%40mail.gmail.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAJH_FNUBdcsx5tQa6kYLhpPO8aGck_4dG3t18Ytdh%3DfWPHTbsA%40mail.gmail.com?utm_medium=email&utm_source=footer>
.
--
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-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAHn%2BA5Ppq%3D7_oB_do2eLjxe2RP-gWRRwdTYtuz-sZ7p%2BQq-pVw%40mail.gmail.com.
Lee Howes
2018-10-23 16:57:09 UTC
Permalink
Well that was what I was thinking, but it's really up to you. I'm probably
not the right person to advise on LEWG procedure so I could easily be
giving the wrong advice anyway.
--
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-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAJH_FNWHNbz4WzoakMi%2B-drrUKwrMnQKs3QQytBpdL-zgirKtg%40mail.gmail.com.
Brian Budge
2018-10-23 17:35:27 UTC
Permalink
David suggested to reach out to Jeffrey Yasskin on LEWG to loop him into the discussion.

Get Outlook for Android<https://aka.ms/ghei36>

________________________________
From: Lee Howes <***@gmail.com>
Sent: Tuesday, October 23, 2018 9:57:09 AM
To: std-***@isocpp.org
Cc: Brian Budge
Subject: Re: [std-proposals] The need for std::unique_function

Well that was what I was thinking, but it's really up to you. I'm probably not the right person to advise on LEWG procedure so I could easily be giving the wrong advice anyway.
--
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-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/MW2PR1501MB2154FB67DE8B9F102CC016EEA2F50%40MW2PR1501MB2154.namprd15.prod.outlook.com.
'David Krauss' via ISO C++ Standard - Future Proposals
2018-10-23 20:59:38 UTC
Permalink
Hi Ryan, all,

Thanks for your support! I’m not an active user of C++ and I won’t be attending the next conference, but anyone can certainly still ping me.

Feel free to carry over the P0288 number. The main work to align the P0288 draft with your proposal would be rebasing the standardese onto the IS, from the current basis of P0045. Most of this can be done by recovering the text from P0288R0.

The rationale for P0288R1 depending on P0045 was that the shallow-const issue was spreading to this new class as a virus, so the fix in P0045 should be applied first to contain the damage. However, two years have passed with less interest in P0045 (or another deep-const fix; adding deep const does not imply all of P0045). The fixed, dependent version P0288R1 was never presented — several volunteers have come forward but the extra complication has been a hindrance.

We might focus now on building LEWG consensus on the order of dependencies. We’ve missed 2018 and there’s still plenty of time before Kona 2019. If some committee members are serious about deep const as a hard requirement, then they have ample opportunity to support P0045 or to invent another solution. Otherwise, everyone should be able to agree on a standalone unique_function.

- Best,
David
Post by Brian Budge
David suggested to reach out to Jeffrey Yasskin on LEWG to loop him into the discussion.
Get Outlook for Android <https://aka.ms/ghei36>
Sent: Tuesday, October 23, 2018 9:57:09 AM
Cc: Brian Budge
Subject: Re: [std-proposals] The need for std::unique_function
Well that was what I was thinking, but it's really up to you. I'm probably not the right person to advise on LEWG procedure so I could easily be giving the wrong advice anyway.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/MW2PR1501MB2154FB67DE8B9F102CC016EEA2F50%40MW2PR1501MB2154.namprd15.prod.outlook.com <https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/MW2PR1501MB2154FB67DE8B9F102CC016EEA2F50%40MW2PR1501MB2154.namprd15.prod.outlook.com?utm_medium=email&utm_source=footer>.
--
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-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/DF91B617-FD32-486E-A5B4-65AB33809C5B%40me.com.
Bryce Adelstein Lelbach aka wash
2018-10-24 06:59:47 UTC
Permalink
I would be willing to allocate time in LEWGI at San Diego to see this
paper, as it's a frequently requested feature.

This comes with the caveat I'd like one updated proposal for this, intended
for discussion ready for circulation on the LEWG reflector by this Friday.


On Tue, Oct 23, 2018, 1:59 PM 'David Krauss' via ISO C++ Standard - Future
Post by 'David Krauss' via ISO C++ Standard - Future Proposals
Hi Ryan, all,
Thanks for your support! I’m not an active user of C++ and I won’t be
attending the next conference, but anyone can certainly still ping me.
Feel free to carry over the P0288 number. The main work to align the P0288
draft with your proposal would be rebasing the standardese onto the IS,
from the current basis of P0045. Most of this can be done by recovering the
text from P0288R0.
The rationale for P0288R1 depending on P0045 was that the shallow-const
issue was spreading to this new class as a virus, so the fix in P0045
should be applied first to contain the damage. However, two years have
passed with less interest in P0045 (or another deep-const fix; adding deep
const does not imply all of P0045). The fixed, dependent version P0288R1
was never presented — several volunteers have come forward but the extra
complication has been a hindrance.
We might focus now on building LEWG consensus on the order of
dependencies. We’ve missed 2018 and there’s still plenty of time before
Kona 2019. If some committee members are serious about deep const as a hard
requirement, then they have ample opportunity to support P0045 or to invent
another solution. Otherwise, everyone should be able to agree on a
standalone unique_function.
- Best,
David
David suggested to reach out to Jeffrey Yasskin on LEWG to loop him into the discussion.
Get Outlook for Android <https://aka.ms/ghei36>
------------------------------
*Sent:* Tuesday, October 23, 2018 9:57:09 AM
*Cc:* Brian Budge
*Subject:* Re: [std-proposals] The need for std::unique_function
Well that was what I was thinking, but it's really up to you. I'm probably
not the right person to advise on LEWG procedure so I could easily be
giving the wrong advice anyway.
--
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
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/MW2PR1501MB2154FB67DE8B9F102CC016EEA2F50%40MW2PR1501MB2154.namprd15.prod.outlook.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/MW2PR1501MB2154FB67DE8B9F102CC016EEA2F50%40MW2PR1501MB2154.namprd15.prod.outlook.com?utm_medium=email&utm_source=footer>
.
--
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
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/DF91B617-FD32-486E-A5B4-65AB33809C5B%40me.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/DF91B617-FD32-486E-A5B4-65AB33809C5B%40me.com?utm_medium=email&utm_source=footer>
.
--
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-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAP3wax9WPkgz5_E1iS_bJf5HD69Qf17XqYRYxX%3DzXHOfpAcK-g%40mail.gmail.com.
Ryan McDougall
2018-10-24 16:41:11 UTC
Permalink
From my point of view there are 2 issues:
1) Do we need std::unique_function: Yes
2) Do we want it to be move-only version of std::function, or should it be
improved version where
```std::unique_function<void() const> f;``` only admits targets with const
callable operators?

I think there's legitimate debate about latter, but it should not preclude
the former. Should I just present it as an open question?

Lastly standardese. I don't speak it at all (yet). I can simply lift
David's -- is that acceptable?

To recapitulate:
- Rename to P0288
- Fold in P0045 as an option
- Steal P0045 standardese
- Ready by friday

^ This is the request?

On Wed, Oct 24, 2018 at 12:00 AM Bryce Adelstein Lelbach aka wash <
Post by Bryce Adelstein Lelbach aka wash
I would be willing to allocate time in LEWGI at San Diego to see this
paper, as it's a frequently requested feature.
This comes with the caveat I'd like one updated proposal for this,
intended for discussion ready for circulation on the LEWG reflector by this
Friday.
On Tue, Oct 23, 2018, 1:59 PM 'David Krauss' via ISO C++ Standard - Future
Post by 'David Krauss' via ISO C++ Standard - Future Proposals
Hi Ryan, all,
Thanks for your support! I’m not an active user of C++ and I won’t be
attending the next conference, but anyone can certainly still ping me.
Feel free to carry over the P0288 number. The main work to align the
P0288 draft with your proposal would be rebasing the standardese onto the
IS, from the current basis of P0045. Most of this can be done by recovering
the text from P0288R0.
The rationale for P0288R1 depending on P0045 was that the shallow-const
issue was spreading to this new class as a virus, so the fix in P0045
should be applied first to contain the damage. However, two years have
passed with less interest in P0045 (or another deep-const fix; adding deep
const does not imply all of P0045). The fixed, dependent version P0288R1
was never presented — several volunteers have come forward but the extra
complication has been a hindrance.
We might focus now on building LEWG consensus on the order of
dependencies. We’ve missed 2018 and there’s still plenty of time before
Kona 2019. If some committee members are serious about deep const as a hard
requirement, then they have ample opportunity to support P0045 or to invent
another solution. Otherwise, everyone should be able to agree on a
standalone unique_function.
- Best,
David
David suggested to reach out to Jeffrey Yasskin on LEWG to loop him into the discussion.
Get Outlook for Android <https://aka.ms/ghei36>
------------------------------
*Sent:* Tuesday, October 23, 2018 9:57:09 AM
*Cc:* Brian Budge
*Subject:* Re: [std-proposals] The need for std::unique_function
Well that was what I was thinking, but it's really up to you. I'm
probably not the right person to advise on LEWG procedure so I could easily
be giving the wrong advice anyway.
--
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
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/MW2PR1501MB2154FB67DE8B9F102CC016EEA2F50%40MW2PR1501MB2154.namprd15.prod.outlook.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/MW2PR1501MB2154FB67DE8B9F102CC016EEA2F50%40MW2PR1501MB2154.namprd15.prod.outlook.com?utm_medium=email&utm_source=footer>
.
--
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
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/DF91B617-FD32-486E-A5B4-65AB33809C5B%40me.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/DF91B617-FD32-486E-A5B4-65AB33809C5B%40me.com?utm_medium=email&utm_source=footer>
.
--
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-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAHn%2BA5MCw%3D5ebSe8zwTSd35GP03dTpvtGv-X_vS4j1ujmEMyFQ%40mail.gmail.com.
Bryce Adelstein Lelbach aka wash
2018-10-25 09:07:35 UTC
Permalink
Post by Ryan McDougall
1) Do we need std::unique_function: Yes
2) Do we want it to be move-only version of std::function, or should it be
improved version where
```std::unique_function<void() const> f;``` only admits targets with const
callable operators?
I think there's legitimate debate about latter, but it should not preclude
Post by Ryan McDougall
the former. Should I just present it as an open question?
Yes. I have not strong feelings here yet.


Lastly standardese. I don't speak it at all (yet). I can simply lift
Post by Ryan McDougall
David's -- is that acceptable?
- Rename to P0288
- Fold in P0045 as an option
- Steal P0045 standardese
- Ready by friday
^ This is the request?
Yes.

On Wed, Oct 24, 2018 at 12:00 AM Bryce Adelstein Lelbach aka wash <
Post by Ryan McDougall
Post by Bryce Adelstein Lelbach aka wash
I would be willing to allocate time in LEWGI at San Diego to see this
paper, as it's a frequently requested feature.
This comes with the caveat I'd like one updated proposal for this,
intended for discussion ready for circulation on the LEWG reflector by this
Friday.
On Tue, Oct 23, 2018, 1:59 PM 'David Krauss' via ISO C++ Standard -
Post by 'David Krauss' via ISO C++ Standard - Future Proposals
Hi Ryan, all,
Thanks for your support! I’m not an active user of C++ and I won’t be
attending the next conference, but anyone can certainly still ping me.
Feel free to carry over the P0288 number. The main work to align the
P0288 draft with your proposal would be rebasing the standardese onto the
IS, from the current basis of P0045. Most of this can be done by recovering
the text from P0288R0.
The rationale for P0288R1 depending on P0045 was that the shallow-const
issue was spreading to this new class as a virus, so the fix in P0045
should be applied first to contain the damage. However, two years have
passed with less interest in P0045 (or another deep-const fix; adding deep
const does not imply all of P0045). The fixed, dependent version P0288R1
was never presented — several volunteers have come forward but the extra
complication has been a hindrance.
We might focus now on building LEWG consensus on the order of
dependencies. We’ve missed 2018 and there’s still plenty of time before
Kona 2019. If some committee members are serious about deep const as a hard
requirement, then they have ample opportunity to support P0045 or to invent
another solution. Otherwise, everyone should be able to agree on a
standalone unique_function.
- Best,
David
David suggested to reach out to Jeffrey Yasskin on LEWG to loop him into
the discussion.
Get Outlook for Android <https://aka.ms/ghei36>
------------------------------
*Sent:* Tuesday, October 23, 2018 9:57:09 AM
*Cc:* Brian Budge
*Subject:* Re: [std-proposals] The need for std::unique_function
Well that was what I was thinking, but it's really up to you. I'm
probably not the right person to advise on LEWG procedure so I could easily
be giving the wrong advice anyway.
--
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
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/MW2PR1501MB2154FB67DE8B9F102CC016EEA2F50%40MW2PR1501MB2154.namprd15.prod.outlook.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/MW2PR1501MB2154FB67DE8B9F102CC016EEA2F50%40MW2PR1501MB2154.namprd15.prod.outlook.com?utm_medium=email&utm_source=footer>
.
--
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
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/DF91B617-FD32-486E-A5B4-65AB33809C5B%40me.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/DF91B617-FD32-486E-A5B4-65AB33809C5B%40me.com?utm_medium=email&utm_source=footer>
.
--
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-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAP3wax__0hayf2KweVWRusT_xEquEkVCXRxt13tXutsZ0%2Baymw%40mail.gmail.com.
Ryan McDougall
2018-10-25 16:27:09 UTC
Permalink
Ok I still don't have access to the mailing lists and am not sure how long
that takes. I assume you can post in my stead until then?

On Thu, Oct 25, 2018 at 2:07 AM Bryce Adelstein Lelbach aka wash <
Post by Ryan McDougall
Post by Ryan McDougall
1) Do we need std::unique_function: Yes
2) Do we want it to be move-only version of std::function, or should it
be improved version where
```std::unique_function<void() const> f;``` only admits targets with
const callable operators?
I think there's legitimate debate about latter, but it should not preclude
Post by Ryan McDougall
the former. Should I just present it as an open question?
Yes. I have not strong feelings here yet.
Lastly standardese. I don't speak it at all (yet). I can simply lift
Post by Ryan McDougall
David's -- is that acceptable?
- Rename to P0288
- Fold in P0045 as an option
- Steal P0045 standardese
- Ready by friday
^ This is the request?
Yes.
On Wed, Oct 24, 2018 at 12:00 AM Bryce Adelstein Lelbach aka wash <
Post by Ryan McDougall
Post by Bryce Adelstein Lelbach aka wash
I would be willing to allocate time in LEWGI at San Diego to see this
paper, as it's a frequently requested feature.
This comes with the caveat I'd like one updated proposal for this,
intended for discussion ready for circulation on the LEWG reflector by this
Friday.
On Tue, Oct 23, 2018, 1:59 PM 'David Krauss' via ISO C++ Standard -
Post by 'David Krauss' via ISO C++ Standard - Future Proposals
Hi Ryan, all,
Thanks for your support! I’m not an active user of C++ and I won’t be
attending the next conference, but anyone can certainly still ping me.
Feel free to carry over the P0288 number. The main work to align the
P0288 draft with your proposal would be rebasing the standardese onto the
IS, from the current basis of P0045. Most of this can be done by recovering
the text from P0288R0.
The rationale for P0288R1 depending on P0045 was that the shallow-const
issue was spreading to this new class as a virus, so the fix in P0045
should be applied first to contain the damage. However, two years have
passed with less interest in P0045 (or another deep-const fix; adding deep
const does not imply all of P0045). The fixed, dependent version P0288R1
was never presented — several volunteers have come forward but the extra
complication has been a hindrance.
We might focus now on building LEWG consensus on the order of
dependencies. We’ve missed 2018 and there’s still plenty of time before
Kona 2019. If some committee members are serious about deep const as a hard
requirement, then they have ample opportunity to support P0045 or to invent
another solution. Otherwise, everyone should be able to agree on a
standalone unique_function.
- Best,
David
David suggested to reach out to Jeffrey Yasskin on LEWG to loop him
into the discussion.
Get Outlook for Android <https://aka.ms/ghei36>
------------------------------
*Sent:* Tuesday, October 23, 2018 9:57:09 AM
*Cc:* Brian Budge
*Subject:* Re: [std-proposals] The need for std::unique_function
Well that was what I was thinking, but it's really up to you. I'm
probably not the right person to advise on LEWG procedure so I could easily
be giving the wrong advice anyway.
--
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
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/MW2PR1501MB2154FB67DE8B9F102CC016EEA2F50%40MW2PR1501MB2154.namprd15.prod.outlook.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/MW2PR1501MB2154FB67DE8B9F102CC016EEA2F50%40MW2PR1501MB2154.namprd15.prod.outlook.com?utm_medium=email&utm_source=footer>
.
--
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
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/DF91B617-FD32-486E-A5B4-65AB33809C5B%40me.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/DF91B617-FD32-486E-A5B4-65AB33809C5B%40me.com?utm_medium=email&utm_source=footer>
.
--
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
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAP3wax__0hayf2KweVWRusT_xEquEkVCXRxt13tXutsZ0%2Baymw%40mail.gmail.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAP3wax__0hayf2KweVWRusT_xEquEkVCXRxt13tXutsZ0%2Baymw%40mail.gmail.com?utm_medium=email&utm_source=footer>
.
--
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-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAHn%2BA5Nb-cB4%3D%3Ds8aRO978Ndt%3DNDRuJPmSnXKCGmmrhcx8z1GA%40mail.gmail.com.
Ryan McDougall
2018-10-25 17:45:55 UTC
Permalink
Updated version for those following at home
https://docs.google.com/document/d/1fiaaqTubxw-EUeveejcTAubAaMQYQZ19Qt2VmHrbbok/edit?usp=sharing
Post by Ryan McDougall
Ok I still don't have access to the mailing lists and am not sure how long
that takes. I assume you can post in my stead until then?
On Thu, Oct 25, 2018 at 2:07 AM Bryce Adelstein Lelbach aka wash <
Post by Ryan McDougall
Post by Ryan McDougall
1) Do we need std::unique_function: Yes
2) Do we want it to be move-only version of std::function, or should it
be improved version where
```std::unique_function<void() const> f;``` only admits targets with
const callable operators?
I think there's legitimate debate about latter, but it should not
Post by Ryan McDougall
preclude the former. Should I just present it as an open question?
Yes. I have not strong feelings here yet.
Lastly standardese. I don't speak it at all (yet). I can simply lift
Post by Ryan McDougall
David's -- is that acceptable?
- Rename to P0288
- Fold in P0045 as an option
- Steal P0045 standardese
- Ready by friday
^ This is the request?
Yes.
On Wed, Oct 24, 2018 at 12:00 AM Bryce Adelstein Lelbach aka wash <
Post by Ryan McDougall
Post by Bryce Adelstein Lelbach aka wash
I would be willing to allocate time in LEWGI at San Diego to see this
paper, as it's a frequently requested feature.
This comes with the caveat I'd like one updated proposal for this,
intended for discussion ready for circulation on the LEWG reflector by this
Friday.
On Tue, Oct 23, 2018, 1:59 PM 'David Krauss' via ISO C++ Standard -
Post by 'David Krauss' via ISO C++ Standard - Future Proposals
Hi Ryan, all,
Thanks for your support! I’m not an active user of C++ and I won’t be
attending the next conference, but anyone can certainly still ping me.
Feel free to carry over the P0288 number. The main work to align the
P0288 draft with your proposal would be rebasing the standardese onto the
IS, from the current basis of P0045. Most of this can be done by recovering
the text from P0288R0.
The rationale for P0288R1 depending on P0045 was that the
shallow-const issue was spreading to this new class as a virus, so the fix
in P0045 should be applied first to contain the damage. However, two years
have passed with less interest in P0045 (or another deep-const fix; adding
deep const does not imply all of P0045). The fixed, dependent version
P0288R1 was never presented — several volunteers have come forward but the
extra complication has been a hindrance.
We might focus now on building LEWG consensus on the order of
dependencies. We’ve missed 2018 and there’s still plenty of time before
Kona 2019. If some committee members are serious about deep const as a hard
requirement, then they have ample opportunity to support P0045 or to invent
another solution. Otherwise, everyone should be able to agree on a
standalone unique_function.
- Best,
David
David suggested to reach out to Jeffrey Yasskin on LEWG to loop him
into the discussion.
Get Outlook for Android <https://aka.ms/ghei36>
------------------------------
*Sent:* Tuesday, October 23, 2018 9:57:09 AM
*Cc:* Brian Budge
*Subject:* Re: [std-proposals] The need for std::unique_function
Well that was what I was thinking, but it's really up to you. I'm
probably not the right person to advise on LEWG procedure so I could easily
be giving the wrong advice anyway.
--
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
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/MW2PR1501MB2154FB67DE8B9F102CC016EEA2F50%40MW2PR1501MB2154.namprd15.prod.outlook.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/MW2PR1501MB2154FB67DE8B9F102CC016EEA2F50%40MW2PR1501MB2154.namprd15.prod.outlook.com?utm_medium=email&utm_source=footer>
.
--
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
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/DF91B617-FD32-486E-A5B4-65AB33809C5B%40me.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/DF91B617-FD32-486E-A5B4-65AB33809C5B%40me.com?utm_medium=email&utm_source=footer>
.
--
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
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAP3wax__0hayf2KweVWRusT_xEquEkVCXRxt13tXutsZ0%2Baymw%40mail.gmail.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAP3wax__0hayf2KweVWRusT_xEquEkVCXRxt13tXutsZ0%2Baymw%40mail.gmail.com?utm_medium=email&utm_source=footer>
.
--
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-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAHn%2BA5P_Qcjx3RmobVoZes3VsRvXF5g6pYxUKfd1ru70DJq_%3DQ%40mail.gmail.com.
Bryce Adelstein Lelbach aka wash
2018-10-26 08:26:50 UTC
Permalink
Indeed.
Post by Ryan McDougall
Ok I still don't have access to the mailing lists and am not sure how long
that takes. I assume you can post in my stead until then?
On Thu, Oct 25, 2018 at 2:07 AM Bryce Adelstein Lelbach aka wash <
Post by Ryan McDougall
Post by Ryan McDougall
1) Do we need std::unique_function: Yes
2) Do we want it to be move-only version of std::function, or should it
be improved version where
```std::unique_function<void() const> f;``` only admits targets with
const callable operators?
I think there's legitimate debate about latter, but it should not
Post by Ryan McDougall
preclude the former. Should I just present it as an open question?
Yes. I have not strong feelings here yet.
Lastly standardese. I don't speak it at all (yet). I can simply lift
Post by Ryan McDougall
David's -- is that acceptable?
- Rename to P0288
- Fold in P0045 as an option
- Steal P0045 standardese
- Ready by friday
^ This is the request?
Yes.
On Wed, Oct 24, 2018 at 12:00 AM Bryce Adelstein Lelbach aka wash <
Post by Ryan McDougall
Post by Bryce Adelstein Lelbach aka wash
I would be willing to allocate time in LEWGI at San Diego to see this
paper, as it's a frequently requested feature.
This comes with the caveat I'd like one updated proposal for this,
intended for discussion ready for circulation on the LEWG reflector by this
Friday.
On Tue, Oct 23, 2018, 1:59 PM 'David Krauss' via ISO C++ Standard -
Post by 'David Krauss' via ISO C++ Standard - Future Proposals
Hi Ryan, all,
Thanks for your support! I’m not an active user of C++ and I won’t be
attending the next conference, but anyone can certainly still ping me.
Feel free to carry over the P0288 number. The main work to align the
P0288 draft with your proposal would be rebasing the standardese onto the
IS, from the current basis of P0045. Most of this can be done by recovering
the text from P0288R0.
The rationale for P0288R1 depending on P0045 was that the
shallow-const issue was spreading to this new class as a virus, so the fix
in P0045 should be applied first to contain the damage. However, two years
have passed with less interest in P0045 (or another deep-const fix; adding
deep const does not imply all of P0045). The fixed, dependent version
P0288R1 was never presented — several volunteers have come forward but the
extra complication has been a hindrance.
We might focus now on building LEWG consensus on the order of
dependencies. We’ve missed 2018 and there’s still plenty of time before
Kona 2019. If some committee members are serious about deep const as a hard
requirement, then they have ample opportunity to support P0045 or to invent
another solution. Otherwise, everyone should be able to agree on a
standalone unique_function.
- Best,
David
David suggested to reach out to Jeffrey Yasskin on LEWG to loop him
into the discussion.
Get Outlook for Android <https://aka.ms/ghei36>
------------------------------
*Sent:* Tuesday, October 23, 2018 9:57:09 AM
*Cc:* Brian Budge
*Subject:* Re: [std-proposals] The need for std::unique_function
Well that was what I was thinking, but it's really up to you. I'm
probably not the right person to advise on LEWG procedure so I could easily
be giving the wrong advice anyway.
--
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
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/MW2PR1501MB2154FB67DE8B9F102CC016EEA2F50%40MW2PR1501MB2154.namprd15.prod.outlook.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/MW2PR1501MB2154FB67DE8B9F102CC016EEA2F50%40MW2PR1501MB2154.namprd15.prod.outlook.com?utm_medium=email&utm_source=footer>
.
--
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
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/DF91B617-FD32-486E-A5B4-65AB33809C5B%40me.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/DF91B617-FD32-486E-A5B4-65AB33809C5B%40me.com?utm_medium=email&utm_source=footer>
.
--
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
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAP3wax__0hayf2KweVWRusT_xEquEkVCXRxt13tXutsZ0%2Baymw%40mail.gmail.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAP3wax__0hayf2KweVWRusT_xEquEkVCXRxt13tXutsZ0%2Baymw%40mail.gmail.com?utm_medium=email&utm_source=footer>
.
--
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-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAP3wax8gQXTSgtD2%3DOaJ4O8PRLx%2BQxjs-o8%2Br%2BN-hHn1p8dJhQ%40mail.gmail.com.
Loading...