невозможно преобразовать ‘std :: function <void * ()> ’в‘ void * (*) (void *) ’ - PullRequest
0 голосов
/ 26 апреля 2018

Я новичок в использовании библиотеки <functional>, так что терпите меня. Следующий код не скомпилируется с этой ошибкой:

ошибка: невозможно преобразовать std::function<void*()> в void* (*)(void*) для аргумента 3 в int pthread_create(pthread_t*, const pthread_attr_t*, void* (*)(void*), void*).

Мне не нужно использовать <functional> в моем коде, но я решил, что посмотрю, есть ли решение, прежде чем отказаться от использования стандартных указателей на функции.

Вот что у меня есть:

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);
        }
    }
}

1 Ответ

0 голосов
/ 27 апреля 2018

Я полагаю, что есть два вида ответов, которые я могу дать на этот вопрос: я могу показать вам правильный способ сделать то, что вы пытаетесь сделать (начать поток) в C ++, или я могу показать вам, как сжать std::function объект в pthread_create. Первый имеет более практическое значение, а второй служит для объяснения того, как pthread_create обрабатывает контекст. В обоих есть значение, поэтому:

1. Как вызвать std::function<void*()> объект в новом потоке

Начиная с C ++ 11, стандартная библиотека содержит средства для многопоточности. В вашем случае у нас есть функция с возвращаемым значением, поэтому задача состоит в том, чтобы

  1. Вызовите функцию асинхронно, а
  2. Получите возвращаемое значение, когда оно нам понадобится.

Самый простой способ сделать это - использовать std::async и std::future<void*>. Рассмотрим этот простой пример для асинхронного вызова one :

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();

В вашем случае вы хотите вызывать более одной функции и получать возвращаемые значения на досуге, поэтому вы хотите где-то хранить объекты std::future<void*>. Как повезет, структура вашего кода может быть в основном сохранена:

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));
    }
}

Позже вы можете получить возвращаемые значения из signal_futures на досуге.

Обратите внимание, что если функции не возвращают значения, вы можете использовать std::thread вместо 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);
    }
}

Вместо .get() результата из будущего, вы в конечном итоге .join() потоков. Использование std::future<void> также сработает, так что на самом деле это дело вкуса.

2. Как заставить std::function<void*()> в pthread_create

Мы только что обсудили разумный способ решения проблемы, а теперь давайте посмотрим на грязные биты. Причина, по которой исходный код не компилируется, состоит в том, что pthread_create ожидает адрес памяти, указывающий на функцию (т. Е. Машинный код), которую она может выполнить, и что signal_handler не такой адрес памяти, а сложный объект с состоянием. pthread_create не может справиться с этим.

Однако: если вы посмотрите внимательно, вы заметите, что функция pthread_create ожидает, принимает аргумент void*, и что pthread_create принимает аргумент void* позже в своей подписи - этот последний аргумент передается функция, когда поток запущен. Если бы мы действительно хотели, мы могли бы использовать этот механизм, и на самом деле в системах на основе POSIX std::thread, как правило, сделает именно это для вас. Например:

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);

Обратите внимание, что я не рекомендую делать это таким образом. Это ужасно, и у него есть проблема, упомянутая в комментарии выше. std::thread и связанные с ним классы в стандартной библиотеке решают все эти проблемы за вас, поэтому нет необходимости заново изобретать это конкретное колесо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...