Value (void (*) (void *))

Can you explain to me what it means (void (*)(void *))

in the following expression:

(void (*)(void *))pthread_mutex_unlock

      

+3


source to share


3 answers


The outer brackets are cast. The material inside these brackets is the type to be distinguished.

In this case, it is a function pointer (*)

, taking an argument void*

and returning void

(i.e. nothing).

It is used here to create pthread_mutex_unlock

that has a signature

int pthread_mutex_unlock(pthread_mutex_t*);

      

so that it can be used (like a callback) with something expecting a function like

void function(void*);

      



Note that the wisdom of this depends on the target platform. It would be safer (i.e. more portable) to wrap the function with another, correct signature

void pthread_mutex_unlock_wrapper(void *mutex)
{
    pthread_mutex_unlock((pthread_mutex_t*)mutex);
}

      

this casts the argument for pthread_mutex_unlock

and discards the return type. This avoids possible stack corruption due to the fact that the caller and caller have a different understanding of space requirements for arguments and return values ​​(although in practice this can rarely be a problem for ignored return values, it is better to be safe).

Footnote:

As a final note, since you tagged the question as C ++, you can replace the (pthread_mutex_t*)mutex

cast to a wrapper function static_cast<pthread_mutex_t*>(mutex)

that does the equivalent conversion, but can be more easily read and understood as a cast. If you are actually using C ++, you should prefer that these "C ++" styles apply everywhere, as they have well-defined semantics (i.e. there are restrictions on what you can static_cast

, what you can dynamic_cast

, etc. .) And they are easier to read when reading the code.

+4


source


This is a "C-style" passed to a function pointer type that takes an argument void*

and returns nothing (with undefined behavior due to mismatched return type) ....

Note that the type is pthread_mutex_unlock

:

int pthread_mutex_unlock(pthread_mutex_t *mutex); 

      

It was probably used to let some code that knew how to call a function that expects, which void*

actually makes callbacks for pthread_mutex_unlock

, which will work on most compilers, because the return value is int

usually just stored in the register for the caller, so ignoring this does not affect stack structure or unwinding, but some compilers may crash.




Example:

template <typename T>
class On_Destruction
{
  public:
    On_Destruction(void(*f)(T), T t)
      : f_(f), t_(t)
    { }

    ~On_Destruction() { f_(t_); }
  private:
    void (*f_)(void*);
    T t_;
};

...in some function...

    pthread_mutex_lock(my_mutex);
    On_Destruction<void*> guard((void(*)(void*))pthread_mutex_unlock,
                                (void*)&my_mutex);
    ...unlock when unwinding stack by return/break/throw etc...

      

There are much better ways to do this that don't have undefined behavior (due to bodgy casts) ... eg. using std::mutex

and std::lock_guard

.

+2


source


This line is not "self-contained"

As is, it tells you that a pointer to pthread_mutex_unlock () will be passed to:

a pointer to a function that points a pointer to something as an argument and returns nothing.

+1


source







All Articles