[cfe-dev] libcxx: std::function should be able to capture blocks
Jared Grubb
jared.grubb at gmail.com
Thu Oct 9 00:11:54 PDT 2014
> On Oct 3, 2014, at 14:50, David Blaikie <dblaikie at gmail.com> wrote:
> On Fri, Oct 3, 2014 at 12:55 PM, Jared Grubb <jared.grubb at gmail.com <mailto:jared.grubb at gmail.com>> wrote:
> I spoke with Marshall briefly about this at CppCon. My suggestion is that std::function should either:
> * correctly capture blocks and just work (as this patch does)
> * compile with error if you try it (and also maybe add a convenience adaptor like you suggest to allow it to manually work)
>
> The current problem is that if you write something like this:
> template <typename F> doSomething(F&& functor) { std::function<...> capturing = std::forward<F>(functor); ... }
> This works great for lambdas. For blocks, it compiles just fine but has runtime issues.
>
> What runtime issues are you referring to? Could you provide a simple standalone complete example?
Here's an example.
int main(int argc, const char**) {
std::function<void()> f;
std::vector<int> vec = {1, 2, 3};
if (argc == 1) {
f = ^{ std::cout << vec.size() << " (block)\n"; };
} else {
f = [=]{ std::cout << vec.size() << " (lambda)\n"; };
}
f();
}
On my machine, it doesnt crash, but the block part is definitely broken:
$ ./a.out
0 (block)
$ ./a.out 1
3 (lambda)
Other examples, especially multithreaded ones, will crash and behave in undefined ways.
> I assume the issue is that std::function will capture the block's (presumably raw) pointer by value but do none of the increment/decrement.
Yes.
> This doesn't seem necessarily broken & I'm not sure (for myself) it merits library support to protect.
>
> This would be the same behavior as any other lifetime issues with pointer captures.
>
> std::function<...> func() {
> int i;
> int *j = &i;
> return [=j] () { ... }; // captured j by value, but it points to something that goes out of scope at the end of the function. Bad.
> }
>
> But similar code isn't a problem at all:
>
> void func(std::function<...>); // func calls the std::function, maybe copies it around, but doesn't copy it to global storage/anywhere that outlives the
> ...
> int i;
> int *j = &i;
> func([=j]() { ... });
You're right that it's the same "bad pointer to stack" issue, but the problem is at a different level. In the example above, the closures themselves are perfectly fine. The problem is that std::function captures the closure incorrectly. That's why I think it's a library issue.
Jared
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20141009/4e967727/attachment.html>
More information about the cfe-dev
mailing list