Calling member function by pointer - c++11

I'm developing on an ESP32 with vscode and the ESPAsyncWebServer and Wifi libraries.
I'm tring to make my own wifi manager, so I'd like to put some function in a class, but I've some trouble to point to member functions.
I have this definitions without class:
void onNotFound(AsyncWebServerRequest *request){
//Handle Unknown Request
request->send(404);
}
String processor(const String& var)
{
if(var == "HELLO_FROM_TEMPLATE")
return F("Hello world!");
return String;
}
I want to call them from a class witch is:
My_Wifi.h
class My_Wifi {
private:
Config *config;
DNSServer dnsServer;
AsyncWebServer server;
uint16_t serverPort = 80;
void onNotFound(AsyncWebServerRequest *request); <------
String processor(const String& var); <-----
void webServerSetup();
public:
My_Wifi();
void setup(uint16_t port);
void sendJsonDoneResponse(AsyncWebServerRequest *request);
};
My_Wifi.cpp
void My_Wifi::onNotFound(AsyncWebServerRequest *request) {...}
String My_Wifi::processor(const String& var) {...}
void My_Wifi::webServerSetup() {
this->dnsServer.start(53, "*", WiFi.softAPIP());
this->server.onNotFound(this->onNotFound); <------
this->server
.serveStatic("/wifi_settings.html", SPIFFS, "/wifi_settings.html")
.setTemplateProcessor(this->processor) <------
.setFilter(ON_STA_FILTER);
...
}
Obviously this it's only to call the function not to reference it.
How can I call a member function via pointer ?
Thanks for your time.
I tryed:
typedef void (My_Wifi::*onNotFoundFn)(AsyncWebServerRequest *request);
void My_Wifi::webServerSetup() {
this->dnsServer.start(53, "*", WiFi.softAPIP());
onNotFoundFn ptr = &My_Wifi::onNotFound;
this->server.onNotFound(*ptr); //this->server.onNotFound(ptr);
...
}

In order to call member functions, you'll need to supply the object the member function is supposed to be called upon and it should match
typedef std::function<String(const String&)> AwsTemplateProcessor;
Example using a lambda, capturing this:
.setTemplateProcessor([this](const String& str) { return processor(str); } )
A similar lambda for onNotFound which should match
typedef std::function<void(AsyncWebServerRequest *request)> ArRequestHandlerFunction;
would look like this:
server.onNotFound([this](AsyncWebServerRequest* r) { onNotFound(r); });
Since you do not actually use this in your onNotFound callback, you could make your current callback function static:
class My_Wifi {
private:
static void onNotFound(AsyncWebServerRequest *request);
and supply that without a lambda:
server.onNotFound(&My_Wifi::onNotFound);
Alternatively, do not create a member function at all. Just supply a lambda:
server.onNotFound([](AsyncWebServerRequest* request){ request->send(404); });

Related

C++ lambda callbacks

I am trying to make an HTTP class, and I want to use C++11 (not C++14 yet) callbacks via lambdas. I have 2 mockups available, the first one works... but looks ugly. The second one I am aiming, is not compiling (error at the end).
I cannot use std::function, as this is an embedded project, and that template generates a lot of code.
#include <cstring>
class HTTP
{
public:
void get1(const char* url, void* context, void (*callback)(void*, const char*) )
{
callback(context, "");
}
void get2(const char* url, void (*callback)(const char*) )
{
callback("");
}
};
void test()
{
int k;
HTTP http;
http.get1( "http://google.com", &k, [](void* context, const char* s){
int *k = (int*) context;
*k = strlen(s);
});
// this does not compile, looking for other alternatives
http.get2( "http://google.com", [&k](const char* s){
k = strlen(s);
});
}
Error from gcc (xtensa-esp32-elf-g++ (crosstool-NG crosstool-ng-1.22.0-80-g6c4433a) 5.2.0)
HttpRequests.cpp: In function 'void test()':
HttpRequests.cpp:29:6: error: no matching function for call to 'HTTP::get2(const char [18], test()::<lambda(const char*)>)'
});
^
HttpRequests.cpp:11:10: note: candidate: void HTTP::get2(const char*, void (*)(const char*))
void get2(const char* url, void (*callback)(const char*) )
^
HttpRequests.cpp:11:10: note: no known conversion for argument 2 from 'test()::<lambda(const char*)>' to 'void (*)(const char*)'
Lambdas without a capture list are compatible with function pointers, so your first lambda can be passed as an argument to get1(). However, lambdas with a capture list are not convertible to function pointers so it can not be passed to get2().
Lambdas with captures have state but functions can not have state, which is why such lambdas are not convertible to function pointers.
The most common way to have a function accept any lambda (or any callable object) is to use function templates:
class HTTP {
// ...
template <typename Callable>
void get1(const char* url, void* context, Callable callback)
{
callback(context, "");
}
template <typename Callable>
void get2(const char* url, Callable callback)
{
callback("");
}
}
Being function templates, code size might become an issue. If that's not acceptable, then keep your current functions and restrict yourself to never passing lambdas that use captures.

C++ Pass lambda to template parameter

How to pass lambda as template parameter.
For example this code
template<void (*callback)()>
void function() {
callback();
}
int main() {
function<[]() -> void { std::cout << "Hello world\n"; }>();
}
fails with error "invalid template argument for 'function', expected compile-time constant expression".
What I'm doing wrong.
Edit
I want to implement something like this
template<typename T,
T (*deserializer)(buffer *data),
void (*serializer)(T item, buffer *data)>
class Type {
public:
T item;
Type(T item) : item(item) {
}
Type(buffer *data) {
deserialize(data);
}
void serialize(buffer *data) {
serializer(item, data);
}
void deserialize(buffer *data) {
deserializer(data);
}
};
typedef Type<int, [](buffer* data) -> int { return -1; }, [](int item, buffer* data) -> void {}> IntType
typedef Type<long, [](buffer* data) -> long { return -1; }, [](long item, buffer* data) -> void {}> LongType
Lambdas in C++14, including their conversion to function pointers, are not constexpr.
In C++17, this is going to change. There are no stable compilers with that feature implemented that I'm aware of (if you find one, can you mention it in the comments below?).
At that point
constexpr auto tmp = []() -> void { std::cout << "Hello world\n"; };
function<+tmp>();
will definitely work. I am uncertain if
function<+[]() -> void { std::cout << "Hello world\n"; }>()
would work; there are some rules about lambdas in unevaluated contexts and inside template argument lists that may be separate from the constexpr lambda problem and may apply here.
We can hack it in C++14.
Create a template class that stores a static copy of a lambda and exposes a static function with the same signature (f_ptr) that calls that static copy of a lambda.
Instantiate it once globally with your lambda.
Pass a pointer to the f_ptr to your template.
So:
template<class L> struct stateless; // todo
template<class L> stateless<L> make_stateless(L l){return std::move(l);}
auto foo = make_stateless( []() -> void { std::cout << "Hello world\n"; } );
function< &foo::f_ptr >();
this is almost certainly not what you want.
The kind of template in the example does not take a type as a parameter, but rather a value. This value needs to be determinable at runtime, in order to instantiate the template, and the value of a lambda is not a compile time constant, so this method just wont do. The common way of sending functors to a function is:
template<typename Func>
void foo(Func&& f)
{
f();
}
And since you want a class template (please put such information in the question, not the comments), here is an example using classes:
#include <utility>
template<typename Func>
class MyClass
{
public:
MyClass(Func&& f) : f(f) {}
void Run() { f(); }
private:
Func f;
};
template<typename Func>
MyClass<Func> MakeMyClass(Func&& f)
{
return { std::forward<Func>(f) };
}
int main()
{
auto x = MakeMyClass( [](){} );
x.Run();
}

C++/CLI marshaling .NET delegate to native delegate

I am trying to pass a delegate with managed parameters to native code to be invoked. My code below runs ok, but the string output is garbage.
Native Class
Header
#pragma once
typedef void (* SegmentCreatedDelegate)(char** arg);
public class SampleClass
{
public:
SampleClass(void);
~SampleClass(void);
void DoWork(SegmentCreatedDelegate callback);
};
Code
SampleClass::SampleClass(void)
{
}
SampleClass::~SampleClass(void)
{
}
void SampleClass::DoWork(SegmentCreatedDelegate callback)
{
for(int x = 0; x< 10; x++)
{
char* myStr2 = "newsegment!";
callback(&myStr2);
}
}
Managed Class
Header
#pragma once
public ref class SampleClassNet
{
public:
delegate void SegmentCreatedDelegateNet(System::String^ arg);
SampleClassNet(void);
void DoWork(SegmentCreatedDelegateNet^ segmentCreatedCallback);
};
Code
SampleClassNet::SampleClassNet(void)
{
}
void SampleClassNet::DoWork(SegmentCreatedDelegateNet^ segmentCreatedCallback)
{
SampleClass* nativeClass = new SampleClass();
System::IntPtr pointer = System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(segmentCreatedCallback);
nativeClass->DoWork((SegmentCreatedDelegate)(void*)pointer);
System::GC::KeepAlive(segmentCreatedCallback);
}
This code runs fine with the follow c#.
var sampleClass = new SampleClassNet();
sampleClass.DoWork((Console.WriteLine));
Except I get the following output, instead of the expected 10 entries of "newsegment!".
(ÇÆX
(ÇÆX☺
(ÇÆX☻
(ÇÆX♥
(ÇÆX♦
(ÇÆX♣
(ÇÆX♠
(ÇÆX
(ÇÆX
(ÇÆX
Not exactly "newsegment!", but I am not sure why the marshaling is not working. Maybe I need I need some kind of "MarshalAs" attribute so that the System::String knows that I have 8-bit chars?
As mentioned in the comments, you should convert the char** to a String^. (Btw, why pass char**, not char*? String has a constructer taking char*, which might simplify things a lot.)
I haven't tried the following, but you might give it a try:
public ref class SampleClassNet {
private:
delegate void SegmentCreatedDelegateNative(char** str);
SegmentCreatedDelegateNet^ managedCallback;
SegmentCreatedDelegateNative^ nativeCallback;
void printString(char** string);
public:
delegate void SegmentCreatedDelegateNet(System::String^ arg);
SampleClassNet();
void DoWork(SegmentCreatedDelegateNet^ segmentCreatedCallback);
};
SampleClassNet::SampleClassNet() {
nativeCallback = printString;
}
void SampleClassNet::DoWork(SegmentCreatedDelegateNet^ segmentCreatedCallback) {
SampleClass* nativeClass = new SampleClass();
managedCallback = segmentCreatedCallback;
System::IntPtr pointer = System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(nativeCallback);
nativeClass->DoWork((SegmentCreatedDelegate)(void*)pointer);
}
void SampleClassNet::printString(char** string) {
if (this->managedCallback != nullptr) {
String^ str = gcnew String(*string);
managedCallback(str);
}
}
The basic idea is to use another delegate, SegmentCreatedDelegateNative, handed to the native class, and to call the actual managed delegate from the function associated with the wrapper.

C++ class member callback and external library

I would like to solve this issue about class member function callback.
Imagine you have a function from an external library (which cannot be modified!) like this:
void fortranFunction(int n, void udf(double*) );
I would like to pass as the udf function above a function member of an existing class. Please look at the following code:
// External function (tipically from a fortran library)
void fortranFunction(int n, void udf(double*) )
{
// do something
}
// User Defined Function (UDF)
void myUDF(double* a)
{
// do something
}
// Class containing the User Defined Function (UDF)
class myClass
{
public:
void classUDF(double* a)
{
// do something...
};
};
int main()
{
int n=1;
// The UDF to be supplied is myUDF
fortranFunction(n, myUDF);
// The UDF is the classUDF member function of a myClass object
myClass myClassObj;
fortranFunction(n, myClassObj.classUDF); // ERROR!!
}
The last line of the code above results in a compilation error, because you cannot declare the classUDF member function as a static function.
Do you know if it is possible to solve this issue?
Probably Boost libraries could help me, but I do not know how (please consider that fortranFunction cannot be modified because is from an external library).
Thanks a lot!
Alberto
I don't understand, why can't you declare classUDF as static like this
class myClass {
public:
static void classUDF(double *a) {
...
}
};
and then pass it like
fortranFunction(n, myClass::classUDF);
You might try that solution (a little bit hacky, but I think, it should work for you):
void fortranFunction(int n, void udf(double*))
{
double d = static_cast<double>(n);
udf(&d);
}
class myClass {
public:
void classUDF(double* a) {
}
};
#ifdef _MSC_VER
#define THREADLOCALSTATIC __declspec(thread) static
#define THREADLOCAL
#else
#define THREADLOCALSTATIC static ___thread
#define THREADLOCAL ___thread
#endif
struct _trampolinebase {
THREADLOCALSTATIC _trampolinebase* current_trampoline;
};
THREADLOCAL _trampolinebase* _trampolinebase::current_trampoline = 0;
#undef THREADLOCAL
#undef THREADLOCALSTATIC
template<class CBRET, class CBARG1, class T>
struct _trampoline1 : _trampolinebase
{
typedef CBRET (T::*CALLBACKFN)(CBARG1);
_trampoline1(T& target, CALLBACKFN& callback)
: callback_(callback)
, target_(target)
{
assert(current_trampoline == 0);
current_trampoline = this;
}
static CBRET callback(CBARG1 a1) {
_trampoline1* this_ = static_cast<_trampoline1*>(current_trampoline);
current_trampoline = 0;
return this_->trampoline(a1);
}
private:
CBRET trampoline(CBARG1 a1) {
return (target_.*callback_)(a1);
}
CALLBACKFN& callback_;
T& target_;
};
template<class FRET, class FARG1, class CBRET, class CBARG1, class T, class F>
FRET call1_1(T& target, CBRET (T::*callback)(CBARG1), F& fortranfunction, FARG1 a)
{
typedef typename _trampoline1<CBRET, CBARG1, T> trampoline;
trampoline t(target, callback);
return fortranFunction(a, trampoline::callback);
}
int main()
{
int n=1;
myClass myClassObj;
call1_1<void,int,void,double*>(myClassObj, &myClass::classUDF, fortranFunction, 1);
}
With the 'threadlocal' stuff, this will work in multithreaded calls, too. You may omit that, if you don't use a multithreaded environment. It also works with recursive calls (e.g. if the callback calls another fortran function).
This solution works only for one single argument plus callback for the fortran function and one single argument in the callback function itself, but you should be able to extend it easily. This is also, why I called it 'call1_1' (fortran function with 1 argument, callbackfunction with 1 argument). FRET is the return type of the fortran function, FARG1 the type of the first argument (int in this case). CBRET and CBARG are the same for the callback function.
Before the fortran function is actually called, the target object is stored within a global (thread-local) variable. The fortran function calls a static callback function, which finally calls your member function.
I invented the trampolinebase to instantiate the static member, I could also have used a global variable for that (but for some reason, I don't like global variables too much) ;-)

Callback parameter for lambdas and member functions

I have this function:
void func(boost::function<void(float)> cb){
//do something with cb()
}
It works with lambdas and functions.
But it does not allow me to pass a member function or a lambda defined in a member function.
I tried to cast something like this:
void class::memberFunc() {
void func((void(*)(float))([](float m){}));
}
But it seems like lambda is ignored at calls.
And no idea how to pass a member function too.
Given:
struct T {
T(int x) : x(x) {};
void foo() {
std::cout << x;
}
int x;
};
The object pointer is an implicit first parameter to functions, and this becomes explicit when dealing with boost::function.
You can "hide" it from func by binding it early:
void func(boost::function<void()> cb) {
cb();
}
int main() {
T t(42);
func(boost::bind(&T::foo, &t));
}
Or otherwise you can bind it late:
T t(42);
void func(boost::function<void(T*)> cb) {
cb(&t);
}
int main() {
func(boost::bind(&T::foo, _1));
}
See it working here.

Resources