progmem, struct and lambda function

I’m working on a Menu class that can work with data in SRAM or PROGMEM but hit an error I can’t figure out.

If a lambda function is used in the array of items it crashes when trying to call the function but using a named function works.

I’ve done a simplified example which replicates the issue I’m having.
So below the **yes** function works but using **[]() { Serial.println("No Fn"); }** causes a crash.

I might have missed something obvious or be trying something that can’t be done, still learning C++;

void setup() {
  Serial.begin(9600);
}

#define PGM

using mfn = void(*)();
struct item {
  char title[20];
  mfn fn;
};

void yes() {
  Serial.println("Yes Fn");
}

const item items[]
#ifdef PGM
PROGMEM
#endif
= {
  { "Yes Itm", yes  },
  { "No Itm", []() { Serial.println("No Fn"); } }
};

void loop() {
  for (int i = 0; i < 2; i++) {
#ifdef PGM
    Serial.println((const __FlashStringHelper*)&items[i].title);
    ((mfn)pgm_read_ptr(&items[i].fn))();
#else
    Serial.println(items[i].title);
    ((mfn)items[i].fn)();
#endif
  }
  delay(5000);
}

If you remove the #define PGM statement, and the #ifdef statements, and simply assume PROGMEM usage, you have:

const item items[] PROGMEM = {
  { "Yes Itm", yes  },
  { "No Itm", []() { Serial.println("No Fn"); } }
};

Now, I want to know how you think you are going to put that code in PROGMEM. And, how you intend to retrieve that data to execute it.

Your approach is dubious, you should not put code in progmem. Progmem is only useful for simple, constant types.

PaulS: If you remove the #define PGM statement, and the #ifdef statements, and simply assume PROGMEM usage, you have:

const item items[] PROGMEM = {
  { "Yes Itm", yes  },
  { "No Itm", []() { Serial.println("No Fn"); } }
};

Now, I want to know how you think you are going to put that code in PROGMEM. And, how you intend to retrieve that data to execute it.

No intention with this. I thought the lambda function would be split out by the compiler then a pointer to it saved in progmem. Didn't realise it would put the code in progmem too.

Danois90: Your approach is dubious, you should not put code in progmem. Progmem is only useful for simple, constant types.

PROGMEM == "Program Memory", no?

gfvalvo: PROGMEM == "Program Memory", no?

Yes, but the way that the compiler structures the code so that it can be stored in memory is different from how OP is trying to store stuff in program memory.

So…for whatever reason, you lambda function doesn’t even have an address that’s being stored at your table. Check out the dump for the details.

Let’s search for where your table information might be stored.

Contents of section .text:
 0000 0c944b00 0c947300 0c947300 0c947300  ..K...s...s...s.
 0010 0c947300 0c947300 0c947300 0c947300  ..s...s...s...s.
 0020 0c947300 0c947300 0c947300 0c947300  ..s...s...s...s.
 0030 0c947300 0c947300 0c947300 0c947300  ..s...s...s...s.
 0040 0c94ce01 0c947300 0c943e02 0c941802  ......s...>.....
 0050 0c947300 0c947300 0c947300 0c947300  ..s...s...s...s.
 0060 0c947300 0c947300 59657320 49746d00  ..s...s.Yes Itm.
 0070 00000000 00000000 00000000 bc014e6f  ..............No
 0080 2049746d 00000000 00000000 00000000   Itm............
 0090 00000000 70021124 1fbecfef d8e0debf  ....p..$........
 00a0 cdbf11e0 a0e0b1e0 e8eef6e0 02c00590  ................
 00b0 0d92a232 b107d9f7 21e0a2e2 b1e001c0  ...2....!.......
 00c0 1d92a83c b207e1f7 10e0cbe4 d0e004c0  ...<............
 00d0 2197fe01 0e946703 ca34d107 c9f70e94  !.....g..4......

Notice that 20 bytes after the start of “Yes Itm” (That’s how long you defined the string in your struct), we find this curious value: bc01. Numbers in AVR seem to be stored in Little Endian (least significant bytes first), so reverse the byte order to 01BC. This should be the pointer to your “yes” function. We can verify that by doubling it (2 bytes per pointer) and seeing what’s at that address in the dump:

00000378 <_Z3yesv>:
  return write(s.c_str(), s.length());
}

Fancy that, exactly what I said it would be. Now if we look 20 bytes after the start of “No Itm” and it’s… 0000. As a wise man once said, “Well there’s your problem!”.

Just don’t use a lambda function with a PROGMEM structure. It’s not necessary. Give it a name.

dump.txt (129 KB)