Функция-член pthread C ++

Возможный дубликат:
функция pthread из класса

У меня есть этот код, который я не могу скомпилировать из-за строки pthread_create:

void* gtk_functor::_threaded_run(void* win)
{
    Gtk::Window* w = static_cast<Gtk::Window*>(win);
    Gtk::Main::run(*w);
    delete w;
}

void gtk_functor::operator ()(Gtk::Window& win, bool threaded)
{
    if (threaded)
    {
        pthread_t t_num;
        pthread_create(&t_num, NULL, (void* (*)(void*))&gtk_functor::_threaded_run, static_cast<void*>(&win));
    }
    else
    {
        Gtk::Main::run(win);
    }
}

Эта строка gcc:

g++ -o main 'pkg-config --cflags --libs sqlite3 gtkmm-3.0' -lpthread main.cpp

в конце компилируется с этим выводом:

code/ui.cpp: In member function 'void ui::gtk_functor::operator()(Gtk::Window&, bool)':
code/ui.cpp:45:65: warning: converting from 'void* (ui::gtk_functor::*)(void*)' to 'void* (*)(void*)' [-Wpmf-conversions]

и, очевидно, код работает неправильно, я получаю sementation fault, когда if (threaded) поднимается.

Я знаю, что это с приведением, но я не знаю правильной формы передачи функции-члена в pthread_create. Какие-либо предложения?


person Haix64    schedule 17.07.2012    source источник
comment
Избавьтесь от слепка и сделайте _threaded_run статичным.   -  person ildjarn    schedule 17.07.2012
comment
gtk_functor::_threaded_run не является функцией. Это функция-член. Нет смысла запрашивать возможность вызова голой функции-члена. Должен существовать объект, функцию-член которого вы вызываете.   -  person Kerrek SB    schedule 17.07.2012
comment
см. также stackoverflow.com/q/1151582/1025391   -  person moooeeeep    schedule 17.07.2012
comment
Замечательный момент @moooeeeep, большое спасибо!   -  person Haix64    schedule 17.07.2012


Ответы (2)


Попробуйте сделать _threaded_run статическим. В шапке:

private:
  static void* _threaded_run(void*);

А в реализации:

void* gtk_functor::_threaded_run(void* win) {
  Gtk::Window* w = static_cast<Gtk::Window*>(win);
  Gtk::Main::run(*w);
  delete w;
}

Затем при создании потока:

pthread_create(&t_num, NULL, &gtk_functor::_threaded_run, static_cast<void*>(&win));
person Linuxios    schedule 17.07.2012
comment
Спасибо, это работает, просто заменив &gtk_functor._threaded_run на &gtk_functor::_threaded_run. Я искал это, хотя все еще настаиваю на своем комментарии к ответу Керрека С.Б., поскольку мы делаем C ++. Стандарт имеет решающее значение и, я думаю, также влияет на наше мышление. - person Haix64; 17.07.2012
comment
pthreads - это C library. Таким образом, он понимает только C указатели на функции. Передача ему указателя на статический метод подвержена ошибкам. Вы должны передавать ему только адрес функции, которая была объявлена ​​как extern "C" или скомпилирована исключительно компилятором C, а не компилятором C ++. В стандарте нет ничего, что гарантирует, что функции C ++ или статические методы имеют то же соглашение о вызовах, что и функции C. - person Martin York; 17.07.2012
comment
@LokiAstari: Ух ты! Верно. Могу ли я объявить статический метод с extern "C"? - person Linuxios; 17.07.2012
comment
Значит, @LokiAstari этот ответ неверен? - person moooeeeep; 17.07.2012
comment
@moooeeeep: Насколько мне известно, нет. Поскольку у статических функций нет объекта и, следовательно, нет this, они должны работать. В некоторых языках у статических методов есть this (обычно self на этих языках), где классы являются объектами (Ruby, JavaScript, Python и т. Д.). C ++ определенно не делает. - person Linuxios; 18.07.2012
comment
@Linuxios: НЕТ Это не имеет ничего общего с объектом. Проблема заключается в ABI, который используется при вызове функций. К счастью для авторов приведенного выше кода, они используют реализацию, в которой ABI одинаков для вызова функций C и статических методов. Но в стандарте на это нет гарантии. Тебе просто повезло. Он не переносится (если в последнем стандарте не было исправления) и, следовательно, в моей книге ошибочен. - person Martin York; 18.07.2012
comment
comment
См .: Точная ситуация, когда этот метод приводит к поломке на платформах, где ABI не соответствует: stackoverflow.com/a/5547236/ 14065 - person Martin York; 18.07.2012
comment
Согласно стандарту передача extern "C++" функции (например, статической функции-члена) в pthread_create не должна даже компилироваться, но это Ошибка G ++. Надеюсь, что однажды привязка POSIX-C ++ перегрузит pthread_create, и весь этот сломанный код станет действительным. - person Jonathan Wakely; 18.07.2012
comment
@JonathanWakely: Хорошо, но тогда у меня к вам вопрос. Могу ли я пометить статический метод как extern "C"? - person Linuxios; 18.07.2012
comment
@JonathanWakely: Спасибо за исправление кода. - person Linuxios; 18.07.2012
comment
Могу ли я пометить статический метод как extern "C" - нет, [dcl.link] / 4 говорит, что языковая привязка AC игнорируется при определении языковой связи имен членов класса и типа функции члена класса функции. См. Также CWG 13. - person Jonathan Wakely; 18.07.2012

Как предлагает @ildjarn, просто сделайте бесплатную функцию:

void * threaded_run(void * win)
{
    Gtk::Window * const w = static_cast<Gtk::Window*>(win);
    Gtk::Main::run(*w);
    delete w;
}

// ...

pthread_create(&t_num, NULL, threaded_run, &win);

Поскольку функция не зависит от состояния какого-либо конкретного gtk_functor объекта, нет смысла делать ее функцией-членом.


В гипотетическом другом мире, где вы действительно хотели бы, чтобы функция-член объекта вызывалась в отдельном потоке, вам нужно каким-то образом передать ссылку на объект для объекта, обычно через указатель void аргумента:

struct Foo
{
    void * run() { /* ... use state ... */ }

    /* ... state ... */
};

Foo x;
pthread_t pt;

// start a new execution context with x.run():
pthread_create(&pt, NULL, FooInvoker, &x);

extern "C" void * FooInvoker(void * p)
{
    return static_cast<Foo*>(p)->run();
}

Фактически, вы можете даже пожелать упаковать больше контекстной информации в некоторую вспомогательную структуру и передать пустой указатель на that функции вызова потока.

person Kerrek SB    schedule 17.07.2012
comment
разве не должно быть анонимного пространства имен вокруг бесплатной функции? - person moooeeeep; 17.07.2012
comment
ваше первое решение действительно работает. Тогда я считаю _threaded_run помощником для gtk_functor. Я не пробовал второе решение, хотя вижу, что оно сработает, но я думаю, что вместо того, чтобы полагаться на такие уловки, мы могли бы оставить наши коды временными и обновить их, как только std::thread будет полностью реализован в GCC. ? (Спасибо хоть) - person Haix64; 17.07.2012
comment
@ ai64: std::thread, однако, не поможет вам преодолеть фундаментальное концептуальное недоразумение, касающееся объектов и функций-членов! - person Kerrek SB; 17.07.2012
comment
То, что я вижу, было моей неправильной интерпретацией некоторых синтаксических чудес, которые я имел по поводу определения статического члена. Хотя я очень признателен за ваше предупреждение о возможной ловушке. Спасибо. - person Haix64; 17.07.2012
comment
Хорошо, кроме FookInvoker, следует объявить как extern "C", помните, что pthread_create () - это библиотека C, которая передает указатели на функции C ++, работа не гарантируется. - person Martin York; 17.07.2012
comment
@LokiAstari: Хороший вопрос, спасибо. - person Kerrek SB; 18.07.2012