[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