Reputation: 1210
I've got a function taking a void*
as a sole parameter, and also returning void*
: gpioThreadFunc_t
I've now got a variable of type track_t, which is basically an unsigned char
, and would like to call my method with its value (!) as a parameter. In turn, the method must make sure that the argument is really a track_t
(resp. unsigned char
) and use it.
I tried to call the method like this:
start_thread (Loop, (void *) &track);
…where Loop
is the name of the method, and track
is the (local) variable in question. When setting its value to 2, the method got 0 or 131 or whatever instead, but not 2.
Loop
is defined like this:
void *Loop (void *params);
Does anybody know how I can call my method and pass the necessary argument safely? Or should I forget about parameter passing and rely on a global (or instance) variable instead? Thank you.
Clarification: I am still working on my code, so this is NOT a request for debug. I'm asking this question just in order not to run into a pitfall.
Upvotes: 0
Views: 125
Reputation: 596703
Unless the thread actually needs to access the track
variable itself (to make alterations to it), then you should just pass the value of the track
variable rather than its address. The value of an unsigned char
easily fits inside the bits of a void*
pointer:
void* Loop(void *params) {
track_t track = static_cast<track_t>(reinterpret_cast<uintptr_t>(params));
...
}
...
track_t track = 2;
start_thread(Loop, reinterpret_cast<void*>(static_cast<uintptr_t>(track)));
Otherwise, you can dynamically allocate a track_t
to pass to the thread, and then free it before the thread exits:
void* Loop(void *params) {
std::unique_ptr<track_t> track(static_cast<track_t*>(params));
...
}
...
std::unique_ptr<track_t> track(new track_t(2));
if (start_thread(Loop, track.get()))
track.release();
Upvotes: 2
Reputation: 238381
Does anybody know how I can call my method and pass the necessary argument
I tried to call the method like this:
start_thread (Loop, (void *) &track);
What you tried would work assuming Loop
converts the void pointer back to track_t*
and assuming the pointed track
object is kept alive until it is no longer used.
The cast to void*
in your example is unnecessary though, because all object pointers implicitly convert to void*
.
How can I safely pass a ... void*
It is not possible to make the function that accepts void*
safe. It is inherently type-unsafe.
What you can do is write a wrapper template that is type safe, and which makes it unnecessary to call start_thread
directly. Example:
template<class T>
using fun_t = T*(T*);
template<auto fun_ptr, class T>
void start_thread_safer(T* params)
{
auto callback = [](void* vparams) -> void* {
return fun_ptr(static_cast<T*>(vparams));
};
start_thread(callback, params);
}
Now you could type-safely use:
track_t* Loop (track_t* params);
start_thread_safer<Loop>(&track);
There is an optimisation that allows one to avoid indirection: Since unsigned char
is guaranteed to fit within the memory of void*
, you can pass the object within that memory directly. With integers (such as unsigned char
), this is simple:
str::uintptr_t temp = track;
start_thread (Loop, reinterpret_cast<void*>(temp));
// in Loop
track_t track = reinterpret_cast<str::uintptr_t>(params);
If the passed type is not an integer, but is trivially copyable and fits within the pointer, following works as well:
void* params = nullptr;
static_assert(sizeof params >= sizeof track);
std::memcpy(¶ms, &track, sizeof track);
start_thread (Loop, params);
// in Loop
track_t track;
std::memcpy(&track, ¶ms, sizeof track);
This optimisation also improves safety because there is no longer any indirection, and thus you can no longer fail to keep a pointed parameter alive until its usage.
I'll leave it as an exercise to combine these approaches.
Upvotes: 2