1

Possible Duplicate:
pthread Function from a Class

I have this code that I can't get to compile because of the pthread_create line:

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

This gcc line:

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

does in the end compile with this output:

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]

and apparently the code doesn't work correctly, I get sementation fault when the if (threaded) is raised.

I know its with the cast, but I don't know the correct form of passing a member function into pthread_create. Any suggestions?

Community
  • 1
  • 1
Haix64
  • 806
  • 1
  • 13
  • 21
  • 1
    Get rid of the cast and make `_threaded_run` static. – ildjarn Jul 17 '12 at 18:35
  • 1
    `gtk_functor::_threaded_run` is not a function. It's a *member function*. It makes no sense to ask to be able to "call" a naked member function. There must be an **object** whose member function you call. – Kerrek SB Jul 17 '12 at 18:35
  • 3
    see also http://stackoverflow.com/q/1151582/1025391 – moooeeeep Jul 17 '12 at 19:07

2 Answers2

5

As @ildjarn suggests, just make a free function:

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

Since the function does not depend on the state of any particular gtk_functor object, there is no point in making it a member function.


In a hypothetical different world where you really would want an object's member function to be called in a separate thread, you need to pass the object reference for the object around somehow, usually via the argument void pointer:

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

In fact, you may even wish to package up more contextual information into some auxiliary structure and pass a void pointer to that to the thread invoker function.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • 1
    shouldn't there be an anonymous namespace around the free function? – moooeeeep Jul 17 '12 at 18:47
  • your first solution does work. I consider _threaded_run then a helper for gtk_functor. I didn't try the second solution though I see that it will work, but I think instead of relying on such tricks we might keep our codes temporary and upgrade as soon as `std::thread` is fully implemented in GCC. ? (Thanks though) – Haix64 Jul 17 '12 at 18:49
  • @ai64: `std::thread` does not help you around a fundamental conceptual misunderstanding concerning objects and member functions, though! – Kerrek SB Jul 17 '12 at 18:53
  • What I see was my wrong interpretation of some syntactical miunderstanding I had about static member definition. Though I greately appreciate your warning to me about the possible pitfall. Thank you. – Haix64 Jul 17 '12 at 19:02
  • 1
    Good apart from FookInvoker should be declared as `extern "C"` remember that pthread_create() is a C library passing it pointers to C++ functions is not guaranteed to work. – Martin York Jul 17 '12 at 19:28
  • @LokiAstari: Good point, thanks. – Kerrek SB Jul 17 '12 at 20:33
5

Try making _threaded_run static. In the header:

private:
  static void* _threaded_run(void*);

And in the implementation:

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

Then when creating the thread:

pthread_create(&t_num, NULL, &gtk_functor::_threaded_run, static_cast<void*>(&win));
Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
Linuxios
  • 34,849
  • 13
  • 91
  • 116
  • Thanks, it does work, just replacing `&gtk_functor._threaded_run` with `&gtk_functor::_threaded_run`. I was looking for this, though I do still insist on my comment on Kerrek SB's answer, since we're doing C++. The std matters critically and I think influences our mindset too. – Haix64 Jul 17 '12 at 18:55
  • 4
    pthreads is a `C library`. Thus it only understands `C` pointers to functions. Passing it a pointer to a static method is error prone. You should only pass it the address of a function that has been declared as `extern "C"` or is compiled solely by the C compiler not the C++ compiler. There is nothing in the standard that gurantees that C++ functions or static methods have the same calling convention as C functions. – Martin York Jul 17 '12 at 19:27
  • @LokiAstari: Doh! That's right. Can I declare a static method with `extern "C"`? – Linuxios Jul 17 '12 at 19:39
  • @LokiAstari is [this answer](http://stackoverflow.com/a/1151638/1025391) wrong, then? – moooeeeep Jul 17 '12 at 19:56
  • @moooeeeep: To the best of my knowledge, no. Because the static functions have no object, and therefore no `this`, they should work. In some languages static methods have a `this` (usually `self` in those languages) where classes are objects (Ruby, JavaScript, Python, etc.). C++ certainly doesn't. – Linuxios Jul 17 '12 at 20:03
  • @Linuxios: **NO** It has nothing to do with the object. The ABI that is used in calling functions is the problem. Fortunately for the writers of the above code they happen to be using an implementation where the ABI is the same for calling "C" functions and static methods. But there is no guarantee in the standard for this. You just happen to get lucky. It is non portable (unless there was a fix in the latest standard) and thus in my book wrong. – Martin York Jul 17 '12 at 20:53
  • See: http://stackoverflow.com/questions/6352280/pthread-create-error-in-c/6352434#6352434 – Martin York Jul 17 '12 at 20:57
  • See: For an exact situation where this technique causes it to break on platforms where the ABI do not conform: http://stackoverflow.com/a/5547236/14065 – Martin York Jul 17 '12 at 21:01
  • According to the standard passing an `extern "C++"` function (such as a static member function) to `pthread_create` shouldn't even compile, but it's a [G++ bug](http://gcc.gnu.org/bugzilla/show_bug.cgi?id=2316). Hopefully one day a POSIX-C++ binding will overload `pthread_create` making all that broken code out there become valid. – Jonathan Wakely Jul 18 '12 at 15:02
  • @JonathanWakely: Alright, but then I have a question for you. Can I mark a static method as `extern "C"`? – Linuxios Jul 18 '12 at 15:06
  • _Can I mark a static method as `extern "C"`_ -- no, [dcl.link]/4 says "A C language linkage is ignored in determining the language linkage of the names of class members and the function type of class member functions." See also [CWG 13](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#13). – Jonathan Wakely Jul 18 '12 at 15:56