Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c++ > #120329 > unrolled thread
| Started by | Michael S <already5chosen@yahoo.com> |
|---|---|
| First post | 2024-09-25 19:54 +0300 |
| Last post | 2024-09-28 04:25 -0700 |
| Articles | 15 on this page of 35 — 6 participants |
Back to article view | Back to comp.lang.c++
thread about the pros and cons of lambdas, but more about cons Michael S <already5chosen@yahoo.com> - 2024-09-25 19:54 +0300
Re: thread about the pros and cons of lambdas, but more about cons Bonita Montero <Bonita.Montero@gmail.com> - 2024-09-25 19:17 +0200
Re: thread about the pros and cons of lambdas, but more about cons Bonita Montero <Bonita.Montero@gmail.com> - 2024-09-25 19:55 +0200
Re: thread about the pros and cons of lambdas, but more about cons Paavo Helde <eesnimi@osa.pri.ee> - 2024-09-25 22:53 +0300
Re: thread about the pros and cons of lambdas, but more about cons Michael S <already5chosen@yahoo.com> - 2024-09-25 23:30 +0300
Re: thread about the pros and cons of lambdas, but more about cons Paavo Helde <eesnimi@osa.pri.ee> - 2024-09-26 00:04 +0300
Re: thread about the pros and cons of lambdas, but more about cons Michael S <already5chosen@yahoo.com> - 2024-09-26 11:17 +0300
Re: thread about the pros and cons of lambdas, but more about cons Paavo Helde <eesnimi@osa.pri.ee> - 2024-09-26 11:25 +0300
Re: thread about the pros and cons of lambdas, but more about cons Bonita Montero <Bonita.Montero@gmail.com> - 2024-09-26 10:28 +0200
Re: thread about the pros and cons of lambdas, but more about cons Paavo Helde <eesnimi@osa.pri.ee> - 2024-09-26 11:49 +0300
Re: thread about the pros and cons of lambdas, but more about cons Michael S <already5chosen@yahoo.com> - 2024-09-26 11:38 +0300
Re: thread about the pros and cons of lambdas, but more about cons Ben Bacarisse <ben@bsb.me.uk> - 2024-09-25 23:13 +0100
Re: thread about the pros and cons of lambdas, but more about cons Ben Bacarisse <ben@bsb.me.uk> - 2024-09-25 23:28 +0100
Re: thread about the pros and cons of lambdas, but more about cons Michael S <already5chosen@yahoo.com> - 2024-09-26 10:41 +0300
Re: thread about the pros and cons of lambdas, but more about cons David Brown <david.brown@hesbynett.no> - 2024-09-26 10:29 +0200
Re: thread about the pros and cons of lambdas, but more about cons Bonita Montero <Bonita.Montero@gmail.com> - 2024-09-26 11:35 +0200
Re: thread about the pros and cons of lambdas, but more about cons David Brown <david.brown@hesbynett.no> - 2024-09-26 13:27 +0200
Re: thread about the pros and cons of lambdas, but more about cons Bonita Montero <Bonita.Montero@gmail.com> - 2024-09-26 13:31 +0200
Re: thread about the pros and cons of lambdas, but more about cons Michael S <already5chosen@yahoo.com> - 2024-09-26 15:25 +0300
Re: thread about the pros and cons of lambdas, but more about cons Bonita Montero <Bonita.Montero@gmail.com> - 2024-09-26 14:58 +0200
Re: thread about the pros and cons of lambdas, but more about cons Michael S <already5chosen@yahoo.com> - 2024-09-26 16:53 +0300
Re: thread about the pros and cons of lambdas, but more about cons Bonita Montero <Bonita.Montero@gmail.com> - 2024-09-26 15:54 +0200
Re: thread about the pros and cons of lambdas, but more about cons Michael S <already5chosen@yahoo.com> - 2024-09-27 16:55 +0300
Re: thread about the pros and cons of lambdas, but more about cons Tim Rentsch <tr.17687@z991.linuxsc.com> - 2024-09-26 10:01 -0700
Re: thread about the pros and cons of lambdas, but more about cons Bonita Montero <Bonita.Montero@gmail.com> - 2024-09-26 19:04 +0200
Re: thread about the pros and cons of lambdas, but more about cons Michael S <already5chosen@yahoo.com> - 2024-09-26 20:20 +0300
Re: thread about the pros and cons of lambdas, but more about cons Tim Rentsch <tr.17687@z991.linuxsc.com> - 2024-09-26 10:38 -0700
Re: thread about the pros and cons of lambdas, but more about cons Michael S <already5chosen@yahoo.com> - 2024-09-27 16:27 +0300
Re: thread about the pros and cons of lambdas, but more about cons Tim Rentsch <tr.17687@z991.linuxsc.com> - 2024-09-28 04:06 -0700
Re: thread about the pros and cons of lambdas, but more about cons Michael S <already5chosen@yahoo.com> - 2024-09-26 10:58 +0300
Re: thread about the pros and cons of lambdas, but more about cons Tim Rentsch <tr.17687@z991.linuxsc.com> - 2024-09-26 10:27 -0700
Re: thread about the pros and cons of lambdas, but more about cons Bonita Montero <Bonita.Montero@gmail.com> - 2024-09-26 19:32 +0200
Re: thread about the pros and cons of lambdas, but more about cons Tim Rentsch <tr.17687@z991.linuxsc.com> - 2024-09-26 14:09 -0700
Re: thread about the pros and cons of lambdas, but more about cons Michael S <already5chosen@yahoo.com> - 2024-09-27 16:15 +0300
Re: thread about the pros and cons of lambdas, but more about cons Tim Rentsch <tr.17687@z991.linuxsc.com> - 2024-09-28 04:25 -0700
Page 2 of 2 — ← Prev page 1 [2]
| From | Michael S <already5chosen@yahoo.com> |
|---|---|
| Date | 2024-09-26 16:53 +0300 |
| Message-ID | <20240926165304.0000543d@yahoo.com> |
| In reply to | #120375 |
On Thu, 26 Sep 2024 14:58:26 +0200 Bonita Montero <Bonita.Montero@gmail.com> wrote: > Am 26.09.2024 um 14:25 schrieb Michael S: > > > As majority of things, it depends. > > I can also imagine code where the call-overhead might be relevant, > but the virtual dispatch on the function<>-object is that fast that > it is not relevant in 95% of all cases. > > Actually, I am not at all sure that slowdown in my case caused by overhead of virtual dispatch. It appears more likely that virtuality of dispatch prevents from happening some sort of compiler optimization, probably partial inlining, that in case of direct call is able to eliminating approximately half of the calls. But exact reason does not matter. What matters is a slowdown.
[toc] | [prev] | [next] | [standalone]
| From | Bonita Montero <Bonita.Montero@gmail.com> |
|---|---|
| Date | 2024-09-26 15:54 +0200 |
| Message-ID | <vd3p2s$72a0$1@raubtier-asyl.eternal-september.org> |
| In reply to | #120376 |
Am 26.09.2024 um 15:53 schrieb Michael S: > Actually, I am not at all sure that slowdown in my case caused by > overhead of virtual dispatch. > It appears more likely that virtuality of dispatch prevents from > happening some sort of compiler optimization, probably partial inlining, > that in case of direct call is able to eliminating approximately half > of the calls. > But exact reason does not matter. What matters is a slowdown. Use function<>-dispatch only for coarse-grained virtuality, then you don't make anything worse.
[toc] | [prev] | [next] | [standalone]
| From | Michael S <already5chosen@yahoo.com> |
|---|---|
| Date | 2024-09-27 16:55 +0300 |
| Message-ID | <20240927165507.00002a56@yahoo.com> |
| In reply to | #120347 |
On Thu, 26 Sep 2024 10:41:24 +0300
Michael S <already5chosen@yahoo.com> wrote:
> On Wed, 25 Sep 2024 23:28:57 +0100
> Ben Bacarisse <ben@bsb.me.uk> wrote:
>
> > Ben Bacarisse <ben@bsb.me.uk> writes:
> >
> > > ... You can,
> > > instead, do this (untested):
> >
> > I should have tested! Of course you have to change the capture (at
> > least for core) from '=' to '&':
> >
> > > #include <functional>
> > >
> > > int floodfill4(
> > > unsigned char *grey,
> > > int width, int height,
> > > int x, int y,
> > > unsigned char target, unsigned char dest)
> > > {
> > > if (width < 1 || height < 1)
> > > return 0;
> > > if (x < 0 || x >= width || y < 0 || y >= height)
> > > return 0;
> > >
> > > size_t w = width, h = height;
> > > if (grey[y*w+x] != target)
> > > return 0;
> > >
> > > std::function<void(size_t, size_t)> core;
> > > core = [&] (size_t x, size_t y) {
> > Corrected-----^
> > > if (x >= w || y >= h)
> > > return;
> > > auto idx = y*w+x;
> > > if (grey[idx] == target) {
> > > grey[idx] = dest;
> > > core(x - 1, y);
> > > core(x + 1, y);
> > > core(x, y - 1);
> > > core(x, y + 1);
> > > }
> > > };
> > > core(x, y);
> > > return 1;
> > > }
> >
>
> That's where I started.
> It is approximately twice slower than tricky version with [&].
> And tricky [&] almost 20% slower than tricky [=].
> I didn't try to understand what std::function thing is, but fast it
> isn't.
>
>
>
After digging a bit deeper I realized that slowness is only a symptom
of the bigger problem. This variant of lambda defeats the whole purpose
of having lambda/context, i.e. reduction of the size of call frame.
This variant of lambda generates call frame of exactly the same size as
straightforward code below.
I'd guess, it means that this variant of lambda tracks its captures
separately, instead of tracking them together via single pointer to
parent's stack frame.
#include <cstddef>
static void floodfill4u(
unsigned char *grey,
size_t width, size_t height,
size_t x, size_t y,
unsigned char target, unsigned char dest)
{
if (x >= width || y >= height)
return;
auto idx = y*width+x;
if (grey[idx] == target) {
grey[idx] = dest;
floodfill4u(grey, width, height, x - 1, y, target, dest);
floodfill4u(grey, width, height, x + 1, y, target, dest);
floodfill4u(grey, width, height, x, y - 1, target, dest);
floodfill4u(grey, width, height, x, y + 1, target, dest);
}
}
int floodfill4(
unsigned char *grey,
int width, int height,
int x, int y,
unsigned char target, unsigned char dest)
{
if (width < 1 || height < 1)
return 0;
if (x < 0 || x >= width || y < 0 || y >= height)
return 0;
if (grey[(size_t)y*width+x] != target)
return 0;
floodfill4u(grey, width, height, x, y, target, dest);
return 1;
}
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <tr.17687@z991.linuxsc.com> |
|---|---|
| Date | 2024-09-26 10:01 -0700 |
| Message-ID | <8634lm73jc.fsf@linuxsc.com> |
| In reply to | #120341 |
Ben Bacarisse <ben@bsb.me.uk> writes:
> Ben Bacarisse <ben@bsb.me.uk> writes:
>
>> ... You can,
>> instead, do this (untested):
>
> I should have tested! Of course you have to change the capture (at
> least for core) from '=' to '&':
>
>> #include <functional>
>>
>> int floodfill4(
>> unsigned char *grey,
>> int width, int height,
>> int x, int y,
>> unsigned char target, unsigned char dest)
>> {
>> if (width < 1 || height < 1)
>> return 0;
>> if (x < 0 || x >= width || y < 0 || y >= height)
>> return 0;
>>
>> size_t w = width, h = height;
>> if (grey[y*w+x] != target)
>> return 0;
>>
>> std::function<void(size_t, size_t)> core;
>> core = [&] (size_t x, size_t y) {
>
> Corrected-----^
Why do you think [&] should be used instead of [=]?
[toc] | [prev] | [next] | [standalone]
| From | Bonita Montero <Bonita.Montero@gmail.com> |
|---|---|
| Date | 2024-09-26 19:04 +0200 |
| Message-ID | <vd4460$8umu$1@raubtier-asyl.eternal-september.org> |
| In reply to | #120383 |
Am 26.09.2024 um 19:01 schrieb Tim Rentsch:
> Ben Bacarisse <ben@bsb.me.uk> writes:
>
>> Ben Bacarisse <ben@bsb.me.uk> writes:
>>
>>> ... You can,
>>> instead, do this (untested):
>>
>> I should have tested! Of course you have to change the capture (at
>> least for core) from '=' to '&':
>>
>>> #include <functional>
>>>
>>> int floodfill4(
>>> unsigned char *grey,
>>> int width, int height,
>>> int x, int y,
>>> unsigned char target, unsigned char dest)
>>> {
>>> if (width < 1 || height < 1)
>>> return 0;
>>> if (x < 0 || x >= width || y < 0 || y >= height)
>>> return 0;
>>>
>>> size_t w = width, h = height;
>>> if (grey[y*w+x] != target)
>>> return 0;
>>>
>>> std::function<void(size_t, size_t)> core;
>>> core = [&] (size_t x, size_t y) {
>>
>> Corrected-----^
>
> Why do you think [&] should be used instead of [=]?
If you're capturing only variables of a fundamental type the whole
copying is optimized away. If you need a lambda in a foreign thread
context you have to copy always.
[toc] | [prev] | [next] | [standalone]
| From | Michael S <already5chosen@yahoo.com> |
|---|---|
| Date | 2024-09-26 20:20 +0300 |
| Message-ID | <20240926202050.00002d46@yahoo.com> |
| In reply to | #120383 |
On Thu, 26 Sep 2024 10:01:11 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
> Ben Bacarisse <ben@bsb.me.uk> writes:
>
> > Ben Bacarisse <ben@bsb.me.uk> writes:
> >
> >> ... You can,
> >> instead, do this (untested):
> >
> > I should have tested! Of course you have to change the capture (at
> > least for core) from '=' to '&':
> >
> >> #include <functional>
> >>
> >> int floodfill4(
> >> unsigned char *grey,
> >> int width, int height,
> >> int x, int y,
> >> unsigned char target, unsigned char dest)
> >> {
> >> if (width < 1 || height < 1)
> >> return 0;
> >> if (x < 0 || x >= width || y < 0 || y >= height)
> >> return 0;
> >>
> >> size_t w = width, h = height;
> >> if (grey[y*w+x] != target)
> >> return 0;
> >>
> >> std::function<void(size_t, size_t)> core;
> >> core = [&] (size_t x, size_t y) {
> >
> > Corrected-----^
>
> Why do you think [&] should be used instead of [=]?
Compiler says that otherwise 'core' captures itself before it properly
initialize.
And indeed, it does not work (Segmentation fault).
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <tr.17687@z991.linuxsc.com> |
|---|---|
| Date | 2024-09-26 10:38 -0700 |
| Message-ID | <86tte25n9h.fsf@linuxsc.com> |
| In reply to | #120385 |
Michael S <already5chosen@yahoo.com> writes:
> On Thu, 26 Sep 2024 10:01:11 -0700
> Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
>
>> Ben Bacarisse <ben@bsb.me.uk> writes:
>>
>>> Ben Bacarisse <ben@bsb.me.uk> writes:
>>>
>>>> ... You can,
>>>> instead, do this (untested):
>>>
>>> I should have tested! Of course you have to change the capture (at
>>> least for core) from '=' to '&':
>>>
>>>> #include <functional>
>>>>
>>>> int floodfill4(
>>>> unsigned char *grey,
>>>> int width, int height,
>>>> int x, int y,
>>>> unsigned char target, unsigned char dest)
>>>> {
>>>> if (width < 1 || height < 1)
>>>> return 0;
>>>> if (x < 0 || x >= width || y < 0 || y >= height)
>>>> return 0;
>>>>
>>>> size_t w = width, h = height;
>>>> if (grey[y*w+x] != target)
>>>> return 0;
>>>>
>>>> std::function<void(size_t, size_t)> core;
>>>> core = [&] (size_t x, size_t y) {
>>>
>>> Corrected-----^
>>
>> Why do you think [&] should be used instead of [=]?
>
> Compiler says that otherwise 'core' captures itself before it
> properly initialize.
> And indeed, it does not work (Segmentation fault).
Ahh. A chicken and egg problem. I sidestepped that by initializing
'core' as part of its declaration (as my other response to Ben
shows).
No compiler warning, but I didn't try running it.
[toc] | [prev] | [next] | [standalone]
| From | Michael S <already5chosen@yahoo.com> |
|---|---|
| Date | 2024-09-27 16:27 +0300 |
| Message-ID | <20240927162713.000060ea@yahoo.com> |
| In reply to | #120388 |
On Thu, 26 Sep 2024 10:38:02 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
> Michael S <already5chosen@yahoo.com> writes:
>
> > On Thu, 26 Sep 2024 10:01:11 -0700
> > Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
> >
> >> Ben Bacarisse <ben@bsb.me.uk> writes:
> >>
> >>> Ben Bacarisse <ben@bsb.me.uk> writes:
> >>>
> >>>> ... You can,
> >>>> instead, do this (untested):
> >>>
> >>> I should have tested! Of course you have to change the capture
> >>> (at least for core) from '=' to '&':
> >>>
> >>>> #include <functional>
> >>>>
> >>>> int floodfill4(
> >>>> unsigned char *grey,
> >>>> int width, int height,
> >>>> int x, int y,
> >>>> unsigned char target, unsigned char dest)
> >>>> {
> >>>> if (width < 1 || height < 1)
> >>>> return 0;
> >>>> if (x < 0 || x >= width || y < 0 || y >= height)
> >>>> return 0;
> >>>>
> >>>> size_t w = width, h = height;
> >>>> if (grey[y*w+x] != target)
> >>>> return 0;
> >>>>
> >>>> std::function<void(size_t, size_t)> core;
> >>>> core = [&] (size_t x, size_t y) {
> >>>
> >>> Corrected-----^
> >>
> >> Why do you think [&] should be used instead of [=]?
> >
> > Compiler says that otherwise 'core' captures itself before it
> > properly initialize.
> > And indeed, it does not work (Segmentation fault).
>
> Ahh. A chicken and egg problem. I sidestepped that by initializing
> 'core' as part of its declaration (as my other response to Ben
> shows).
>
> No compiler warning, but I didn't try running it.
It does not make any difference. The same crash as before.
BTW, I am surprised that your compiler issues no warning.
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <tr.17687@z991.linuxsc.com> |
|---|---|
| Date | 2024-09-28 04:06 -0700 |
| Message-ID | <86setk3um7.fsf@linuxsc.com> |
| In reply to | #120413 |
Michael S <already5chosen@yahoo.com> writes:
> On Thu, 26 Sep 2024 10:38:02 -0700
> Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
>
>> Michael S <already5chosen@yahoo.com> writes:
>>
>>> On Thu, 26 Sep 2024 10:01:11 -0700
>>> Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
>>>
>>>> Ben Bacarisse <ben@bsb.me.uk> writes:
>>>>
>>>>> Ben Bacarisse <ben@bsb.me.uk> writes:
>>>>>
>>>>>> ... You can,
>>>>>> instead, do this (untested):
>>>>>
>>>>> I should have tested! Of course you have to change the capture
>>>>> (at least for core) from '=' to '&':
>>>>>
>>>>>> #include <functional>
>>>>>>
>>>>>> int floodfill4(
>>>>>> unsigned char *grey,
>>>>>> int width, int height,
>>>>>> int x, int y,
>>>>>> unsigned char target, unsigned char dest)
>>>>>> {
>>>>>> if (width < 1 || height < 1)
>>>>>> return 0;
>>>>>> if (x < 0 || x >= width || y < 0 || y >= height)
>>>>>> return 0;
>>>>>>
>>>>>> size_t w = width, h = height;
>>>>>> if (grey[y*w+x] != target)
>>>>>> return 0;
>>>>>>
>>>>>> std::function<void(size_t, size_t)> core;
>>>>>> core = [&] (size_t x, size_t y) {
>>>>>
>>>>> Corrected-----^
>>>>
>>>> Why do you think [&] should be used instead of [=]?
>>>
>>> Compiler says that otherwise 'core' captures itself before it
>>> properly initialize.
>>> And indeed, it does not work (Segmentation fault).
>>
>> Ahh. A chicken and egg problem. I sidestepped that by initializing
>> 'core' as part of its declaration (as my other response to Ben
>> shows).
>>
>> No compiler warning, but I didn't try running it.
>
> It does not make any difference. The same crash as before.
Yes, not surprising.
> BTW, I am surprised that your compiler issues no warning.
Probably just an old compiler. I'm not up-to-date with what's
going on with C++ so there isn't much incentive to upgrade.
Trying again with clang I do get a warning.
[toc] | [prev] | [next] | [standalone]
| From | Michael S <already5chosen@yahoo.com> |
|---|---|
| Date | 2024-09-26 10:58 +0300 |
| Message-ID | <20240926105846.00003cfe@yahoo.com> |
| In reply to | #120340 |
On Wed, 25 Sep 2024 23:13:00 +0100 Ben Bacarisse <ben@bsb.me.uk> wrote: > Michael S <already5chosen@yahoo.com> writes: > > > > > // end of floodfill_recursive_lambda. > > > > > > In the second variant in order to make it compile at all I had to > > uses very dirty trick with lambda passed as parameter to itself. I > > copied it from Stack Overflow, but don't pretend to understand why > > it works and why it needed in the first place. > > For this part the answer is simple. The lambda only captures names > that are defined at its point of definition, and core is not defined > until the end of the declarator which include the initialisation -- > the lambda you are defining. The lambda stored in 'core', can't > therefore refer to the name core because core was not defined before > the lambda. It does not sound too different from struct that holds pointer to itself. If forward declaration works for later I don't see why it can not work for the former. Isn't lambda a sort of struct when we look under the hood? May be, the syntax for forward declaration would need a new keyword, but that does not sound like exaggerated price for convenience and for improved readability. > You can, instead, do this (untested): > <snip> > > > But that's only part of the story. > > The other part is that the first variant is 1.2x faster. > > Interesting, though this is not really what a lambda is for. What you > want here is a plain lexical nested function -- it's purpose being > just an auxiliary function that can refer to the outer scope so as to > require fewer parameters. > Yes, that's a fair definition of my needs. The main and almost only reason for having core() instead of calling recursively to floodfill4() itself is to reduce the size of stack frame of recursive call. > It would be interesting to see is gcc's nested function extension > produced something that was faster than a lambda. > > Note: this is a very old problem and actually pre-dates the computing > era. Combinatory logic had to come up with the Y combinator to make > recursive "functions", and (slightly more recently) some Lisps have > two forms of 'let' to deal with this issue. > I don't know what is Y combinator. I do know that in Go forward declaration works fine.
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <tr.17687@z991.linuxsc.com> |
|---|---|
| Date | 2024-09-26 10:27 -0700 |
| Message-ID | <86y13e5nri.fsf@linuxsc.com> |
| In reply to | #120340 |
Ben Bacarisse <ben@bsb.me.uk> writes:
> Michael S <already5chosen@yahoo.com> writes:
[...]
>> Some time ago on comp.lang.c we had very long thread about
>> floodfill4 algorithm (that both myself and TimR took more
>> seriously than an issue deserves, but that's off topic).
>>
>> Today I coded two implementations of original brute-force
>> recursive algorithm.
>>
>> // floodfill_recursive_nested.
>> // Implementation is in none-tricky C++
>> // Very similar to what can be done in C
>>
>> #include <cstddef>
>>
>> int floodfill4(
>> unsigned char *grey,
>> int width, int height,
>> int x, int y,
>> unsigned char target, unsigned char dest)
>> {
>> if (width < 1 || height < 1)
>> return 0;
>> if (x < 0 || x >= width || y < 0 || y >= height)
>> return 0;
>>
>> size_t w = width, h = height;
>> if (grey[y*w+x] != target)
>> return 0;
>>
>> struct {
>> unsigned char *grey;
>> size_t width, height;
>> unsigned char target, dest;
>> void core(size_t x, size_t y) const
>> {
>> if (x < width && y < height) {
>> auto idx = y*width+x;
>> if (grey[idx] == target) {
>> grey[idx] = dest;
>> core(x - 1, y);
>> core(x + 1, y);
>> core(x, y - 1);
>> core(x, y + 1);
>> }
>> }
>> }
>> } context = {
>> .grey = grey,
>> .width = w, .height = h,
>> .target = target, .dest = dest,
>> };
>> context.core(x, y);
>> return 1;
>> }
>>
>> // end of floodfill_recursive_nested.
>>
>>
>>
>> // floodfill_recursive_lambda.
>> // Implementation in tricky C++
>> // C can not do it
>>
>> #include <cstddef>
>>
>> int floodfill4(
>> unsigned char *grey,
>> int width, int height,
>> int x, int y,
>> unsigned char target, unsigned char dest)
>> {
>> if (width < 1 || height < 1)
>> return 0;
>> if (x < 0 || x >= width || y < 0 || y >= height)
>> return 0;
>>
>> size_t w = width, h = height;
>> if (grey[y*w+x] != target)
>> return 0;
>>
>> auto core = [=] (auto& a_ref, size_t x, size_t y) {
>> if (x >= w || y >= h)
>> return;
>> auto idx = y*w+x;
>> if (grey[idx] == target) {
>> grey[idx] = dest;
>> a_ref(a_ref, x - 1, y);
>> a_ref(a_ref, x + 1, y);
>> a_ref(a_ref, x, y - 1);
>> a_ref(a_ref, x, y + 1);
>> }
>> };
>> core(core, x, y);
>> return 1;
>> }
>>
>> // end of floodfill_recursive_lambda.
>>
>>
>> In the second variant in order to make it compile at all I had to
>> uses very dirty trick with lambda passed as parameter to itself.
>> I copied it from Stack Overflow, but don't pretend to understand
>> why it works and why it needed in the first place.
>
> For this part the answer is simple. The lambda only captures
> names that are defined at its point of definition, and core is not
> defined until the end of the declarator which include the
> initialisation -- the lambda you are defining. The lambda stored
> in 'core', can't therefore refer to the name core because core was
> not defined before the lambda.
This explanation isn't right. In both C and C++ a declared name is
available as soon as its declarator is complete, and declarators are
complete before the following initializer (if any). The following code
compiles just fine:
#include <functional>
int revised_floodfill4(
unsigned char *const grey,
int width, int height,
int x, int y,
unsigned char target, unsigned char dest)
{
if (width < 1 || height < 1)
return 0;
if (x < 0 || x >= width || y < 0 || y >= height)
return 0;
const size_t w = width, h = height;
if (grey[y*w+x] != target)
return 0;
std::function<void(size_t, size_t)> core = [=]
(size_t x, size_t y) {
if (x >= w || y >= h)
return;
auto idx = y*w+x;
if (grey[idx] == target) {
grey[idx] = dest;
core(x - 1, y);
core(x + 1, y);
core(x, y - 1);
core(x, y + 1);
}
};
core(x, y);
return 1;
}
The problem with the earlier definition is not the use of 'core' in
the body of the lambda, but the 'auto' part of the declaration.
Because the type of 'core' is not yet known, it can't be used in the
body. Once that problem is fixed by giving 'core' a specific type,
there is no problem calling it recursively in the body of the
lambda.
[toc] | [prev] | [next] | [standalone]
| From | Bonita Montero <Bonita.Montero@gmail.com> |
|---|---|
| Date | 2024-09-26 19:32 +0200 |
| Message-ID | <vd45rf$97bj$1@raubtier-asyl.eternal-september.org> |
| In reply to | #120386 |
Why do you use a function<>-object where you don't need any runtime-polymorphism. Use a variable which gets a lambda assigned and the call is likely to be inlined.
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <tr.17687@z991.linuxsc.com> |
|---|---|
| Date | 2024-09-26 14:09 -0700 |
| Message-ID | <86ploq5dhm.fsf@linuxsc.com> |
| In reply to | #120329 |
Michael S <already5chosen@yahoo.com> writes:
[.. when or why to use lambdas? ..]
> Some time ago on comp.lang.c we had very long thread about
> floodfill4 algorithm (that both myself and TimR took more
> seriously than an issue deserves, but that's off topic).
Thank goodness for that. :)
> Today I coded two implementations of original brute-force
> recursive algorithm.
>
> // floodfill_recursive_nested.
> // Implementation is in none-tricky C++
> // Very similar to what can be done in C
>
> #include <cstddef>
>
> int floodfill4(
> unsigned char *grey,
> int width, int height,
> int x, int y,
> unsigned char target, unsigned char dest)
> {
> if (width < 1 || height < 1)
> return 0;
> if (x < 0 || x >= width || y < 0 || y >= height)
> return 0;
>
> size_t w = width, h = height;
> if (grey[y*w+x] != target)
> return 0;
>
> struct {
> unsigned char *grey;
> size_t width, height;
> unsigned char target, dest;
> void core(size_t x, size_t y) const
> {
> if (x < width && y < height) {
> auto idx = y*width+x;
> if (grey[idx] == target) {
> grey[idx] = dest;
> core(x - 1, y);
> core(x + 1, y);
> core(x, y - 1);
> core(x, y + 1);
> }
> }
> }
> } context = {
> .grey = grey,
> .width = w, .height = h,
> .target = target, .dest = dest,
> };
> context.core(x, y);
> return 1;
> }
>
> // end of floodfill_recursive_nested.
>
>
>
> // floodfill_recursive_lambda.
> // Implementation in tricky C++
> // C can not do it
>
> #include <cstddef>
>
> int floodfill4(
> unsigned char *grey,
> int width, int height,
> int x, int y,
> unsigned char target, unsigned char dest)
> {
> if (width < 1 || height < 1)
> return 0;
> if (x < 0 || x >= width || y < 0 || y >= height)
> return 0;
>
> size_t w = width, h = height;
> if (grey[y*w+x] != target)
> return 0;
>
> auto core = [=] (auto& a_ref, size_t x, size_t y) {
> if (x >= w || y >= h)
> return;
> auto idx = y*w+x;
> if (grey[idx] == target) {
> grey[idx] = dest;
> a_ref(a_ref, x - 1, y);
> a_ref(a_ref, x + 1, y);
> a_ref(a_ref, x, y - 1);
> a_ref(a_ref, x, y + 1);
> }
> };
> core(core, x, y);
> return 1;
> }
>
> // end of floodfill_recursive_lambda.
>
>
> In the second variant in order to make it compile at all I had to
> uses very dirty trick with lambda passed as parameter to itself.
> I copied it from Stack Overflow, but don't pretend to understand
> why it works and why it needed in the first place.
>
> But that's only part of the story.
> The other part is that the first variant is 1.2x faster.
The examples show two different ways of achieving the same goal.
However these two ways aren't that much different from each
other. To me it seems like they are both making the same
mistake, which is using a language feature to hide some context
that is better left unhidden. Here is a sketch to make that
comment more concrete:
int floodfill4(
unsigned char *grey,
int width, int height,
int x, int y,
unsigned char target, unsigned char dest)
{
/* possible early return */
typedef struct {
unsigned char *grey;
// ... etc
} Stuff;
Stuff stuff = { grey, width, height, /*...*/ };
void
core( Stuff *it, size_t x, size_t y ){
auto k = y*it->width + x;
/* possible early return */
it->grey[k] = it->dest;
core( it, x-1, y );
core( it, x+1, y );
core( it, x, y-1 );
core( it, x, y+1 );
}
core( &stuff, x, y )
return 1;
}
The code for core() looks basically the same except that in a few
places we need to say it->width instead of width, etc. There is
no particular meaning to the contents of the struct; all that's
been done is to disguise where the variables are coming from. I
don't see any compelling reason to do that in this situation.
Getting back to lambdas, I would say that there are two primary
uses for lambdas. One use is as a convenience function, local to
an outer function definition, where some small-scale processing
step is encapsulated rather than being replicated. The second use
is as a call-back function given as an argument to some outside
function, where there is state that the outside function doesn't
know about. The same kind of reasoning applies to member functions
in structs defined locally in the outer function. Neither of those
scenarios applies in the earlier example functions.
In situations where an interface needs a callback function, usually
specifying a lambda parameter means less work for the client of
the interface, and so that scenario would be a good one to explore.
[toc] | [prev] | [next] | [standalone]
| From | Michael S <already5chosen@yahoo.com> |
|---|---|
| Date | 2024-09-27 16:15 +0300 |
| Message-ID | <20240927161541.000036d8@yahoo.com> |
| In reply to | #120405 |
On Thu, 26 Sep 2024 14:09:09 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
> Michael S <already5chosen@yahoo.com> writes:
>
> [.. when or why to use lambdas? ..]
>
> > Some time ago on comp.lang.c we had very long thread about
> > floodfill4 algorithm (that both myself and TimR took more
> > seriously than an issue deserves, but that's off topic).
>
> Thank goodness for that. :)
>
> > Today I coded two implementations of original brute-force
> > recursive algorithm.
> >
> > // floodfill_recursive_nested.
> > // Implementation is in none-tricky C++
> > // Very similar to what can be done in C
> >
> > #include <cstddef>
> >
> > int floodfill4(
> > unsigned char *grey,
> > int width, int height,
> > int x, int y,
> > unsigned char target, unsigned char dest)
> > {
> > if (width < 1 || height < 1)
> > return 0;
> > if (x < 0 || x >= width || y < 0 || y >= height)
> > return 0;
> >
> > size_t w = width, h = height;
> > if (grey[y*w+x] != target)
> > return 0;
> >
> > struct {
> > unsigned char *grey;
> > size_t width, height;
> > unsigned char target, dest;
> > void core(size_t x, size_t y) const
> > {
> > if (x < width && y < height) {
> > auto idx = y*width+x;
> > if (grey[idx] == target) {
> > grey[idx] = dest;
> > core(x - 1, y);
> > core(x + 1, y);
> > core(x, y - 1);
> > core(x, y + 1);
> > }
> > }
> > }
> > } context = {
> > .grey = grey,
> > .width = w, .height = h,
> > .target = target, .dest = dest,
> > };
> > context.core(x, y);
> > return 1;
> > }
> >
> > // end of floodfill_recursive_nested.
> >
> >
> >
> > // floodfill_recursive_lambda.
> > // Implementation in tricky C++
> > // C can not do it
> >
> > #include <cstddef>
> >
> > int floodfill4(
> > unsigned char *grey,
> > int width, int height,
> > int x, int y,
> > unsigned char target, unsigned char dest)
> > {
> > if (width < 1 || height < 1)
> > return 0;
> > if (x < 0 || x >= width || y < 0 || y >= height)
> > return 0;
> >
> > size_t w = width, h = height;
> > if (grey[y*w+x] != target)
> > return 0;
> >
> > auto core = [=] (auto& a_ref, size_t x, size_t y) {
> > if (x >= w || y >= h)
> > return;
> > auto idx = y*w+x;
> > if (grey[idx] == target) {
> > grey[idx] = dest;
> > a_ref(a_ref, x - 1, y);
> > a_ref(a_ref, x + 1, y);
> > a_ref(a_ref, x, y - 1);
> > a_ref(a_ref, x, y + 1);
> > }
> > };
> > core(core, x, y);
> > return 1;
> > }
> >
> > // end of floodfill_recursive_lambda.
> >
> >
> > In the second variant in order to make it compile at all I had to
> > uses very dirty trick with lambda passed as parameter to itself.
> > I copied it from Stack Overflow, but don't pretend to understand
> > why it works and why it needed in the first place.
> >
> > But that's only part of the story.
> > The other part is that the first variant is 1.2x faster.
>
> The examples show two different ways of achieving the same goal.
> However these two ways aren't that much different from each
> other. To me it seems like they are both making the same
> mistake, which is using a language feature to hide some context
> that is better left unhidden. Here is a sketch to make that
> comment more concrete:
>
> int floodfill4(
> unsigned char *grey,
> int width, int height,
> int x, int y,
> unsigned char target, unsigned char dest)
> {
> /* possible early return */
>
> typedef struct {
> unsigned char *grey;
> // ... etc
> } Stuff;
>
> Stuff stuff = { grey, width, height, /*...*/ };
>
> void
> core( Stuff *it, size_t x, size_t y ){
> auto k = y*it->width + x;
> /* possible early return */
> it->grey[k] = it->dest;
> core( it, x-1, y );
> core( it, x+1, y );
> core( it, x, y-1 );
> core( it, x, y+1 );
> }
>
> core( &stuff, x, y )
> return 1;
> }
>
> The code for core() looks basically the same except that in a few
> places we need to say it->width instead of width, etc. There is
> no particular meaning to the contents of the struct; all that's
> been done is to disguise where the variables are coming from. I
> don't see any compelling reason to do that in this situation.
>
Well, the code above is neither legal standard C++ nor legal standard C.
But I understand what you mean and how to make it legal by extracting
core() out of floodfill4() body. More so, that's where I started.
In theory, it's should be exactly the same as variant with core() as
member function. In practice, compiler (gcc) optimizes recursion of
member function much better.
> Getting back to lambdas, I would say that there are two primary
> uses for lambdas. One use is as a convenience function, local to
> an outer function definition, where some small-scale processing
> step is encapsulated rather than being replicated. The second use
> is as a call-back function given as an argument to some outside
> function, where there is state that the outside function doesn't
> know about. The same kind of reasoning applies to member functions
> in structs defined locally in the outer function. Neither of those
> scenarios applies in the earlier example functions.
>
I don't disagree in theory. Practice is something else.
> In situations where an interface needs a callback function, usually
> specifying a lambda parameter means less work for the client of
> the interface, and so that scenario would be a good one to explore.
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <tr.17687@z991.linuxsc.com> |
|---|---|
| Date | 2024-09-28 04:25 -0700 |
| Message-ID | <86o7483tqh.fsf@linuxsc.com> |
| In reply to | #120412 |
Michael S <already5chosen@yahoo.com> writes: > On Thu, 26 Sep 2024 14:09:09 -0700 > Tim Rentsch <tr.17687@z991.linuxsc.com> wrote: > [...] >> Getting back to lambdas, I would say that there are two primary >> uses for lambdas. One use is as a convenience function, local to >> an outer function definition, where some small-scale processing >> step is encapsulated rather than being replicated. The second use >> is as a call-back function given as an argument to some outside >> function, where there is state that the outside function doesn't >> know about. The same kind of reasoning applies to member functions >> in structs defined locally in the outer function. Neither of those >> scenarios applies in the earlier example functions. > > I don't disagree in theory. Practice is something else. Could I ask you to expand on that answer? I'm not sure what it is you're agreeing with (or not disagreeing with), and also not sure what the reasons are for not agreeing. I confess to being completely lost about what you're getting at with the practice/theory distinction here.
[toc] | [prev] | [standalone]
Page 2 of 2 — ← Prev page 1 [2]
Back to top | Article view | comp.lang.c++
csiph-web