greg
greg

Reputation: 33

cannot convert ‘std::function<void*()>’ to ‘void* (*)(void*)’

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*()> to void* (*)(void*) for argument 3 to int 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

Answers (1)

Wintermute
Wintermute

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:

1. How to call a std::function<void*()> object in a new thread

Since 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

  1. Call the function asynchronously, and
  2. Get the return value when we need it.

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.

2. How to force 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

Related Questions