Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Possible to define a function-like macro with a variable body?

Tags:

c

macros

mutex

I've been looking at the GCC docs for defining macros and it looks like what I want isn't possible, but I figure if it is, someone here would know.

What I want to do is define this macro:

synchronized(x) {
  do_thing();
}

Which expands to:

{
    pthread_mutex_lock(&x);
    do_thing();
    pthread_mutex_unlock(&x);
}

In C++ I could just make a SynchronizedBlock object that gets the lock in its constructor and unlocks in the destructor, but I have no idea how to do it in C.

I realize I could use a function pointer in the form synchronized(x, &myfunction);, but my goal is to make some C code look as much like Java as possible. And yes, I know this is evil.

like image 244
Brendan Long Avatar asked Oct 27 '10 19:10

Brendan Long


4 Answers

EDIT: Changed to nategoose's version

#define synchronized(lock) \
for (pthread_mutex_t * i_#lock = &lock; i_#lock; \
     i_#lock = NULL, pthread_mutex_unlock(i_#lock)) \
    for (pthread_mutex_lock(i_#lock); i_#lock; i_#lock = NULL)

And you can use it like this:

synchronized(x) {
    do_thing(x);
}

Or even without braces

synchronized(x)
    do_thing();
like image 55
Joe D Avatar answered Nov 13 '22 08:11

Joe D


Here's a start, but you may need to tweak it:

#define synchronized(lock, func, args...) do { \
    pthread_mutex_lock(&(lock)); \
    func(##args); \
    pthread_mutex_unlock(&(lock)); \
} while (0)

Use like this (unfortunately, not the Java-like syntax you wanted):

synchronized(x, do_thing, arg1, arg2);
like image 4
Jonathan Avatar answered Nov 13 '22 09:11

Jonathan


This was the best I came up with:

#define synchronized(x, things) \
      do { \
           pthread_mutex_t * _lp = &(x); \
           pthread_mutex_lock(_lp);      \
           (things);                     \
           pthread_mutex_unlock(_lp);    \
      } while (0)

...

        synchronized(x,(
                          printf("hey buddy\n"),
                          a += b,
                          printf("bye buddy\n")
                        ));

Note that you have to use the rarely used comma operator and there are restrictions to what code can live within the (not quite java-like) synchronization code list.

like image 1
nategoose Avatar answered Nov 13 '22 10:11

nategoose


Very interesting question!

I looked at the other answers and liked the one using for. I have an improvement, if I may! GCC 4.3 introduces the COUNTER macro, which we can use to generate unique variable names.

#define CONCAT(X, Y) X##__##Y
#define CONCATWRAP(X, Y) CONCAT(X, Y)
#define UNIQUE_COUNTER(prefix) CONCATWRAP(prefix, __COUNTER__)

#define DO_MUTEX(m, counter) char counter; \
for (counter = 1, lock(m); counter == 1; --counter, unlock(m))

#define mutex(m) DO_MUTEX(m, UNIQUE_COUNTER(m))

Using those macros, this code...

mutex(my_mutex) {
    foo();
}

... will expand to...

char my_mutex__0;
for (my_mutex__0 = 1, lock(my_mutex); my_mutex__0 == 1; --my_mutex__0, unlock(m)) {
    foo();
}

With my_mutex__n starting in 0 and generating a new name each time its used! You can use the same technique to create monitor-like bodies of code, with a unique-but-unknown name for the mutex.

like image 1
slezica Avatar answered Nov 13 '22 10:11

slezica