Upper limit on Class sizes

This is a skeleton of a program I'm writing where I am trying to eliminate as many global objects and variables as I can to try to make it more reusable. As is (with the commented parts of the Config class) it works and the root page of its web site can get the device "name" from the Config class. If I try to add any more items to my Config class, the lambda functions that I normally write for page handlers can no longer access ANY variables from the WebServer class, much less the Config class (all the methods are still callable).

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <wifiCreds.h>
#include <LittleFS.h>
#include <ESPAsyncWebServer.h>

class Config {
  public:
    Config();
    virtual ~Config() {};
    bool setName(char *val);
    struct {
      char name[32];
      //char ssid[32];
      //char pass[32];
      //char ntp[32];
    } host;
};

Config::Config() {
  LittleFS.begin();
}

bool Config::setName(char *val) {
  strlcpy(host.name, val, sizeof(host.name));
  return true;
}

class WebServer {
  public:
    WebServer(Config *conf);
    virtual ~WebServer() {};
    bool setup();
    char detail[32];
    void funcPage(AsyncWebServerRequest *request);
  private:
    AsyncWebServer *web;
    Config *config;
};

WebServer::WebServer(Config *conf) {
  web = new AsyncWebServer(80);
  config = conf;
}

bool WebServer::setup() {
  web->on("/", HTTP_GET, [this](AsyncWebServerRequest *request) {
    AsyncResponseStream *response = request->beginResponseStream("text/plain");
    response->setCode(200);
    response->print("The name passed to this instance was ");
    response->print(config->host.name);
    request->send(response);
  });
  
  web->on("/func", HTTP_GET, [this](AsyncWebServerRequest *r) { funcPage(r); });

  web->begin();

  return true;
}

void WebServer::funcPage(AsyncWebServerRequest *request) {
  AsyncResponseStream *response = request->beginResponseStream("text/plain");
  response->setCode(200);
  response->print("Alternate page to see that the device name is ");
  response->print(config->host.name);
  request->send(response);  
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  while (WiFi.isConnected() != 1) {
    delay(100);
  }
  Serial.print(F("Connected to "));
  Serial.println(WIFI_SSID);
  Serial.print(F("IP Address: "));
  Serial.println(WiFi.localIP());

  Config conf;

  conf.setName("Garage");
  WebServer srv(&conf);
  srv.setup();
}

void loop() {
  // put your main code here, to run repeatedly:
}

If I uncomment any other variables in the Config class, neither page will be able to access the value of host.name. Can anyone please educate me on what I'm doing wrong here?

The actual Config class is supposed to read a JSON file in the LittleFS partition, if it exists, and the actual WebServer class provides forms and ajax endpoints for changing/setting config items. When it boots, if there is no configuration it starts the SoftAP to allow one to put initial settings in to get it onto a network. Making Config global it all worked, but it's my understanding that writing it that way isn't "right."

It's also probably not a good idea to give your WebServer instance a pointer to a Config that is local to setup. Once setup ends that local variable is destroyed. The stuff it contained might sit there in memory for a while or it might get overwritten by something right away. It all depends on any little tiny change you make anywhere in your code. So it might seem to work sometimes when the memory ends up actually being intact, and then suddenly fail for no reason.

Either create that as a global variable, or make a copy of it in the class. But taking pointers to local data is almost always a bad idea.

The WebServer class is also really serving no function here other than wrapping calls to AsyncWebServer to set it up. As soon as setup ends that local srv instance goes out of scope so it can't possibly be doing anything.

You've basically got an AsyncWebServer instance sitting in the heap with configuration data that may or may not be where it thinks it is.

This really needs a redesign. The object is not to cram everything into the class.

Actually, those lambdas may have the same issue. I don't remember how exactly lambdas work in memory but I think you're leaving a dangling pointer there too. When srv dies at the end of setup those function pointers are pointing to a function that may or may not still be there. They're certainly trying to call code on an instance that no longer exists.

Thank you! I made global pointers for the classes and it works again. With that explanation I'm surprised it worked as well as it did when everything went out of scope.

I know it's hideous to look at but it's an idea I wanted to try out. Thank you, again!

You might be breaking the rules here as well. Libraries commonly have a .begin() function that you call in setup() due to the fact that when you create a global class variable, it gets created before the entire runtime environment is set up which is why you put class setup code inside .begin() rather than the constructor.