The reason that this cannot be done
const std::array<decltype([](){ return 0;}), 3> x = {
[](){return 1;},
[](){return 2;},
[](){return 3;}
};
is that each lambda has its own unique type?
Of course this can be done with an array of std::function.
Daniel.
The reason I'm posting it here is to analyze whether such code should make sense or not. At least from a performance POV, that example I think would be faster than the array of std::function.
>
> Daniel.
const std::array<int(*)(), 3> x = {
[](){return 1;},
[](){return 2;},
[](){return 3;}
};
On 2015–06–24, at 11:03 PM, dgutson . <daniel...@gmail.com> wrote:
The reason that this cannot be done
is that each lambda has its own unique type?
Of course this can be done with an array of std::function.
std::function is horrible. It allocates dynamically, causing it to be useless in many cases that would otherwise need it.
On 2015–06–24, at 11:03 PM, dgutson . <daniel...@gmail.com> wrote:The reason that this cannot be done
is that each lambda has its own unique type?
Correct.Of course this can be done with an array of std::function.
Since those lambdas are all captureless, it can also be done with an array of void(*)().
If you do have captures, but you’re interested in something lighter than std::function, ideally you could pass it a non-allocating allocator. Then it’s basically a function pointer plus space for three word-size captures. (More or less… but usually three.)
Given that most std::function implementations implement the small object optimization, and a captureless lambda is about as small an object as you can have, can you elaborate on why you think dynamic allocation is an in the case (other than the size of the array might be a little bigger)?
(Note: it could be if the held object had a throwing move constructor and the particular implementation of std::function had a noexcept move constructor, but captureless lambdas don't have throwing move constructors),
On 2015–06–25, at 2:00 AM, Shachar Shemesh <sha...@lingnu.com> wrote:Can you elaborate how you reached this number? Or is this just your estimation of the average?(More or less… but usually three.)
On 2015–06–25, at 7:26 AM, Thiago Macieira <thi...@macieira.org> wrote:Qualification required: variables in the same scope that are captured by
reference.
If those variables are in different scopes, then the it needs to capture the
different scopes.
On 2015–06–25, at 8:22 AM, Richard Smith <ric...@metafoo.co.uk> wrote:The Itanium C++ ABI does not do so (though it should, in the cases where the layout is visible across translation units).
The problem is that it's extremely difficult to use a pointer to the stack frame or similar. For instance, it depends on a number of factors that are not visible until some stage in code generation, and must be decided during parsing / semantic analysis because it affects observable properties of the closure type, such as its size.
It would also force not just the layout of the closure type, but the stack frame layout of the enclosing function, to be part of the ABI if it applied to lambdas in inline functions or function templates, which is a completely unreasonable thing for an ABI to require. It's also not clear that it's unambiguously a good thing in the remaining cases: it puts some pretty stringent constraints on the stack layout of the enclosing function, and very likely makes it uninlineable.
On 2015–06–25, at 8:22 AM, Richard Smith <ric...@metafoo.co.uk> wrote:The Itanium C++ ABI does not do so (though it should, in the cases where the layout is visible across translation units).… and this implies that the lambda layout should not be sensitive to stack frame layout, because the inlined function body in TU #1 may call the un-inlined copy from TU #2 if it tries to recurse.The problem is that it's extremely difficult to use a pointer to the stack frame or similar. For instance, it depends on a number of factors that are not visible until some stage in code generation, and must be decided during parsing / semantic analysis because it affects observable properties of the closure type, such as its size.Huh? The point is to decouple the size of the closure from its reference captures.
But, yeah, copies of an unrolled loop (for example) could have different stack layouts.It would also force not just the layout of the closure type, but the stack frame layout of the enclosing function, to be part of the ABI if it applied to lambdas in inline functions or function templates, which is a completely unreasonable thing for an ABI to require. It's also not clear that it's unambiguously a good thing in the remaining cases: it puts some pretty stringent constraints on the stack layout of the enclosing function, and very likely makes it uninlineable.I see. New idea, then: implement a local, invisible struct in the scope of the outermost capture. Populate it with the reference captures, adding assignment operations as targeted variables’ lifetimes begin. The lambda points to the invisible struct. In the common case, the assignments can be hoisted out of loops or such, all back out to the scope with the struct.The indirection should only add one memory access per lambda call, and locality is good so it should never have much negative performance impact. The performance benefit with std::function on the other hand is significant.
On Wednesday 24 June 2015 21:00:57 Shachar Shemesh wrote:For a lambda where the capture is composed only of variables passed by reference, one pointer ought to have been enough.Qualification required: variables in the same scope that are captured by reference. If those variables are in different scopes, then the it needs to capture the different scopes.
It would also force not just the layout of the closure type, but the stack frame layout of the enclosing function
On 25/06/15 03:22, Richard Smith wrote:
It would also force not just the layout of the closure type, but the stack frame layout of the enclosing functionThat's true whether you concentrate the size of the lambda to a single (whatever) pointer or not.
If you want to employ loop unrolling and duplicate a variable, it doesn't matter if you are storing a single pointer to the frame or a pointer to the variable in question. Either way, you cannot duplicate that variable if there is an outside reference to it.
Also, bear in mind that a lambda's capture cannot reference variables not yet defined in the function inside which it is defined. As such, I doubt your observation makes a difference in this particular case.
I might be missing something. If that's the case, please provide an example to illustrate your point.
Shachar
--
---
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-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
It's easy to create a counter-example. Just make the lambda leave the scope it
is created in:
auto create_lambda(int &i, int &j)
{
return [&]{ ++i; ++j; return i + j; }
}
The above clearly requires two word-sized references to be stored in the
lambda.
On Wed, Jun 24, 2015 at 10:02 PM, Shachar Shemesh <sha...@lingnu.com> wrote:
On 25/06/15 03:22, Richard Smith wrote:
It would also force not just the layout of the closure type, but the stack frame layout of the enclosing functionThat's true whether you concentrate the size of the lambda to a single (whatever) pointer or not.
Read the next part of the sentence that you snipped:
"to be part of the ABI". If the closure type only holds pointers to locals, the lambda call operator doesn't need to know anything about the frame layout of the enclosing function. If it just holds a pointer to the enclosing stack frame, it needs to know where within that frame the local variables live.