Discussion:
[std-proposals] Classes with lambda captures
c***@gmail.com
2018-10-22 19:27:10 UTC
Permalink
Hi,

I have an API that requires passing an object which implements multiple
callbacks.

For instance:

template<typename Callbacks> void myAPI(Callbacks &&c)
{
c.foo();
c.bar();
}

When I use it, I end up doing something like this:

void useMyAPI() {
int i;
class MyCallbacks {
MyCallbacks(int &i): i(i) {}
int &i;
void foo() { ++ i; }
void bar() { --i; }
};
myAPI(MyCallbacks{i});
}


This can be cumbersome if I want to capture more local variables.

So, to solve this problem, I'd like to allow captures in local class
definitions.

To make this work, I would:

- make the classes unnamed
- force the class to be used immediately to declare a variable or
generate a temporary

For instance, something like this:

void useMyAPI() {
int i = 0;
myAPI(class [&] {
void foo() { ++ i; }
void bar() { --i; }
}());
}

This would also be useful to generate a class implementing a specific
interface using inheritance.

Incidentally, lambdas could then be defined in terms of a capture-class
with an operator().

A useful addition would be to allow putting destructors in them and, in
that case, allow an implicit, unnamed variable declaration, for easy
RAII, for instance like this:

int *p = (int *)malloc(10);
class [p] {
~() { free(p); }
};

Anyway, so my questions are:
- Has something like this already been proposed? It's not easy to search
for so I didn't find anything in 5 minutes on google
- If not, does it sound interesting? If so I could work on a paper

Thanks!
--
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-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/bf3a4a59-cc3f-4272-a239-80d7e28c4bf0%40isocpp.org.
m***@gmail.com
2018-10-22 20:45:44 UTC
Permalink
It has been suggested (can't find the posts). Problem is it is ultimately a
niche feature, that has workarounds, and still requires considerable amount
of work to design and implement.

Also don't forget that lambdas don't exactly stimulate code reuse.
Post by c***@gmail.com
Hi,
I have an API that requires passing an object which implements multiple
callbacks.
template<typename Callbacks> void myAPI(Callbacks &&c)
{
c.foo();
c.bar();
}
void useMyAPI() {
int i;
class MyCallbacks {
MyCallbacks(int &i): i(i) {}
int &i;
void foo() { ++ i; }
void bar() { --i; }
};
myAPI(MyCallbacks{i});
}
This can be cumbersome if I want to capture more local variables.
So, to solve this problem, I'd like to allow captures in local class
definitions.
- make the classes unnamed
- force the class to be used immediately to declare a variable or
generate a temporary
void useMyAPI() {
int i = 0;
myAPI(class [&] {
void foo() { ++ i; }
void bar() { --i; }
}());
}
This would also be useful to generate a class implementing a specific
interface using inheritance.
Incidentally, lambdas could then be defined in terms of a capture-class
with an operator().
A useful addition would be to allow putting destructors in them and, in
that case, allow an implicit, unnamed variable declaration, for easy
int *p = (int *)malloc(10);
class [p] {
~() { free(p); }
};
- Has something like this already been proposed? It's not easy to search
for so I didn't find anything in 5 minutes on google
- If not, does it sound interesting? If so I could work on a paper
Thanks!
--
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-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/19a0f2e0-344e-489e-9541-387784c2b269%40isocpp.org.
c***@gmail.com
2018-10-22 21:34:09 UTC
Permalink
Post by m***@gmail.com
It has been suggested (can't find the posts). Problem is it is ultimately
a niche feature, that has workarounds, and still requires considerable
amount of work to design and implement.
Fair enough.
Post by m***@gmail.com
Also don't forget that lambdas don't exactly stimulate code reuse.
Curious, my experience has been the exact opposite:
- It allows using std algorithms like remove_if, which without lambdas are
often easier to reimplement inline each time
- It allows factoring small amounts of code within a single function, where
the data dependency would make extracting the code logic harder than
duplicating it. For instance, a common thing I do is factor some common
(invariant) arguments to a recursive function call.
Post by m***@gmail.com
Post by c***@gmail.com
Hi,
I have an API that requires passing an object which implements multiple
callbacks.
template<typename Callbacks> void myAPI(Callbacks &&c)
{
c.foo();
c.bar();
}
void useMyAPI() {
int i;
class MyCallbacks {
MyCallbacks(int &i): i(i) {}
int &i;
void foo() { ++ i; }
void bar() { --i; }
};
myAPI(MyCallbacks{i});
}
This can be cumbersome if I want to capture more local variables.
So, to solve this problem, I'd like to allow captures in local class
definitions.
- make the classes unnamed
- force the class to be used immediately to declare a variable or
generate a temporary
void useMyAPI() {
int i = 0;
myAPI(class [&] {
void foo() { ++ i; }
void bar() { --i; }
}());
}
This would also be useful to generate a class implementing a specific
interface using inheritance.
Incidentally, lambdas could then be defined in terms of a capture-class
with an operator().
A useful addition would be to allow putting destructors in them and, in
that case, allow an implicit, unnamed variable declaration, for easy
int *p = (int *)malloc(10);
class [p] {
~() { free(p); }
};
- Has something like this already been proposed? It's not easy to search
for so I didn't find anything in 5 minutes on google
- If not, does it sound interesting? If so I could work on a paper
Thanks!
--
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-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/1b789066-7b95-4034-bd33-ff510b6f5094%40isocpp.org.
m***@gmail.com
2018-10-23 05:12:15 UTC
Permalink
You might be able to get what you need out of the proposed std::overload
(not sure of the status of that).
Godbolt example: https://godbolt.org/z/IulFWz
Post by c***@gmail.com
Hi,
I have an API that requires passing an object which implements multiple
callbacks.
template<typename Callbacks> void myAPI(Callbacks &&c)
{
c.foo();
c.bar();
}
void useMyAPI() {
int i;
class MyCallbacks {
MyCallbacks(int &i): i(i) {}
int &i;
void foo() { ++ i; }
void bar() { --i; }
};
myAPI(MyCallbacks{i});
}
This can be cumbersome if I want to capture more local variables.
So, to solve this problem, I'd like to allow captures in local class
definitions.
- make the classes unnamed
- force the class to be used immediately to declare a variable or
generate a temporary
void useMyAPI() {
int i = 0;
myAPI(class [&] {
void foo() { ++ i; }
void bar() { --i; }
}());
}
This would also be useful to generate a class implementing a specific
interface using inheritance.
Incidentally, lambdas could then be defined in terms of a capture-class
with an operator().
A useful addition would be to allow putting destructors in them and, in
that case, allow an implicit, unnamed variable declaration, for easy
int *p = (int *)malloc(10);
class [p] {
~() { free(p); }
};
- Has something like this already been proposed? It's not easy to search
for so I didn't find anything in 5 minutes on google
- If not, does it sound interesting? If so I could work on a paper
Thanks!
--
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-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/00a6d453-fc6f-49c9-aa7d-f1135b3aacd3%40isocpp.org.
Gašper Ažman
2018-10-24 14:31:59 UTC
Permalink
If not directly std::overload, its implementation should give you the idea
of how to implement what you need as a pure library.
Post by m***@gmail.com
You might be able to get what you need out of the proposed std::overload
(not sure of the status of that).
Godbolt example: https://godbolt.org/z/IulFWz
Post by c***@gmail.com
Hi,
I have an API that requires passing an object which implements multiple
callbacks.
template<typename Callbacks> void myAPI(Callbacks &&c)
{
c.foo();
c.bar();
}
void useMyAPI() {
int i;
class MyCallbacks {
MyCallbacks(int &i): i(i) {}
int &i;
void foo() { ++ i; }
void bar() { --i; }
};
myAPI(MyCallbacks{i});
}
This can be cumbersome if I want to capture more local variables.
So, to solve this problem, I'd like to allow captures in local class
definitions.
- make the classes unnamed
- force the class to be used immediately to declare a variable or
generate a temporary
void useMyAPI() {
int i = 0;
myAPI(class [&] {
void foo() { ++ i; }
void bar() { --i; }
}());
}
This would also be useful to generate a class implementing a specific
interface using inheritance.
Incidentally, lambdas could then be defined in terms of a capture-class
with an operator().
A useful addition would be to allow putting destructors in them and, in
that case, allow an implicit, unnamed variable declaration, for easy
int *p = (int *)malloc(10);
class [p] {
~() { free(p); }
};
- Has something like this already been proposed? It's not easy to search
for so I didn't find anything in 5 minutes on google
- If not, does it sound interesting? If so I could work on a paper
Thanks!
--
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
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/00a6d453-fc6f-49c9-aa7d-f1135b3aacd3%40isocpp.org
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/00a6d453-fc6f-49c9-aa7d-f1135b3aacd3%40isocpp.org?utm_medium=email&utm_source=footer>
.
--
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-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAANG%3DkWkKtC-bHrToHc-zHWySFWxhW6_1LxrjZoVo9VfeWCSPA%40mail.gmail.com.
Victor Dyachenko
2018-10-25 10:47:44 UTC
Permalink
Post by m***@gmail.com
You might be able to get what you need out of the proposed std::overload
(not sure of the status of that).
Godbolt example: https://godbolt.org/z/IulFWz
And now we have copy of the captured data in EACH lambda. std::overload is
a bad solution.


- If not, does it sound interesting? If so I could work on a paper
Sounds interesting for me. std::variant visitation is more convenient using
functional objects like:

struct visitor
{
void operator()(int v) const { ...; }
void operator()(float v) const { ...; }
template<class Other> void operator()(const Other &v) const { ...; }
};

It's more convenient that

[](auto &v) {
using T = std::remove_cvref_t<decltype(v)>;
if constexpr(std::is_same<T, int>())
...;
else if constexpr(std::is_same<T, float>())
...;
else
...;
}

But almost always we need to capture some data and it's a pain with
hand-written class now... :-(
--
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-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/a92696b6-06b5-41f8-8be9-b7bf8c2bc6c1%40isocpp.org.
Olivier Sohn
2018-10-26 08:29:49 UTC
Permalink
Hello,

I like the first part of the idea, however I think that allowing to
implement destructors may not be a good idea: with the example you provide,
if the lambda is somewhere passed by value, then the source lambda will be
destructed, the pointer will be deleted and the lambda copy will have a
dangling pointer. Capturing using a std::unique_ptr
<https://stackoverflow.com/questions/8236521/how-to-capture-a-unique-ptr-into-a-lambda-expression> in
your example would do what you want, without the need to write a destructor.

My 2 cents

Olivier
Post by c***@gmail.com
Hi,
I have an API that requires passing an object which implements multiple
callbacks.
template<typename Callbacks> void myAPI(Callbacks &&c)
{
c.foo();
c.bar();
}
void useMyAPI() {
int i;
class MyCallbacks {
MyCallbacks(int &i): i(i) {}
int &i;
void foo() { ++ i; }
void bar() { --i; }
};
myAPI(MyCallbacks{i});
}
This can be cumbersome if I want to capture more local variables.
So, to solve this problem, I'd like to allow captures in local class
definitions.
- make the classes unnamed
- force the class to be used immediately to declare a variable or
generate a temporary
void useMyAPI() {
int i = 0;
myAPI(class [&] {
void foo() { ++ i; }
void bar() { --i; }
}());
}
This would also be useful to generate a class implementing a specific
interface using inheritance.
Incidentally, lambdas could then be defined in terms of a capture-class
with an operator().
A useful addition would be to allow putting destructors in them and, in
that case, allow an implicit, unnamed variable declaration, for easy
int *p = (int *)malloc(10);
class [p] {
~() { free(p); }
};
- Has something like this already been proposed? It's not easy to search
for so I didn't find anything in 5 minutes on google
- If not, does it sound interesting? If so I could work on a paper
Thanks!
--
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-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/523bb4bb-aca6-4ae8-911f-9e0839675e5b%40isocpp.org.
c***@gmail.com
2018-10-26 23:51:15 UTC
Permalink
Post by Olivier Sohn
Hello,
I like the first part of the idea, however I think that allowing to
implement destructors may not be a good idea: with the example you provide,
if the lambda is somewhere passed by value, then the source lambda will be
destructed, the pointer will be deleted and the lambda copy will have a
dangling pointer. Capturing using a std::unique_ptr
<https://www.google.com/url?q=https%3A%2F%2Fstackoverflow.com%2Fquestions%2F8236521%2Fhow-to-capture-a-unique-ptr-into-a-lambda-expression&sa=D&sntz=1&usg=AFQjCNFQoPc45xM-K5CxKLIKPRjumfq0lw> in
your example would do what you want, without the need to write a destructor.
In my example with a destructor, an anonymous instance of the unnamed type
is generated. There's no way to copy the instance. I'm not sure I
understand your objection.
My preferred solution would be to allow "finally" in C++, but that's
probably never going to happen.
Post by Olivier Sohn
My 2 cents
Olivier
Post by c***@gmail.com
Hi,
I have an API that requires passing an object which implements multiple
callbacks.
template<typename Callbacks> void myAPI(Callbacks &&c)
{
c.foo();
c.bar();
}
void useMyAPI() {
int i;
class MyCallbacks {
MyCallbacks(int &i): i(i) {}
int &i;
void foo() { ++ i; }
void bar() { --i; }
};
myAPI(MyCallbacks{i});
}
This can be cumbersome if I want to capture more local variables.
So, to solve this problem, I'd like to allow captures in local class
definitions.
- make the classes unnamed
- force the class to be used immediately to declare a variable or
generate a temporary
void useMyAPI() {
int i = 0;
myAPI(class [&] {
void foo() { ++ i; }
void bar() { --i; }
}());
}
This would also be useful to generate a class implementing a specific
interface using inheritance.
Incidentally, lambdas could then be defined in terms of a capture-class
with an operator().
A useful addition would be to allow putting destructors in them and, in
that case, allow an implicit, unnamed variable declaration, for easy
int *p = (int *)malloc(10);
class [p] {
~() { free(p); }
};
- Has something like this already been proposed? It's not easy to search
for so I didn't find anything in 5 minutes on google
- If not, does it sound interesting? If so I could work on a paper
Thanks!
--
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-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/1ca63b59-268a-4818-9ba9-08c4f58595ef%40isocpp.org.
Arthur O'Dwyer
2018-10-27 02:52:24 UTC
Permalink
Post by c***@gmail.com
Post by Olivier Sohn
I like the first part of the idea, however I think that allowing to
implement destructors may not be a good idea: with the example you provide,
if the lambda is somewhere passed by value, then the source lambda will be
destructed, the pointer will be deleted and the lambda copy will have a
dangling pointer. Capturing using a std::unique_ptr
<https://www.google.com/url?q=https%3A%2F%2Fstackoverflow.com%2Fquestions%2F8236521%2Fhow-to-capture-a-unique-ptr-into-a-lambda-expression&sa=D&sntz=1&usg=AFQjCNFQoPc45xM-K5CxKLIKPRjumfq0lw> in
your example would do what you want, without the need to write a destructor.
In my example with a destructor, an anonymous instance of the unnamed type
is generated. There's no way to copy the instance. I'm not sure I
understand your objection.
Providing a user-provided destructor does not actually render the class
non-copyable.
https://wandbox.org/permlink/O9hsBJpgsiqJZCI1
(This behavior is deprecated, but not actually removed.)


My preferred solution would be to allow "finally" in C++, but that's
Post by c***@gmail.com
probably never going to happen.
Shameless plug:
https://quuxplusone.github.io/blog/2018/08/11/the-auto-macro/

Anyway, as for the idea, I think it's philosophically interesting, but not
worth pursuing in that form in practice because the cost/benefit ratio (in
terms of "friction with the existing C++ grammar" and "cost savings for
software developers") is just not there.

The people telling you to look at std::overload, AFAICT, are wrong and have
misunderstood your original post.

I'll chime in a different wrong idea, though: Your problem seems similar in
spirit to "dynamic polymorphism", in that you have a bag of behaviors and
you want to wrap it up in something with the right "shape" (which in your
current codebase is a class with foo and bar methods). If you can change
your codebase to prefer Dyno <https://github.com/ldionne/dyno#overview>
syntax, then you could end up with something like a cross between std::bind
and std::overload that yields a Dyno object. I'm too lazy to look up the
real syntax but I can see how to implement this syntax in vanilla C++14:

DEFINE_POLY_METHOD_TAG(foo);

DEFINE_POLY_METHOD_TAG(bar);


template<typename Callbacks>

void myAPI(const poly<foo, bar>& c)
{
c.call<foo>();
c.call<bar>();
}

void useMyAPI() {
int i;

myAPI(

polybind(std::ref(i))

.define<foo>([](int& i){ ++i; })

.define<bar>([](int& i){ --i; })

);
}


void useMyAPI_simpler() {
int i;

myAPI(

polybind()

.define<foo>([&](){ ++i; })

.define<bar>([&](){ --i; })

);
}


(Hat tip to John Bandela for the c.call<foo>() syntax. He showed it to me at a Bay Area C++ meetup a while back; I don't think it's been implemented anywhere public yet. But it's doable!)


–Arthur
--
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-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/c729089c-c560-4add-abd2-6cd6a651d57e%40isocpp.org.
Raymund Hofmann
2018-10-29 13:55:54 UTC
Permalink
I had a similar idea for anonymous "lambda classes" just extending the
lambda function idea we already have.

We have lambda functions anyway and basically they are a class with
function call operator, why not extend them to be a "lambda class"?

My motivation was to create a self-owned anonymous deriving from
"enable_shared_from_this" class that is kicked off as a servlet that
captures a few things from the current scope and then doing some network
stuff and destroying itself when finished.

I ended up doing it with a lambda like so:

struct selfowning_functor : public
std::enable_shared_from_this<selfowning_functor>
{
typedef function<void(shared_ptr<struct selfowning_functor>&&)> rc_ft;
rc_ft lambda;
selfowning_functor(rc_ft&& _f) : lambda(std::move(_f)) {}
void start(void)
{
lambda(shared_from_this());
}
void operator()(void)
{
lambda(shared_from_this());
}
};

template<class _Type>
shared_ptr<selfowning_functor> make_selfowning_functor(_Type&& _Arg)
{
return make_shared<selfowning_functor>(_Arg);
}

use:
make_selfowning_functor([=, buffer =
vector<char>(0x10000)](auto&& self) mutable
{
if (auto read_length = ifs->read(&buffer[0],
buffer.size()).gcount(); read_length > 0)
{
response->write(&buffer[0], read_length);
if (read_length == buffer.size())
response->send([=](auto &ec)
{
if (!ec)
(*self)();
else
cerr << "Connection interrupted" <<
endl;
});
}
})->start();


So this is the "library" solution wrapping a capturing lambda in a class
and also allowing the lambda to be recursive.

I can't see how a language extension somewhat like "[] class (){}" to the
already existing and very useful "[](){}" would create much "friction with
the existing C++ grammar. How would it?


Then you could also define classes quickly that have members/captures
without repeating their name thrice, greatly improving DRY.

Sure, details are not worked out, but it is a good idea to work on.
--
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-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAD_7Vbks-oT0%3DRFiaPFn304iHY07J6LygUfCCRjv-kpd7yhKGw%40mail.gmail.com.
Loading...