Obscure Arduino tips #2

This one may not be so obscure if you're a competent C++ programmer, but if you're somebody just finding their way in writing their own Arduino libraries it can be a major roadblock.

It is not uncommon for libraries that allow you to do work triggered by external events or that happen asynchronously to use callback functions. Your code will work fine but once you start turning that sketch into into a C++ class as a library, you won't be able to compile it.

The example I'll use is the ESP8266 WiFi scanning class, as this is where I encountered the problem.

While my code was a monolithic Arduino sketch I could kick off a scan with code like this.
WiFi.scanNetworksAsync(myCallbackFunction,true);
Where 'myCallbackFunction' is just the name of the callback function in my sketch. This is nice and easy.

Once you create a class, you can in principle have multiple instances of that class. Not to get into the detail of C++ classes (because I'm not a real C++ programmer) but when you need to refer to the specific instance of a class there is a 'magic' extra parameter 'this'.

A lot of the time you can ignore 'this' as a naive Arduino programmer when you make a library but you will bash up against it eventually, probably when you go to use a callback or function from another library.

You can use one of the standard C++ functional adaptors to help you, std::bind, which allows you to create a new function referring to the old, but change the arguments to the function. This allows you to pass 'this' into the callback without changing your original function at all.

So the code now looks like this.
WiFi.scanNetworksAsync(std::bind(&MyClass::myCallbackFunction,this,std::placeholders::_1),true);
The callback function in WiFi.scanNetworksAsync is passed an integer telling it how many networks were found and to make sure that is there in the new function it needs a placeholder 'std::placeholders::_1'.

For every argument in your original function you need a 'std::placeholder::_X' argument. So add extra parameters of 'std::placeholders::_2', 'std::placeholders::_3' etc. etc.

That's it. Unless you're actually trying to use a C library when it gets more convoluted.

No comments: