me-no-dev / ESPAsyncWebServer

Async Web Server for ESP8266 and ESP32
3.6k stars 1.17k forks source link

Passing functions to .on() methods #1278

Open federico77 opened 1 year ago

federico77 commented 1 year ago

Hello guys, I am trying to solve a puzzle with my API webroutes and AsyncWebserver. My idea is to use the typical method "on" to build the update route for my sensors.

this->webServer->on("^api\\/sensors\\/([0-9]+)$", HTTP_PUT, [](AsyncWebServerRequest *request) {
...
}, NULL, onUpdateSensor);

I am leaving the lambda empty and using the "onUpdateSensor" function to do my update magic. This has been working until I refactored the code and implemented most of my stuff in C++ classes.

Now the webserver is a pointer within my Network class (you can see it from "this->" in the code). Everything works fine if I put my code in the lambda but, as you can see, I have no way of gathering my data from there. So I have to work with the function onUpdateSensor that has uint8_t *data available. All good except from the fact that being within my Network class (remember this->) my code does not compile with the error:

invalid use of non-static member function 'void miniGreenhouse::Network::onUpdateSensor(AsyncWebServerRequest*, uint8_t*, size_t, size_t, size_t)'.

I have tried switching to AsyncCallbackJsonWebHandler but it has 2 other limits, leaving me, basically, stuck:

  1. can't solve the regex;
  2. can't use an explicit HTTP_PUT, HTTP_DELETE and other methods directly.

Any idea on how I can fix this?

MENIER commented 1 year ago

Hello i'm having about the same problem ([https://github.com/me-no-dev/ESPAsyncWebServer/issues/1270]), i try template but with no succes yet...Pass C function (extern function()) is ok and Make static function class is ok....

federico77 commented 1 year ago

Hi Menier, I am a bit of a noob at C++ so I tried to find an usable workaround with the few knowledge I have. What I did to temporarily overcome my issue was to use the following:

    this->webServer->on("^api\\/sensors\\/([0-9]+)$", HTTP_PUT, [](AsyncWebServerRequest *request) {
        ...
    }, NULL, [](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {
        **my data parsing code went here**
    });

This way I could use my current code with just a bit of copy and paste wrapping it in the bodyhandler function available for the server->on method. Sadly this is a lambda and I am not really in love with it...

If you happen to find more elegant ways, please post them here!

MENIER commented 1 year ago

Hi federico77 It's cleaner than make all static! a litle more prety one lambda: auto lambdaFunc = [] ( AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) { /*SomethingsToDo*/ }; this->onEvent(lambdaFunc); it's compiling for me:

this->onEvent([](
                AsyncWebSocket *server,
                AsyncWebSocketClient *client,
                AwsEventType type,
                void *arg,
                uint8_t *data,
                size_t len)
                {
                /*SomethingsToDo*/
                });

I'm hanging around std::function patching the code ESPAsyncWebServer with this but no result more:

//event listener
    void onEvent(AwsEventHandler handler){
      _eventHandler = handler;
    }
void onEventStd(std::function<void(
        AsyncWebSocket* ,
        AsyncWebSocketClient*,
        AwsEventType,
        void*,
        uint8_t *,
        size_t )>handler){
     _eventHandler = handler;
    }
MENIER commented 1 year ago

Using Lambda and this pointer:

auto lambda = [&] (
                            AsyncWebSocket *server,
                            AsyncWebSocketClient *client,
                            AwsEventType type,
                            void *arg,
                            uint8_t *data,
                            size_t len)
                            {
                            return this->onEventDerived(
                                    server,
                                    client,
                                    type,
                                    arg,
                                    data,
                                    len);
                            };
this->onEvent(lambda);```

//with mumber function:

void onEventDerived( AsyncWebSocket server, AsyncWebSocketClient client, AwsEventType type, void arg, uint8_t data, size_t len) {/Somethings/}

MacDada commented 1 year ago

I'm not sure what exactly you have a problem with, but my guess is that you want to use a function/variable from OUTSIDE the lambda.

If so, you have to "capture the context": https://learn.microsoft.com/en-us/cpp/cpp/lambda-expressions-in-cpp?view=msvc-170#capture-clause

stale[bot] commented 1 year ago

[STALE_SET] This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 14 days if no further activity occurs. Thank you for your contributions.