ESP12E LoadProhibited exception accessing instance variable from lambda

I'm want to give logging functionality to a class ("UhServer" in the code below). To make it dynamic I want to pass a lambda, so that I could use some different logging stuff in the future.

So I made a little mockup with a lamda that just calls logger->log(); ("Logger" handles the logging by keep track of sinks and passing on log message). When I run this on my ESP12E just calling logger->log(); works fine but calling it from inside the lambda throws a "LoadProhibited" exception. It specifically happens when I try to access the instance variable "sinks". I tried to create everything dynamically instead of on the stack to check if it's a scope issue but that doesn't help either? I don't think I'm doing any really weird memory stuff so I'm kind of at a loss here...

#include <Arduino.h>
#include <cstddef>

const int baudRate = 115200;

using byte = unsigned char;

class LogSink {
  public:
  LogSink(){};

  void out(){};
};

class Logger {
  public:
  Logger() {
    sinks = new LogSink*[5];
  }

  virtual ~Logger() {
    delete[] sinks;
  }


  void log() {
    
    Serial.println("Crashing...");
    LogSink* sink = sinks[0];
    Serial.println("no crash");

    // Never true; tricks compiler into not deleting the line above
    if (sink != nullptr) {
      Serial.print("out");
      sink->out();
    }

    // Logging stuff would happen here
  };

  LogSink** sinks;
};

using LogHandler = std::function<void()>;

class UhServer {
  public:
  UhServer(LogHandler logHandler) : logHandler(logHandler) {};

  void testLog() {
    logHandler();
  };

  LogHandler& logHandler;
};

void setup() {
  Serial.begin(baudRate);

  Logger* logger = new Logger();

  UhServer uhServer([logger]() {
    logger->log();
  });

  // >>>> No crash here
  logger->log();

  // >>>> Crash here
  uhServer.testLog();
};

void loop() {
  delay(1000);
  Serial.print(".");
};

std::functions are not magic, they're just objects like any other object. The lifetime of the lambda ends when the constructor of UhServer ends.

UhServer only keeps a reference to the function, so it becomes invalid as soon as you exit the constructor.
The solution: save the function by value.

(Change LogHandler& logHandler; to LogHandler logHandler;

Pieter

Wow, that makes a lot of sense. I guess the part of the lambda that holds the instructions doesn't get marked off, but the captured variables do.

Thanks so much!