Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Meaning of (void (*)(void *))

Can you explain me what is the meaning of (void (*)(void *)) in the following expression:

(void (*)(void *))pthread_mutex_unlock
like image 714
Konstantin Avatar asked May 07 '15 08:05

Konstantin


2 Answers

The outer brackets represent a cast. The stuff inside those brackets is the type to cast to.

In this case, it is a pointer to a function (*), taking a void* argument, and returning void (i.e. nothing).

It is used here to cast pthread_mutex_unlock, which has the signature

int pthread_mutex_unlock(pthread_mutex_t*);

such that it can be used (for example, as a callback) with something that expects a function of the type

void function(void*);

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

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

this performs a cast to the argument type for pthread_mutex_unlock and discards the return type. This avoids the potential for stack corruption as a result of the caller and callee having a different understanding of the space requirements for arguments and return values (although in practice this may rarely be an issue for ignored return values, it is still better to be safe).

Endnote:

As a final note, since you tagged the question as C++, you could replace the (pthread_mutex_t*)mutex cast in the wrapper function with static_cast<pthread_mutex_t*>(mutex), which performs an equivalent conversion, but can be more easily read and understood to be a cast. If you're genuinely using C++, you should prefer these "C++ style" casts everywhere, since they have clearly defined semantics (i.e. there are limits to what you can static_cast, what you can dynamic_cast, etc.) and they are easier to spot when reading code.

like image 182
Andrew Avatar answered Oct 29 '22 17:10

Andrew


It's a "C-style" cast to the type pointer-to-function taking a void* argument and not returning anything (with undefined behaviour due to the return type mismatch)....

Note that the type of pthread_mutex_unlock itself is:

int pthread_mutex_unlock(pthread_mutex_t *mutex); 

It was probably used let some code that knew how to call a function expecting a void* actually make callbacks to pthread_mutex_unlock, which will work on most compilers because the int return value is typically just kept in a register for the caller so ignoring it doesn't affect the stack layout or unwinding, but some compilers might 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 behaviour (due to bodgy casts)... e.g. using std::mutex and std::lock_guard.

like image 36
Tony Delroy Avatar answered Oct 29 '22 18:10

Tony Delroy