Reputation: 33
I'm new to using the <functional>
library, so bear with me. The following code is failing to compile, with this error:
error: cannot convert
std::function<void*()>
tovoid* (*)(void*)
for argument3
toint pthread_create(pthread_t*, const pthread_attr_t*, void* (*)(void*), void*)
.
I don't have to use <functional>
in my code, but I figured I'd see if there's a solution before giving up and using standard function pointers.
Here's what I have:
vector< function< void*() > > signals;
vector< pthread_t > signal_threads;
// Master Signal Handler
void MasterSignalHandler()
{
for (auto & signal_handler : signals)
{
// Create a thread handle
pthread_t thread_handle;
// Spawn a thread for the signal handler
if (0 == pthread_create(&thread_handle, NULL, signal_handler, NULL))
{
// Push back the thread handle to the signal thread vector
signal_threads.push_back(thread_handle);
}
}
}
Upvotes: 3
Views: 3588
Reputation: 44023
I suppose there are two kinds of answers I can give to this one: I can show you the proper way to do what you're trying to do (start a thread) in C++, or I can show you how to squeeze a std::function
object into pthread_create
. The first has more practical value, while the second serves to explain how pthread_create
handles context. There's value in both, so:
std::function<void*()>
object in a new threadSince C++11, the standard library contains facilities for multithreading. In your case, we have a function with a return value, so the task is to
The most straightforward way to do this is to use std::async
and std::future<void*>
. Consider this simple example to make one call asynchronously:
std::function<void*()> f; // the function to call
// Call f asynchronously. It is possible to omit the std::launch::async
// parameter, in which case it is up to the implementation when or even
// if a thread is spawned to make the call.
// The future object represents a handle to the void* value that f will
// eventually return (i.e., the future value).
std::future<void*> fut = std::async(std::launch::async, f);
// Do some other stuff while we're waiting
// get f's return value. This will wait if the thread is not yet finished.
void *value_returned_by_f = fut.get();
In your case, you want to call more than one function and get the return values at your leisure, so you want to store the std::future<void*>
objects somewhere. As luck would have it, the structure of your code can be mostly preserved:
vector<function<void*()>> signals;
vector<std::future<void*>> signal_futures;
// Master Signal Handler
void MasterSignalHandler()
{
for (auto & signal_handler : signals)
{
signal_futures.push_back(std::async(std::launch::async, signal_handler));
}
}
You can then later retrieve the return values from signal_futures
at your leisure.
Note that if the functions did not return values, you could use std::thread
instead of std::future
:
vector<function<void()>> signals;
vector<std::thread> signal_threads;
// Master Signal Handler
void MasterSignalHandler()
{
for (auto & signal_handler : signals)
{
signal_threads.emplace_back(signal_handler);
}
}
Instead of .get()
ing the result from the future, you would then eventually .join()
the threads. Using std::future<void>
would also work, so this is mostly a matter of taste, really.
std::function<void*()>
into pthread_create
We've just discussed the sensible way to go about the problem, now let's take a look at the dirty bits. The reason your original code does not compile is that pthread_create
expects a memory address pointing to a function (i.e., machine code) that it can execute, and that signal_handler
is not such a memory address but a complex object with state. pthread_create
cannot handle it.
However: if you look carefully, you will note that the function pthread_create
expects takes a void*
argument, and that pthread_create
takes a void*
argument later in its signature -- this latter argument is passed to the function when the thread is started. If we really wanted to, we could use this mechanism, and in fact on POSIX-based systems, std::thread
will generally do exactly this for you. For example:
void *thread_stub(void *context) {
// context is static_cast<void*>(&f) below. We reverse the cast to
// void* and call the std::function object.
std::function<void*()> &func = *static_cast<std::function<void*()>*>(context);
return func();
}
// ...
std::function<void*()> f;
// IMPORTANT: Used this way, f must not go out of scope while the thread
// is running because the thread holds a pointer to it!
pthread_t thread_handle;
pthread_create(&thread_handle, NULL, thread_stub, &f);
Note that I do not recommend doing it this way. It is ugly, and it has the problem mentioned in the comment above. std::thread
and its associated classes in the standard library solve all these issues for you, so there's no need to reinvent this particular wheel.
Upvotes: 7