[cfe-dev] libcxx: std::function should be able to capture blocks
David Blaikie
dblaikie at gmail.com
Thu Oct 9 09:47:17 PDT 2014
On Thu, Oct 9, 2014 at 12:11 AM, Jared Grubb <jared.grubb at gmail.com> wrote:
>
> 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>
> 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:
>
Fair enough.
> $ ./a.out
> 0 (block)
> $ ./a.out 1
> 3 (lambda)
>
> Other examples, especially multithreaded ones, will crash and behave in
> undefined ways.
>
Hmm - which kind of multithreading do you have in mind? I'm not sure why
that'd be a particular problem.
>
> 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.
>
Would it be possible/reasonable to warn/error on copying the raw block
pointer? (passing it to any function at all) std::function isn't the only
entity that could cause this kind of dangling block pointer bug & it might
be nicer to fix it in the language for all such cases, than fix one (albeit
common/easily triggered) case in the library?
I assume I could write the same bug with something like:
T *t; // not sure what 'T' is, but I imagine it's writeable?
if (x) {
t = ^{ std::cout << vec.size() << " (block)\n"; };
}
t();
>
> 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/e1ce1dc1/attachment.html>
More information about the cfe-dev
mailing list