Thread: How to understand the prototype of a function that is a parameter of another function

  1. #1
    Registered User
    Join Date
    Jul 2005
    Posts
    98

    How to understand the prototype of a function that is a parameter of another function

    On my Solaris 9 system, the prototype of the pthread_cleanup_push() function is, according to the man page:

    void pthread_cleanup_push(void (*handler, void *), void *arg);

    However, on my Linux 2.6 system, its prototype is:

    void pthread_cleanup_push(void (*routine) (void *), void *arg);

    I can understand the Linux prototype: the first parameter is a function which takes a single parameter of void * type and returns void. But I am not able to understand the Solaris prototype. How to parse this "void (*handler, void *)" thing? Is there any difference between the Linux prototype and the Solaris prototype? Which way is "better", e.g. based on ANSI standard?

    Also, if my "routine" takes as its parameter a pointer other than void *, do I need to cast it to "void *" before passing it to pthread_cleanup_push()?

  2. #2
    Registered User OnionKnight's Avatar
    Join Date
    Jan 2005
    Posts
    555
    You should go with the latter one, I have never seen that Solaris one before but all the function pointers I've run across have defined like the one your Linux system. I can't explain the phenomenon so I leave to the others.

  3. #3
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,656
    > according to the man page
    Looks like a typo to me, find the real one in the appropriate header file and then compare.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  4. #4
    Registered User
    Join Date
    Jul 2005
    Posts
    98
    1. I have this piece of test code:
    Code:
    #include <stdio.h>
    
    void callee(void *msg)
    {
      char *m = (char *)msg;
      printf("%s\n", m);  
    }
    void caller(void *(callee)(void *), void *arg)
    {
      printf("caller\n");
      callee(arg);
    }
    int main()
    {
      caller(callee, "caller calling callee");
      return 0;
    }
    % gcc -Wall testcall.c -o testcall
    testcall.c: In function `main':
    testcall.c:15: warning: passing arg 1 of `caller' from incompatible pointer type

    What did I do wrong? caller's definition says callee should be a function that takes a void * parameter and returns void. Isn't callee exactly that?

    2. Solaris' man page for pthread_cleanup_push() apperas to have a typo, as suggested by Salem. I wrote some code to test that "void (*handler, void *)," format and I got a syntax error. I will try to post this to a Solaris user forum to get this documentation bug confirmed. On the other hand, I checked the pthread.h file that contains the prototype of pthread_cleanup_push() :
    Code:
    void    __pthread_cleanup_push(void (*)(void *), void *, caddr_t, _cleanup_t *);
    typedef void (*_Voidfp)(void*); /* pointer to extern "C" function */
    #define pthread_cleanup_push(routine, args) { \
            _cleanup_t _cleanup_info; \
            __pthread_cleanup_push((_Voidfp)(routine), (void *)(args), \
                                    (caddr_t)_getfp(), &_cleanup_info);
    #define pthread_cleanup_pop(ex) \
            __pthread_cleanup_pop(ex, &_cleanup_info); \
    }
    Note that the type of the first parameter of __pthread_cleanup_push() is
    Code:
    void (*)(void *)
    How is it different from
    Code:
    void *(void *)
    ? Interestingly, if I add a prototype for caller():
    Code:
    void caller(void (*)(void *), void *);
    A bunch of syntax errors are resulted:
    % gcc -Wall testcall.c -o testcall
    testcall.c:11: error: conflicting types for 'caller'
    testcall.c:3: error: previous declaration of 'caller' was here
    testcall.c:11: error: conflicting types for 'caller'
    testcall.c:3: error: previous declaration of 'caller' was here
    But if I add it like this:
    Code:
    void caller(void *(void *), void *);
    it is fine. What is going on? I thought the first * means the parameter is a function, rather than the funtion returning a void *. Am I wrong?

  5. #5
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    Code:
    void caller(void *(callee)(void *), void *arg)
    Code:
    void caller(void (*callee)(void *), void *arg)
    Spot the sublte difference.


    Quzah.
    Hope is the first step on the road to disappointment.

  6. #6
    Registered User
    Join Date
    Jul 2005
    Posts
    98
    Thanks, quzah. The definition of my caller should have been:
    void caller(void (*callee)(void *), void *arg) { ... }
    One more question:
    The prototype of pthread_create() is
    int pthread_create(pthread_t *thread, pthread_attr_t *attr,
    void *(*start_routine)(void*),void *arg);
    If I have a function
    int my_func(struct my_struct *x) { ... }
    that I wish to pass as the third parameter of pthread_create(), how should I cast it?
    I tried
    ret = pthread_create(&tid, NULL, &((void *(*)(void *))my_func), y);
    but gcc says:
    invalid lvalue in unary `&'
    whereas
    1. if I remove the & from the third parameter, it compiles without error, and
    2. if I define my_func as
    void *my_func(void *xx)
    { struct my_struct *x = (struct my_struct *)xx; ... }
    and pass it to pthread_create():
    ret = pthread_create(&tid, NULL, &my_func, y);
    it compiles without error.
    Why does the '&' sometimes cause error and sometimes does not?

  7. #7
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    You don't need the address of your function. Just pass the function name itself. You're passing it something other than what it expects. Passing it a function that takes a structure and returns an integer is not the same thing as passing it a function that takes a void pointer and returns a void pointer. Two different things.


    Quzah.
    Hope is the first step on the road to disappointment.

  8. #8
    Registered User
    Join Date
    Jul 2005
    Posts
    98

    "Two different things."

    1. int my_func(struct my_struct *x) { ... }
    ret = pthread_create(&tid, NULL, (void *(*)(void *))my_func, y);
    2. void *my_func(void *xx) { struct my_struct *x = (struct my_struct *)xx; ... }
    ret = pthread_create(&tid, NULL, my_func, y);
    What is the difference between the two and what is the significance of the differences? Thx.

  9. #9
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    One is a function pointer you're typecasting the ........ out of, the other is one you're not.


    Quzah.
    Hope is the first step on the road to disappointment.

  10. #10
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,656
    > ret = pthread_create(&tid, NULL, &((void *(*)(void *))my_func), y);
    If your function is prototyped properly, then it's simply the name of the function, nothing added, nothing taken away (just like when you pass any other variable to a function).

    ret = pthread_create(&tid, NULL, my_func, y);
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  11. #11
    Registered User
    Join Date
    Jul 2005
    Posts
    98
    Quote Originally Posted by Salem
    > If your function is prototyped properly, ...
    What is proper depends on your viewpoint. Let's say my_func(), in addition to being passed as a parameter of pthread_create(), is also called by some other function, and in the latter case, it is proper to have a "struct my_struct *" parameter and return an int. Now, one can declare two functions, one being
    int my_func1(struct my_struct *x)
    the other being
    void *my_func2(void *xx)
    that basically performs the same thing. I don't think that's desirable. If I have to choose one, I would choose the first one, and cast it to the type acceptable to pthread_create().

    Granted there are differences between 1. and 2. in my preceding post, but are the differences of any practical consequence at all?

  12. #12
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,656
    > What is proper depends on your viewpoint
    Er - no actually it doesn't.

    If your function has the wrong signature, and you force it through using some cast, then there's a chance it isn't going to work at all.

    > Let's say my_func(), in addition to being passed as a parameter of pthread_create(), is also called by some other function,
    OK, so you do it like this
    Code:
    int my_func1(struct my_struct *x) {
      // whole bunch of useful work
      return result;
    }
    
    // A thread-able wrapper around the useful function.
    void *my_func2(void *xx)  {
      (void)my_func1(xx); // void* to type* conversion is fine
      return NULL;
    }
    Just an eentsy tiny function to make a wrapper round your main code and the whole casting problem goes away.

    > but are the differences of any practical consequence at all?
    Depends on how long you plan on being lucky.
    A lot of stuff works despite peoples best efforts to break things.
    Yet at other times, even the tiniest screw-up will crash.

    It's all about attitude, do you want to do the best you can or play fast and loose and see what you can get away with?
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  13. #13
    Registered User
    Join Date
    Jul 2005
    Posts
    98
    Assumed my_func() is called exclusively by pthread_create(). Which one of the following is better?
    Code:
    int my_func1(struct my_struct *x) {
      // whole bunch of useful work
      return result;
    }
    
    // A thread-able wrapper around the useful function.
    void *my_func2(void *xx)  {
      (void)my_func1(xx); // void* to type* conversion is fine
      return NULL;
    }
    or
    Code:
    void *my_func(void *xx) {
      struct my_struct *x = (struct my_struct *)xx;
      // whole bunch of useful work
      return NULL;
    }

  14. #14
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,656
    If it's only called from a thread, your second one is fine.
    The cast isn't necessary because xx is void*
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Beginner Needs help in Dev-C++
    By Korrupt Lawz in forum C++ Programming
    Replies: 20
    Last Post: 09-28-2010, 01:17 AM
  2. Be confused with one function prototype
    By chings in forum C Programming
    Replies: 3
    Last Post: 05-12-2009, 11:09 PM
  3. Getting an error with OpenGL: collect2: ld returned 1 exit status
    By Lorgon Jortle in forum C++ Programming
    Replies: 6
    Last Post: 05-08-2009, 08:18 PM
  4. We Got _DEBUG Errors
    By Tonto in forum Windows Programming
    Replies: 5
    Last Post: 12-22-2006, 05:45 PM
  5. call to function without prototype
    By lgbeeb in forum C Programming
    Replies: 2
    Last Post: 03-25-2003, 12:20 PM