--
--
Chromium Developers mailing list: chromi...@chromium.org
View archives, change email options, or unsubscribe:
http://groups.google.com/a/chromium.org/group/chromium-dev
What:Anonymous function objects (lambdas) allow declaring simple inline functions. This is especially useful with generic algorithms.Why:Enable easy usage of generic algorithms, simple callbacks, and callback adapters without having to create stand-alone functor objects or functions.Example:1. Convert an array of strings to lowercase:std::vector<string> lines = GetTextLines();std::for_each(lines.begin(), lines.end(),[](string& s) { base::StringToLowerASCII(&s); });
2. Given an array of integers, move all odd numbers to the front of the array:std::vector<int> numbers{1, 2, 3, 4, 5, 6, 7, 8};std::stable_partition(numbers.begin(), numbers.end(),[](int n) { return (n % 2) != 0; });
Lambdas/functors can be easily plugged into the existing Chrome's Callback/Bind system. I have already created a bind adapter for this in Chrome OS: https://chromium.googlesource.com/chromiumos/platform2/+/master/libchromeos/chromeos/bind_lambda.h
Without std::function or something equivalent like Alex's https://chromium.googlesource.com/chromiumos/platform2/+/master/libchromeos/chromeos/bind_lambda.h, any function taking a lambda has to be a template. But any template taking a C++98 functor should be fine taking a lambda instead.I haven't investigated if lambdas themselves are implemented using any code that needs to be shipped with the standard library, but I'd be a little surprised if they were.
Yes, the lambda captures seem to be handled with base::Bind adapter. Also, lambda without any capture decays to just function pointer, so you can do this:int foo(int(*func)(int, int)) {return func(10, 20);}int main() {printf("a = %d\n", foo([](int x, int y) { return x + y; }));printf("b = %d\n", foo([](int x, int y) { return x * y; }));return 0;}This prints:a = 30b = 200
How about we let the auto thread resolve then enable lambdas?
On Wed, Sep 24, 2014 at 4:59 PM, Alex Vakulenko <avaku...@chromium.org> wrote:Yes, the lambda captures seem to be handled with base::Bind adapter. Also, lambda without any capture decays to just function pointer, so you can do this:int foo(int(*func)(int, int)) {return func(10, 20);}int main() {printf("a = %d\n", foo([](int x, int y) { return x + y; }));printf("b = %d\n", foo([](int x, int y) { return x * y; }));return 0;}This prints:a = 30b = 200OK, this makes sense. I've played with it a bit and the base::Unretained/ConstRef appear to work the same way, for the reasoning you apply below which makes sense.
One thing about lambdas that is not obvious is that while they can be converted to functor-like types (like your base::Bind adapter does) the actual type of the lambda can't be spelled out, so you can't efficiently store a lambda in a variable without using 'auto'. How about we let the auto thread resolve then enable lambdas?- James
--
On Oct 21, 2014 8:09 AM, "David Michael" <dmic...@chromium.org> wrote:
>
> On Thu, Sep 25, 2014 at 12:40 AM, James Robinson <jam...@chromium.org> wrote:
>>
>>
>>
>> On Wed, Sep 24, 2014 at 4:59 PM, Alex Vakulenko <avaku...@chromium.org> wrote:
>>>
>>> Yes, the lambda captures seem to be handled with base::Bind adapter. Also, lambda without any capture decays to just function pointer, so you can do this:
>>>
>>> int foo(int(*func)(int, int)) {
>>> return func(10, 20);
>>> }
>>>
>>> int main() {
>>> printf("a = %d\n", foo([](int x, int y) { return x + y; }));
>>> printf("b = %d\n", foo([](int x, int y) { return x * y; }));
>>> return 0;
>>> }
>>>
>>> This prints:
>>> a = 30
>>> b = 200
>>>
>>
>> OK, this makes sense. I've played with it a bit and the base::Unretained/ConstRef appear to work the same way, for the reasoning you apply below which makes sense.
>
> It makes sense that things like base::Unretained and base::Owned work if you remember them. But that's kind of beside the point of those. By replacing usages of Bind with lambdas and captures, we'd be giving up the protections of base::Bind. Currently base::Bind will actually fail if you try to bind a raw pointer without specifying ownership. I haven't played with lambdas, but I assume they happily capture a raw pointer.
>
> base::Bind also does detection of RefCounted types and ref-counts the target object for member functions by default.
>
> I think if we allow lambdas, it would probably be wise (at least initially) to only allow them to be defined and used within the scope of a function, and not passed in or out of a function. That way, they can be used in things like STL algorithms, but we don't give up the safety net that base::Bind gives us.
>
Isn't it true that lambdas for use with STL algorithms require std::function, a library feature (ergo not universal)?
On Oct 21, 2014 8:09 AM, "David Michael" <dmic...@chromium.org> wrote:
>
> On Thu, Sep 25, 2014 at 12:40 AM, James Robinson <jam...@chromium.org> wrote:
>>
>>
>>
>> On Wed, Sep 24, 2014 at 4:59 PM, Alex Vakulenko <avaku...@chromium.org> wrote:
>>>
>>> Yes, the lambda captures seem to be handled with base::Bind adapter. Also, lambda without any capture decays to just function pointer, so you can do this:
>>>
>>> int foo(int(*func)(int, int)) {
>>> return func(10, 20);
>>> }
>>>
>>> int main() {
>>> printf("a = %d\n", foo([](int x, int y) { return x + y; }));
>>> printf("b = %d\n", foo([](int x, int y) { return x * y; }));
>>> return 0;
>>> }
>>>
>>> This prints:
>>> a = 30
>>> b = 200
>>>
>>
>> OK, this makes sense. I've played with it a bit and the base::Unretained/ConstRef appear to work the same way, for the reasoning you apply below which makes sense.
>
> It makes sense that things like base::Unretained and base::Owned work if you remember them. But that's kind of beside the point of those. By replacing usages of Bind with lambdas and captures, we'd be giving up the protections of base::Bind. Currently base::Bind will actually fail if you try to bind a raw pointer without specifying ownership. I haven't played with lambdas, but I assume they happily capture a raw pointer.
>
> base::Bind also does detection of RefCounted types and ref-counts the target object for member functions by default.
>
> I think if we allow lambdas, it would probably be wise (at least initially) to only allow them to be defined and used within the scope of a function, and not passed in or out of a function. That way, they can be used in things like STL algorithms, but we don't give up the safety net that base::Bind gives us.
>Isn't it true that lambdas for use with STL algorithms require std::function, a library feature (ergo not universal)?
Casually jumping in here to voice my "yes please" for lambdas. They will hugely simplify a lot of my async code and make it much more readable
--
--
Chromium Developers mailing list: chromi...@chromium.org
View archives, change email options, or unsubscribe:
http://groups.google.com/a/chromium.org/group/chromium-dev
To unsubscribe from this group and stop receiving emails from it, send an email to chromium-dev...@chromium.org.
-- BR Maciej Pawlowski Opera Desktop Wroclaw
Hi all,I would like to second that "yes please" voice for lambdas.They, indeed, simplify the code and remove the noise of havingto declare explicit functors for simple operations.As Jeremy has said, no C++11 standard library functionality is neededfor lambda expressions.They are simply the compiler-generated "non-union classes which have const public inline function call operator" which have "an implicitly-declared copy constructor"and each capture becomes a "non-static data member of the resulting closure object".More details are available in "5.1.2 Lambda expressions" of C++ standard.Therefore, they can be passed everywhere where functor with templated type is expected.This includes C++03 stdlib algorithms like std::for_each or std::transform.Additionally, if lambda expression does not capture any state, it can be implicitlyconverted to plain function pointer (5.1.2.6).We could also make it possible to bind lambda expressions to base::Callback<>,to be able to pass them around where base::Callback<> is expected and also,possibly, compensate for the lack of std::function<>.The demonstrative CL showing how it could be achieved:It seems to work on all Chromium compilers.So are there any concerns not to include lambdas in the list of allowed features?
Even with the inconvenience of having to retype all the argument
types, this would still be easier to read and more convenient in many
cases than having to define a completely separate function.
In the long run, we might be able to require that all lambdas use [=]
capture (instead of [&]) and that nothing captured is a raw pointer.
However, for "Owned" captures, we need to be able to capture
std::unique_ptr<>s by move, which won't come until C++14 and MSVC14:
init-captures in
http://blogs.msdn.com/b/vcblog/archive/2014/06/11/c-11-14-feature-tables-for-visual-studio-14-ctp1.aspx.
> If we allow lambdas, I would prefer they were only allowed within the scope
> of a function, and are never passed in or out, to protect against most
> lifetime mistakes. We could see if that is sufficient for most uses and go
> from there.
I'm having trouble seeing why you'd think that there's any chance this
would be sufficient for most uses. Do you commonly create Closures
that you don't pass to any other function?
W dniu 2014-10-30 o 16:11, David Michael pisze:
I'm still concerned about the fact that lambdas will allow us to bind raw pointers without any thoughts to lifetime. As I noted before, base::Callback ensures that you specify base::Owned or base::Unretained, and also ensures that base::RefCounted pointers get ref-counted.
If we allow lambdas, I would prefer they were only allowed within the scope of a function, and are never passed in or out, to protect against most lifetime mistakes. We could see if that is sufficient for most uses and go from there.
Agreed, this is a concern. I think there should be some guidance in the style guide about it, but the feature is too useful in general to abandon it IMO.
Using short lambdas in local scope, for STL algorithms or whatnot, sounds like a no-brainer to me. It's clearer to read and easier to write than having an anonymous namespace at the top of the file and a random predicate (usually with a comment like "for the std::sort in function X").
As for larger scopes, perhaps they could be allowed without the default captures that could bind an unguarded 'this'? So, if one wanted to have a long-lived lambda, he'd be required to capture 'this' explicitly (which would be equivalent to base::Unretained).
Callbacks are still more explicit/safer about it, yeah.
Maybe all captures of 'this' should be wrapped in some little class that seamlessly allows RefCounted classes and weak_ptrs but disallows other bare pointers, unless also passed through base::Unretained? Something like in Callback. Haven't given this much thought.
Also, it's actually kind of rare that I find myself wishing I could use one, and the syntax is abominable. That could change over time.
Whether one finds the syntax of a feature pretty or not, or whether one finds himself personally using it or not, should be irrelevant in terms of allowing or banning it. For example, I wished for lambdas many times when writing find_ifs and the syntax doesn't bother me.
Generally, IMHO the default approach to allowing/banning a C++11 feature should be "Allow unless it's really bad", not "Ban unless proven really good".
We want to be as close to standard C++ as possible within the constraint of the core principles of the style guide, not end up with a small subset of the language because there was once a couple of guys who didn't think they'd be needing feature X. Any rule (banned feature) is an organizational cost required to enforce it (and circumvent it when needed), new rules should be introduced only when not doing so would be even costlier.
--
BR
Maciej Pawlowski
Opera Desktop Wroclaw
I'm not totally against lambda, but if we allow it, let's start with a safer subset of possible usage. We can grow from there once we have experience.
I'm not totally against lambda, but if we allow it, let's start with a safer subset of possible usage. We can grow from there once we have experience.I'm totally fine with this.
MyAsyncFunction(base::Bind([](RefcountedClass* self, Arg1* a1, Arg2*
a2, Arg3 a3) {
// body
}, this, base::Owned(local_a1), base::Unretained(local_a2), local_a3));
If you see anything inside the []s, you'd know it was against the style guide.
Michael, what about the case that Jeffrey stated above?MyAsyncFunction(base::Bind([](RefcountedClass* self, Arg1* a1, Arg2*
a2, Arg3 a3) {
// body
}, this, base::Owned(local_a1), base::Unretained(local_a2), local_a3));
If you see anything inside the []s, you'd know it was against the style guide.Basically, allowing the use of lambdas in the "Bind" architecture, but with no context capturing. This will wrap the lambda in the safely of the callback which provides all of our memory management additions. This is where I see myself using lambdas the most.
"this" is not available in the lambda unless you capture it (I believe)
Also, I just noticed that the provided example used a (presumably) refcounted type, so I guess the generic 'refcountable arguments must be bound as a scoped_refptr' logic should save you here. But there are also many instances where base::Bind() is used with member functions on non-refcounted classes, and using the lambda form would default to the dangerous behavior for those instances.
Because base::Bind() is not dealing with a member function callback (a lambda is not a member function), none of the usual checks that base::Bind() applies to |this| would be used.
The ref counting heuristic is the only one applied to all arguments. You can bind a raw pointer to a non-refcounted class as non-this argument without any special annotations.
../../base/bind.h:154:3: error: static_assert failed "p1_is_refcounted_type_and_needs_scoped_refptr""
There *is* protection built in to Bind to make sure you don't bind a raw pointer to a ref-counted type. See here:Here's a little demonstration CL that shows the compile error:"In file included from ../../base/bind_unittest.cc:5:../../base/bind.h:154:3: error: static_assert failed "p1_is_refcounted_type_and_needs_scoped_refptr""Note I just used global functions instead of lambdas, because it seems we'd need to do a little bit of work to make Bind work with lambdas if we wanted to use them that way. But Bind's protections should be the same in either case; they're based on parameter type.Here's the build output with the error:
On Fri Oct 31 2014 at 6:16:37 PM David Michael <dmic...@chromium.org> wrote:There *is* protection built in to Bind to make sure you don't bind a raw pointer to a ref-counted type. See here:Here's a little demonstration CL that shows the compile error:"In file included from ../../base/bind_unittest.cc:5:../../base/bind.h:154:3: error: static_assert failed "p1_is_refcounted_type_and_needs_scoped_refptr""Note I just used global functions instead of lambdas, because it seems we'd need to do a little bit of work to make Bind work with lambdas if we wanted to use them that way. But Bind's protections should be the same in either case; they're based on parameter type.Here's the build output with the error:Yep, agreed; this is effectively the same experiment that I reported earlier in this thread. My comment is about the usage of base::Owned on a1 in his example. My understanding is that that example would compile just fine passing in the a1 instance raw (and also the a2 instance raw if that isn't on a refcounted type, which was ambiguous in his example).
On Fri, Oct 31, 2014 at 12:47 PM, Colin Blundell <blun...@chromium.org> wrote:On Fri Oct 31 2014 at 6:16:37 PM David Michael <dmic...@chromium.org> wrote:There *is* protection built in to Bind to make sure you don't bind a raw pointer to a ref-counted type. See here:Here's a little demonstration CL that shows the compile error:"In file included from ../../base/bind_unittest.cc:5:../../base/bind.h:154:3: error: static_assert failed "p1_is_refcounted_type_and_needs_scoped_refptr""Note I just used global functions instead of lambdas, because it seems we'd need to do a little bit of work to make Bind work with lambdas if we wanted to use them that way. But Bind's protections should be the same in either case; they're based on parameter type.Here's the build output with the error:Yep, agreed; this is effectively the same experiment that I reported earlier in this thread. My comment is about the usage of base::Owned on a1 in his example. My understanding is that that example would compile just fine passing in the a1 instance raw (and also the a2 instance raw if that isn't on a refcounted type, which was ambiguous in his example).Okay, sorry for missing the point earlier. I tried tweaking my example, and I see what you mean. Bind happily accepts raw pointer parameters for parameters with non-refcounted type. (I didn't realize that, and I'm with Jeffrey in wondering why we make the distinction between |this| and other parameters.)
Can we please add lambdas now? I'm reviewing code that will benefit from them.Thanks!
--
I think this thread is waiting for an example where they'd be beneficial. So far, there's only examples of using them with Bind(), where the consensus seems to be that they're both less safe and less readable.
--
I made an attempt at a rule earlier:
"Only use lambdas in cases where they will run in the current thread of execution. For example, a lambda may be used as a functor parameter to methods or functions that will use it immediately, such as those in <algorithm>. Do not bind or store lambdas; use base::Bind and base::Callback instead, because they offer protection against a large class of object lifetime mistakes."
Yes, I think we are talking about ownership transfer via capture. At this point we cannot capture move-only types anyway (until we adopt C++14), so scoped_ptr can't be used in lambda capture. The rule should regarding transferring ownership implicitly by capturing a raw pointer.
If you use for_each on a vector of raw pointers, then you will be passing those pointers to the parameters of the lambda. We are talking about capturing other pointers.So this is fine:std::vector<Data*> data;std::for_each(data.begin(), data.end(), [](Data* p) { p->DoSomething(); });But this one is not:void DoSomething(Data* data) {//...}template<typename F>void Invoke(const F& func) {func();}void foo() {Data* some_data = new Data();auto my_lambda = [some_data]() { DoSomething(some_data); delete some_data; };Invoke(my_lambda);}Here the captured raw pointer |some_data| will be freed as part of lambda execution sequence. So, we are capturing the raw pointer and effectively transferring the ownership of the object to the lambda...
Yes, sure, your last example looks as nasty. However, I'd say it is "pushing it". Your use case has no semantics specific to lambdas. It will happen with a normal function call or a callback. And base::Bind() wouldn't save you in this case either. So, I wouldn't bother mentioning this specific case in the rules...
Right now you can't use lambdas with Bind unless one provides a running adapter to marry the two. You can, however, use lambdas that don't capture anything, since they decay to simple C-style function pointers.
On Thu, Oct 30, 2014 at 4:03 PM, Daniel Murphy <dmu...@chromium.org> wrote:Michael, what about the case that Jeffrey stated above?MyAsyncFunction(base::Bind([](RefcountedClass* self, Arg1* a1, Arg2*
a2, Arg3 a3) {
// body
}, this, base::Owned(local_a1), base::Unretained(local_a2), local_a3));(side note, |this| won't get automatically ref-counted here, because we're not binding with a member function. But I do think Bind will require the use of scoped_refptr<> or base::Unretained when it sees a raw ref-counted type parameter. So the example probably should have scoped_refptr<RefcountedClass>.)
If you see anything inside the []s, you'd know it was against the style guide.Basically, allowing the use of lambdas in the "Bind" architecture, but with no context capturing. This will wrap the lambda in the safely of the callback which provides all of our memory management additions. This is where I see myself using lambdas the most.I think this is less readable than having a separately-defined function in many/most cases. If the function is going to run at a later time or in another thread, it doesn't seem unreasonable for it to be defined separately.That's not necessarily a good argument for banning this usage; we could reasonably count on authors and reviewers to decide what's readable on a case-by-case basis. But IMHO the benefit is small enough we could say "maybe later" for this usage.
--