5
votes

I have created the following macros to lock a mutex and return (from the function within which this macro is called) in case the attempt at locking fails. Currently I have narrowed it down to 2 macros - one for returning from functions which do return a value, irrespective of type, and another for returning from functions which return nothing (i.e. void).

The code beyond the macros (below) is for illustration only and has very little to do with the actual production code that the macros will be used in.

#define MUTEX_LOCK()\
    {\
        if (pthread_mutex_lock(&mutex) != 0)\
        {\
            printf("Failed to lock mutex.\n");\
            return;\
        }\
    }
#define MUTEX_LOCK_RVAL(err_val)\
    {\
        if (pthread_mutex_lock(&mutex) != 0)\
        {\
            printf("Failed to lock mutex.\n");\
            return err_val;\
        }\
    }

void vfunc()
{
    printf("\nIn vfunc()\n");
    MUTEX_LOCK();
    printf("\nOut of vfunc()\n");
}

UINT16 uint16func()
{
    printf("\nIn uint16func()\n");
    MUTEX_LOCK_RVAL(0);
    printf("\nOut of uint16func()\n");

    return 9;
}

CHAR* errstr = "Hoo boy!";
CHAR* strfunc()
{
    printf("\nIn strfunc()\n");
    MUTEX_LOCK_RVAL(errstr);
    printf("\nOut of strfunc()\n");

    return NULL;
}

Is there a way to reduce these to a single macro that can be used in functions returning a value as well as void.

3
Function-like macros that contain a return... that's really really nasty. Forget about it, just use if (!my_lock()) return <whatever>; at the call sites, with a proper lock function.Mat
It might be possible using e.g. variadic macros, but there are caveats. And it will really be confusing for other readers of your code (and with "others" I include you in a year or so).Some programmer dude
@JoachimPileborg Do you expect the calls to be confusing, or only the macro definition? If only the latter, some judicious comments should solve it, no?Tom Zych
Oh, and you macros in their current incarnation contains undefined behavior as you compare the return value of pthread_mutex_lock with uninitialized local variables. You don't need the variables at all actually, and I really recommend you think about what @Mat is saying, then you can have a single macro that is a simple expression.Some programmer dude
You put the print in the lock function, no need to repeat that. Hiding the return statement is not a win if the only thing it saves you a few keystrokes. Remember you write code once, but read it many times. Saving a few characters is a bad idea if it means your function's control flow has become invisible.Mat

3 Answers

4
votes

To make it ANSI compatible I define the macro with return and another simple symbol that evaluates to null and is clearly showing that it is null. I.e.:

#define VOID_RET    //This nulls the return value
#define MUTEX_LOCK(err_val)\
    {\
        if (pthread_mutex_lock(&mutex) != 0)\
        {\
            printf("Failed to lock mutex.\n");\
            return err_val;\
        }\
    }
void *mutex = NULL;
void vfunc(void)
{
    printf("\nIn vfunc()\n");
    MUTEX_LOCK(VOID_RET);
    printf("\nOut of vfunc()\n");
}
UINT16 uint16func(void)
{
    printf("\nIn uint16func()\n");
    MUTEX_LOCK(0);
    printf("\nOut of uint16func()\n");

    return 9;
}
CHAR* errstr = "Hoo boy!";
CHAR* strfunc(void)
{
    printf("\nIn strfunc()\n");
    MUTEX_LOCK(errstr);
    printf("\nOut of strfunc()\n");

    return NULL;
}

I tested this code under C11 and C99.

2
votes

As far as I can tell from the documentation, you can remove the macro function taking no argument because you are not obligated to pass an argument at all to a macro function expecting arguments.
From the GNU GCC documentation:

You can leave macro arguments empty; this is not an error to the preprocessor [...]


For multiple arguments, this is interesting:

You cannot leave out arguments entirely; if a macro takes two arguments, there must be exactly one comma at the top level of its argument list.

With these examples given:

min(, b)        ==> ((   ) < (b) ? (   ) : (b))
min(a, )        ==> ((a  ) < ( ) ? (a  ) : ( ))
min(,)          ==> ((   ) < ( ) ? (   ) : ( ))
min((,),)       ==> (((,)) < ( ) ? ((,)) : ( ))
2
votes

The only solution i can imagine is this:

#define MUTEX_LOCK( err_val )\
{\
  {\
      if (pthread_mutex_lock(&mutex) != 0)\
      {\
          printf("Failed to lock mutex.\n");\
          return err_val;\
      }\
   }\
}

int test_int()
{
    MUTEX_LOCK( 1 );

    return 0;
}

void test_void()
{
    MUTEX_LOCK( ; );

    return;
}