2010-10-27 13 views
8

Ho osservato lo GCC docs per la definizione dei macro e sembra che ciò che voglio non sia possibile, ma immagino che se lo fosse, qualcuno qui lo saprebbe.È possibile definire una macro simile a una funzione con un corpo variabile?

Quello che voglio fare è definire questa macro:

synchronized(x) { 
    do_thing(); 
} 

che si espande a:

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

In C++ ho potuto solo fare un oggetto SynchronizedBlock che ottiene il blocco nel suo costruttore e sblocca nel distruttore, ma non ho idea di come farlo in C.

Mi rendo conto che potrei usare un puntatore di funzione nel modulo synchronized(x, &myfunction);, ma il mio l'obiettivo è far sì che un codice C assomigli il più possibile a Java. E sì, so che questo è malvagio.

+4

Dovresti davvero pensarci due volte prima di provare a far sembrare un linguaggio come un altro. Una macro che assomiglia alla sintassi che stai chiedendo sarebbe estremamente confusa per qualcuno che la legge come C. –

+0

R. ha ragione che questo potrebbe rendere le cose difficili da leggere, dal momento che non è facilmente visibile che questa è una macro. Penso che la cosa migliore sia attenersi alla convenzione che le macro dovrebbero essere maiuscole e che il nome dovrebbe essere davvero informativo. Proverò qualcosa come "MUTUALY_EXCLUDE' o così. –

+0

^'E sì, so che questo è il male. –

risposta

16

EDIT: Modificato nella versione di nategoose

#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) 

E lo si può usare come questo:

synchronized(x) { 
    do_thing(x); 
} 

o anche senza bretelle

synchronized(x) 
    do_thing(); 
+0

Molto impressionante. Perfino i numeri di riga in errori sarebbero risultati giusti. L'unico vero difetto è che l'argomento lock non dovrebbe apparire due volte all'interno della macro e hai lasciato fuori la maggior parte del tuo '\' st finale '\\ – nategoose

+5

che è allo stesso tempo sorprendente e terrificante. Non ci avrei mai pensato. –

+3

Questo dovrebbe risolvere entrambi: '#define sincronizzato (blocco)' \ ' per (pthread_mutex_t * i_ = &lock; i_; i_ = NULL)' \ ' per (pthread_mutex_lock (i_); i_;' \ ' pthread_mutex_unlock (i_), i_ = NULL) ' – nategoose

4

Ecco un inizio, ma potrebbe essere necessario modificarlo:

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

Usa come questo (purtroppo, non la sintassi simile a Java si voleva):

synchronized(x, do_thing, arg1, arg2); 
+0

Sembra che il comando 'do ... while (0)' non sia necessario, il '{..}' funziona da solo (almeno con gcc). Ciò richiede comunque un blocco come 'synchronized (x, function)' invece di 'synchronized (x) {/ * function * /}' comunque. –

+0

Non importa, vedo cosa significa 'do ... while (0)'. –

+0

Ecco il motivo del do/while: http://kernelnewbies.org/FAQ/DoWhile0 – Jonathan

1

Questa è stata la migliore che si avvicinò con:

#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") 
         )); 

Si noti che è necessario utilizzare l'operatore virgola usato raramente e ci sono restrizioni a quale codice può vivere all'interno dell'elenco di codici di sincronizzazione (non proprio java).

2

Domanda molto interessante!

Ho guardato le altre risposte e mi è piaciuto quello con for. Ho un miglioramento, se posso! GCC 4.3 introduce la macro COUNTER, che possiamo usare per generare nomi di variabili univoci.

#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)) 

Usando le macro, questo codice ...

mutex(my_mutex) { 
    foo(); 
} 

... si espanderà per ...

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

Con my_mutex__n a partire dal 0 e generando un nuovo nome ogni tempo è usato! È possibile utilizzare la stessa tecnica per creare corpi di codice simili a monitor, con un nome univoco ma sconosciuto per il mutex.

+0

Mi piace l'approccio, ma non funziona se passo un membro struct. 'syn chronized (object-> lock) {/ * stuff * /} 'si espande in' char object-> lock__0;/* etc */', che causa un problema perché lock__0 non è un membro struct. –

+0

Ahh, true = (beh, la risposta modificata qui sopra sembra comunque migliore e non ha bisogno di contatori Cheers! – slezica

+0

Il solo naming della variabile '_lock_ ## __COUNTER__' sembra funzionare (e risolvere un problema nell'altra implementazione). –

Problemi correlati