Solved need help with pointer to member function
-
@mzimmers said in need help with pointer to member function:
Yes?
Yes.
The only glitch is that I need to declare wifi as a global, but this is a small price to pay.
Actually, you don't. I've taken the liberty of looking up the documentation of
xTaskCreate
, which allows for context to be passed. So consider something like this:struct TaksParameters { Wifi wifi; QueueHandle_t queue; }; void callback(void * data) { TaskParameters & parameters = *reinterpret_cast<TaksParameters *>(data); // Pure C magic ;) }
Which you'd start as:
TaksParameters parameters; // Or in the heap, I didn't go that far in the docs xTaskCreate(callback, ..., ¶meters, ... /* More C goodness */);
-
What does this line do?
TaskParameters & parameters = *reinterpret_cast<TaksParameters *>(data);
I see that it's distinct from the "parameters" declared outside of the function, so I'm not sure what its purpose is.
-
void *
is the so called opaque pointer, it means you hold an address to memory, but it doesn't specify what kind of data resides in that memory. As the data passed toxTaskCreate
is ofTaskParameters
type, you know that your callback should be called with that data as argument. However, inside the function you must tell the compiler how to treat that data block, it doesn't know what is behind thevoid *
. As you do, you tell it: "interpret the address to nothing (void *
), as address toTaskParameters
and don't ask any questions".reinterpret_cast
doesn't expand to anything in the assembly (no instructions), it just tells the compiler that behind this particular address in memory there's an object of typeTaskParameters
. -
Thanks for the explanation. I was referring, though, to the declaration itself. I guess I don't understand what "TaskParameters & parameters" is, and how it relates to the structure created here:
TaksParameters parameters; // Or in the heap, I didn't go that far in the docs
-
Ah, this you can ignore, it's simply a syntactic sugar of sorts to dereference the object (the
*
before thereinterpret_cast
) to get an (l-value) reference. You can simply work with the pointer (and is probably better in this case, as I suspect you may be the one to free the memory in the end. I.e. you can simply substitute it with:TaskParameters * parameters = reinterpret_cast<TaksParameters *>(data);
Just converts the
void *
toTaksParameters *
. And thevoid *
points to aTaksParameters
objects as per thexTaskCreate
documentation, look at thepvParameters
description here.PS. And indeed, you should create the
TaskParameters
in the heap, not in the stack (as I conveniently missed in the docs), and delete the object at the end of your task. -
I can see that I'm doing a poor job of phrasing the question. Here's my understanding of your suggestion, in code:
struct TaskParams { Wifi wifi; QueueHandle_t queue; }; void wifiTaskWrapper(void * data) { TaskParams * params = reinterpret_cast<TaskParams *>(data); } void app_main() { ... TaskParams params; xTaskCreate(wifiTaskWrapper, "wifi_task", 4096, params, 5, &h); }
This obviously won't work, because the params structure in app_main() isn't initialized. How does the pointer you create end up pointing to this value?
-
@mzimmers said in need help with pointer to member function:
How does the pointer you create end up pointing to this value?
I specifically requested the address:
xTaskCreate(callback, ..., ¶meters, ... /* More C goodness */);
Notice the
&
?
ThenxTaskCreate
will callcallback
at the appropriate time with the pointer I passed it as an argument.However, consider the following updated/corrected suggestion:
struct TaskParams { Wifi wifi; QueueHandle_t queue; }; void callback(void * data) { TaskParams * params = reinterpret_cast<TaskParams *>(data); } void app_main() { ... TaskParams * params = new TaskParams(); //< As mentioned you need to create it in the heap. // Here it's your job to initialize it :) xTaskCreate(wifiTaskWrapper, "wifi_task", 4096, params, 5, &h); //< You pass it to `xTaskCreate`, which will pass it to your `wifiTaskWrapper` function when it calls it. }
-
OK, I'm getting this bit by bit. So, if my program looks like this:
struct TaskParams { Wifi wifi; QueueHandle_t q; }; void testTaskWrapper(void *data) { TaskParams *params = reinterpret_cast<TaskParams *>(data); } void app_main() { Wifi wifiObj; TaskParams *params = new TaskParams(); params->wifi = ???; xTaskCreate(testTaskWrapper, "wifi_task", 4096, params, 5, &wifiTaskHandle);
What is the correct syntax for the Wifi member of the params struct? And where do I point it to the routine wifi_task?
-
@mzimmers said in need help with pointer to member function:
What is the correct syntax for the Wifi member of the params struct?
Well, it's the
wifi
object itself. It will be created when you create theTaskParams
instance, but you could also create it in the heap:struct TaskParams { Wifi * wifi; QueueHandle_t q; }; void app_main() { TaskParams *params = new TaskParams(); params->wifi = new Wifi( ... ); // .... }
If it's copyable data type, you can also directly assign it:
struct TaskParams { Wifi wifi; QueueHandle_t q; }; TaskParams * params = new TaskParams(); Wifi wifiObj; params->wifi = wifiObj;
And where do I point it to the routine wifi_task?
You don't, you can call that method directly, you don't really need to store a pointer to member:
void testTaskWrapper(void *data) { TaskParams * params = reinterpret_cast<TaskParams *>(data); params->wifi->wifi_task(); }
The idea is to pack all the arguments you task function needs into one variable. To that end you could also use a
std::tuple
if you don't like the idea of defining your own struct; it'd work the same way. -
Well, that's some pretty fancy stuff. Thanks for the explanation(s)...this will be very useful.