Hi,
Here's a snippet from ESP8266WebServer.h:
typedef std::function<void(void)> THandlerFunction;
void on(const char* uri, THandlerFunction handler);
void on(const char* uri, HTTPMethod method, THandlerFunction fn);
void on(const char* uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn);
Is there a reason why it's not defined like this instead:
typedef std::function<void(void*)> THandlerFunction
void on(const char* uri, THandlerFunction handler, void *user_ctx);
void on(const char* uri, HTTPMethod method, THandlerFunction fn, void *user_ctx);
void on(const char* uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn, void *user_ctx);
If it was defined this way, we could register a user context variable along with the handler function.
At the moment, if I have a context I'd like to use in the handler function, I must put the context into a global variable (do you agree?). I don't like using globals if I don't have to.
What do you guys think?
I think void* user_ctx is so very C.
How about doing what you need in a C++-way?
void handler(SomeParam param)
{
}
// later...
SomeParam myParam;
server.on("/", HTTP_GET, std::bind(&handler, myParam));
or better yet:
class MyClass {
public:
MyClass(ESP8266WebServer& server) : m_server(server)
{
}
void begin(const char* uri)
{
m_server.on(uri, HTTP_GET, std::bind(&MyClass::handler, this));
}
protected:
void handler()
{
// do something
}
ESP8266WebServer& m_server;
};
@ronlivne are you okay with this solution? Can we close this?
Hi,
Yeah, I'm fine with it.
Thanks,
Ron
On Fri, Mar 11, 2016 at 4:13 PM, Ivan Grokhotkov [email protected]
wrote:
@ronlivne https://github.com/ronlivne are you okay with this solution?
Can we close this?—
Reply to this email directly or view it on GitHub
https://github.com/esp8266/Arduino/issues/1711#issuecomment-195381891.
@igrr thanks for the first hint to solve at least one problem I have.
So this now works fine for me:
server.on("/login", std::bind(&EspDevice::handleLogin, this));
Do you also have a solution for the other function for use within own classes:
void ESP8266WebServer::on(const char* uri, HTTPMethod method, ESP8266WebServer::THandlerFunction fn, ESP8266WebServer::THandlerFunction ufn) { ...
server.on("/edit", HTTP_POST, [](){ server.send(200, "text/plain", ""); }, handleFileUpload);
this doesn't compile:
server.on("/edit", HTTP_POST,
[](){ server.send(200, "text/plain", ""); },
std::bind(&EspDevice::handleFileUpload, this));
nor this:
server.on("/edit", HTTP_POST,
std::bind(&EspDevice::renderPage, this),
std::bind(&EspDevice::handleFileUpload, this));
with
void EspDevice::renderPage(){
server.send(200, "text/plain", "");
}
Is to late but the solution is put in the second argument '&'
server.on("/edit", HTTP_POST, [ & ](){ server.send(200, "text/plain", ""); }, handleFileUpload);
This issue was closed based on the acceptance of the proposed solution. However, I was not able to make this proposed solution work:
server.on("/", HTTP_GET, std::bind(&handler, myParam));
or using alternative notation
server.on("/", HTTP_ANY, [this](MyParam *myParam) { std::bind(&handleSitemap, myParam);));
using the master branch version.
error: 'myParam' is not captured
Looking at source code, it looks like the capability was not (yet) implemented. Did I miss something?
Thanks in advance.
My bad, lack of c++ 11 skills. It is 100% working on both 2.5.2 and git master. If your callback function is part of a class, it requires the proper std::bind syntax:
struct Sitemap {
const char *name;
};
void MyClass::handleSitemap(Sitemap* sm) {
DbgPrintln("Sitemap name", sm->name);
}
Sitemap sm;
sm.name = "test";
server.on("/rest/sitemaps", std::bind(&MyClass::handleSitemap, this, &sm));
When defined outside of a class, one can use a simpler notation:
std::bind(handleSitemap, &sm)
but inside a class, the function address of _handleSitemap_ is relative to the _MyClass_ instance referred to by _this_.
Another simple way to test the call back code is using:
auto fn = bind(&MyClass ::handleSitemap, this, &sm);
fn();
which is the equivalent of calling
MyClass myclass;
myclass.handleSitemap(&sm);
but represented by a variable _fn_ of type "_std::function
Most helpful comment
I think
void* user_ctxis so very C.How about doing what you need in a C++-way?
or better yet: