What’s the significance of a C function declaration in parentheses apparently forever calling itself?

What’s the significance of a C function declaration in parentheses apparently forever calling itself?

22

In gatomic.c of glib there are several function declarations that look like this:

gboolean
(g_atomic_int_compare_and_exchange_full) (gint *atomic,
                                          gint  oldval,
                                          gint  newval,
                                          gint *preval)
{
  return g_atomic_int_compare_and_exchange_full (atomic, oldval, newval, preval);
}

Can someone explain what this code exactly does? I’m confused by several things here:

  1. The function name g_atomic_int_compare_and_exchange_full is in
    parentheses. What’s the significance of this?

  2. The function’s body apparently consists of nothing but a call to the function itself so this will run forever and result in stack
    overflow (pun intended).

I can’t make any sense of this function declaration at all. What’s really going on here?

Share
Improve this question

3

  • 5

    Without having the full code, it is hard to tell. Putting a function name in brackets avoids any macro expansion in case there is a function like macro with the same name. That said, this could be a wrapper function for said macro. But that is just guessing.

    – Gerhardh

    23 hours ago

  • 2

    Full code is here: gitlab.gnome.org/GNOME/glib/-/blob/main/glib/gatomic.c

    – Andreas

    23 hours ago

  • 6

    One of my favorite things is when two apparent separate mysteries answer each other.

    – Daniel R. Collins

    12 hours ago

3 Answers
3

Reset to default

Highest score (default)

Trending (recent votes count more)

Date modified (newest first)

Date created (oldest first)

33

  1. The function name g_atomic_int_compare_and_exchange_full is in parentheses. What’s the significance of this?

Putting a function name in brackets avoids any macro expansion in case there is a function like macro with the same name.

That means, g_atomic_int_compare_and_exchange_full(...) will use the macro while (g_atomic_int_compare_and_exchange_full)(...) will use the function.

Why is this used?
You cannot assign a macro to a function pointer. In that case you can provide the definition as you saw it and then you can use

ptr = g_atomic_int_compare_and_exchange_full;

to use the function instead of the macro.

  1. The function’s body apparently consists of nothing but a call to the function itself so this will run forever and result in stack overflow (pun intended).

If you look into the associated header gatomic.h you will see that such a macro is indeed defined. And in the function body, no brackets are used around the function name. That means, the macro is used and it is not an infinite recursion.

Share
Improve this answer

0

17

Answer is given in the comments of the code you’ve linked.

The following is a collection of compiler macros to provide atomic access to integer and pointer-sized values.

And related to @Gerhardh comment, btw.

int (fn)(int param){
    fn(param);
}

Defines a function fn whose action is whatever macro fn expands too.

The parenthesis around the first occurrence of fn are there to avoid expansion of this one, which, obviously, would lead to inconsistent code.

Example

sqr.c

#define sqr(x) x*x
int (sqr)(int x){
   return sqr(x);
}

main.c

#include <stdio.h>
extern int sqr(int);
int main(){
    printf("%dn", sqr(12));
}

Compile with gcc -o main main.c sqr.c

Running ./main prints 144. Of course.

But more interestingly, main.c, after preprocessing looks like (gcc -E main.c)

extern int sqr(int);
int main(){
    printf("%dn", sqr(12));
}

(So, sqr is a function here. If it were a macro, it would have been expanded by now)

And sqr.c preprocessing gives

int (sqr)(int x){
   return x*x;
}

And that the main point: sqr is a function, whose code is the macro expansion of sqr(x).

Share
Improve this answer

9

There is a macro with the name g_atomic_int_compare_and_exchange_full defined in header gatomic.h:

#define g_atomic_int_compare_and_exchange_full(atomic, oldval, newval, preval) 
  (G_GNUC_EXTENSION ({                                                         
    G_STATIC_ASSERT (sizeof *(atomic) == sizeof (gint));                       
    G_STATIC_ASSERT (sizeof *(preval) == sizeof (gint));                       
    (void) (0 ? *(atomic) ^ (newval) ^ (oldval) ^ *(preval) : 1);              
    *(preval) = (oldval);                                                      
    __atomic_compare_exchange_n ((atomic), (preval), (newval), FALSE,          
                                 __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST)           
                                 ? TRUE : FALSE;                               
  }))

and there is a function with the same name.

When the name is enclosed in parentheses as here,

gboolean
(g_atomic_int_compare_and_exchange_full) (gint *atomic,
                                          gint  oldval,
                                          gint  newval,
                                          gint *preval)

then the compiler can not consider it as a macro.

That is, you have a function definition within which there is used a macro with the same name as the name of the function.

Here is a demonstration program:

#include <stdio.h>

#define from( x )( x ) * ( x )

int (from)( int x ) { return from( x ); }

int main( void )
{
    printf( "%dn", (from)( 10 ) );
}

Pay attention to that a declarator (including a function declarator) may be enclosed in parentheses.

From the C grammar that defines declarators:

direct-declarator:
    identifier
    ( declarator )

Here is a declaration of a two-dimensional array with enclosing declarators in parentheses:

int ( ( ( a )[10] )[10] );

Though the parentheses evidently are redundant.

Share
Improve this answer

1

  • "compiler can not consider it as a macro" – The compiler will never consider stuff as a macro, IMHO. At compilation time, all macros have already been eliminated by the preprocessor.

    – Thomas Weller

    5 hours ago

Your Answer

Draft saved
Draft discarded

Post as a guest

Required, but never shown


By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged

or ask your own question.

Leave a Reply

Your email address will not be published. Required fields are marked *