1

My error:

error: cannot convert 'MainWindow::producerThreadFunction' from type 'void* (MainWindow::)(void*)' to type 'void* (*)(void*)'
     if (pthread_create (&producer, NULL, producerThreadFunction, NULL))
                                                                      ^

Header file:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QApplication>

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <iostream>
#include <QDebug>

class MainWindow : public QMainWindow
{
    Q_OBJECT

    pthread_mutex_t mutexVariable = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t  conditionVariable = PTHREAD_COND_INITIALIZER;

    QList <int> queueLIFO;

public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();

    //  This function is run by the thread `Producer`.
    void *producerThreadFunction (void *arg);

    //  This function is run by the thread `Consumer`.
    void *consumerThreadFunction (void *arg);

    int start ();
};

#endif // MAINWINDOW_H

Source file: (function where the error occurs)

int MainWindow :: start()
{
    pthread_t producer;
    pthread_t consumer;

    if (pthread_create (&producer, NULL, producerThreadFunction, NULL))
    {
        fprintf (stderr, "Error creating thread Producer\n");
        return 1;
    }
if (pthread_create (&consumer, NULL, consumerThreadFunction, NULL))
{
    fprintf (stderr, "Error creating thread Consumer\n");
    return 1;
}

if (pthread_join (producer, NULL))
{
    fprintf (stderr, "Error joining thread Producer\n");
    return 2;
}

if (pthread_join (consumer, NULL))
{
    fprintf (stderr, "Error joining thread Consumer\n");
    return 2;
}

return 0;

}

According to this thread, the solution is to make the producerThreadFunction static.

Why should the thread function of a class be made static to be accessible in the same class?

That function is the member function of that class. Why cannot I access it directly?

Community
  • 1
  • 1
CoffeeDay
  • 203
  • 1
  • 11
  • Just use standard C++ threads. – Cheers and hth. - Alf Dec 02 '15 at 07:14
  • 2
    Note while most compilers won't stop you from using a static member function as a "C" function, it's language binding is wrong. And in standard C++ it can't be declared `extern "C"`. I seem to recall that Sun's C++ compiler would warn about it by default. – Cheers and hth. - Alf Dec 02 '15 at 07:17
  • @Cheersandhth.-Alf Good catch - I didn't realize that. I did a little digging and found out that you are correct. I updated my answer accordingly. – Michael Koval Dec 02 '15 at 07:25
  • 1
    @MichaelKoval: I may have been wrong about not possible to declare as `extern "C"`. It appears to be possible for an out-of-class definition. Consequently I posted a [question about it](http://stackoverflow.com/questions/34037212/static-member-function-with-c-language-binding). – Cheers and hth. - Alf Dec 02 '15 at 07:39
  • @Cheersandhth.-Alf Thanks for opening the question - it's a very interesting discussion. It does appear that static member functions *cannot* be declared `extern "C"`. I updated my answer accordingly. – Michael Koval Dec 02 '15 at 17:42

2 Answers2

3

pthread_create expects a function pointer, not a pointer to a member function. These are very different types in C++ because member function pointers include an implicit this pointer. In practice, static member functions are equivalent to a non-member function, so that works fine (N.B. this is not technically true according to the standard - see below).

If pthread were a C++ library, it would likely take an std::function (or, before C++11, a boost::function) which can accept a variety of function-like objects; e.g. function pointer, member function pointers, or functor classes. However, since pthread is a C library, however, you're stuck writing a static function and manually passing the this pointer as an argument.

You should seriously consider using std::thread (or, before C++11, boost::thread) instead of pthreads. The same synchronization primitives are available as are available in pthreads, e.g. std::condition_variable. The std::thread constructor can directly accept a member function pointer:

std::thread producer(&MainWindow::producerThreadFunction, this);

The Standard

C and C++ may use different calling conventions. This means that it is not safe to call a C++ function from C code unless it is wrapped in an extern "C" block. However, as this answer on StackOverflow points out, C++11 7.5/4 "Linkage specifications" 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.

Therefore, it is not guaranteed to work by the standard. The only standards compliant option is to put the code in a non-member function which, internally, calls a member function:

extern "C" {

void producerThreadFunctionWrapper(void *arg)
{
    static_cast<MainWindow *>(arg)->producerThreadFunction();
}

} // extern "C"

// ...

pthread_create(&consumer, NULL, consumerThreadFunctionWrapper, this);

In Practice

In practice, I have never encountered an architecture/compiler where static member functions do not use the C linkage. The answer to question 33.2 in the C++FQA humorously makes the same point:

Regarding the static-members-as-callbacks issue: if your implementation uses different binary calling conventions for C functions and C++ static member functions, call the support and inform them that their developers consume mind-altering chemicals at work.

Caveat emptor, however, there are a few reports on StackOverflow (e.g. in 32 bit Visual Studio) of people being burned by this. The safest option is to use std::thread or write an extern "C" wrapper for your callbacks.

Community
  • 1
  • 1
Michael Koval
  • 8,207
  • 5
  • 42
  • 53
  • The answer is "because pthreads is a C library". Which is, for many reasons, a Good Thing. – paulsm4 Dec 02 '15 at 07:15
  • @paulsm4 I didn't mean to imply that `pthread` *should* be a C++ library - I am quite glad that it is written in C. However, it does make operations like this more verbose. :) – Michael Koval Dec 02 '15 at 07:17
  • @MichaelKoval: The answers to [my question](http://stackoverflow.com/questions/34037212/static-member-function-with-c-language-binding) indicate that the static member function will formally always have C++ language linkage, even with `extern "C"` (which is just ignored); hence even if using it as a C callback works in practice with all extant compilers, it's formally Wrong™. – Cheers and hth. - Alf Dec 02 '15 at 08:47
  • @Cheersandhth.-Alf, but would linkage matter when it is called by the pointer from the same translation unit? – SergeyA Dec 02 '15 at 14:55
  • 2
    @SergeyA: For calls from C++ no issue. But for calls from C: Formally, it could have a different machine code level calling convention, than expected by the C language code calling it. In practice I think doing things correctly here is mostly about avoiding warnings about the possible formal issue. So it's almost a non-issue, but still it's there. – Cheers and hth. - Alf Dec 02 '15 at 17:14
1

Since we are almost done with 2015, it is time to throw C++2003 away. The best way to call your thread is following:

int MainWindow :: start()
{
    std::thread producer(&MainWindow::producerThreadFunction, this);
    ...

See how easy it is? And no need to worry about static member functions at all!

SergeyA
  • 61,605
  • 5
  • 78
  • 137