2009-07-04 11 views
15

Sto creando una macro in C++ che dichiara una variabile e assegna un valore ad essa. A seconda di come viene utilizzata la macro, la seconda occorrenza della macro può sostituire il valore della prima variabile. Per esempio:Come generare nomi di variabili casuali in C++ usando macro?

#define MY_MACRO int my_variable_[random-number-here] = getCurrentTime(); 

L'altra motivazione per usare vale a evitare di selezionare determinato nome alla variabile in modo che sia uguale a un nome poi scelta dallo sviluppatore utilizzando la macro.

C'è un modo per generare nomi di variabili casuali all'interno di una macro in C++?

- Modifica -

voglio dire unica, ma anche a caso, una volta che posso utilizzare il mio macro due volte in un blocco e in questo caso si genererà qualcosa come:

int unique_variable_name; 
... 
int unique_variable_name; 

In questo caso, per essere unici entrambi i nomi delle variabili devono essere generati casualmente.

+2

Sicuramente vuoi dire nomi di variabili univoci, piuttosto che casuali? –

+1

Sono un po 'confuso su come sarebbe utile. Il programmatore può quindi utilizzare in seguito i riferimenti my_variable_ *? getCurrentTime() ha qualche effetto collaterale utile? – SingleNegationElimination

risposta

10

Aggiungi M4 al flusso di build? Questo linguaggio macro ha alcune funzionalità stateful e può essere combinato con successo con macro CPP. Questo probabilmente non è un modo standard per generare nomi univoci in un ambiente C, sebbene sia stato in grado di usarlo con successo in questo modo.

Probabilmente non vuoi essere casuale, a proposito, in base al modo in cui hai posto la tua domanda. Si desidera univoco.

Si potrebbe utilizzare __FILE__ e __LINE__ nella macro di espansione per ottenere l'unicità ti sembra di essere in corso per ... quei metavariables vengono definite nel contesto file sorgente, quindi state attenti a fare in modo che si ottiene ciò che si sta cercando per (ad esempio, i rischi di più di una macro sulla stessa riga).

+3

Esiste anche la macro __COUNTER__ che genera nuovo intero ogni volta che viene richiamata, ma non è standard. –

+1

Whoa, SO ha ora la formattazione dei commenti! Ad ogni modo, questo dovrebbe essere COUNTER con due underscore precedenti e successivi. –

+4

Questo non funzionerà per me dal momento che posso utilizzare la macro più di una volta all'interno dello stesso file e farvi riferimento più tardi in un'altra macro. Il "__ COUNTER __" (so che è tutto insieme) potrebbe funzionare ma avrei bisogno di conoscere il valore corrente del contatore senza incrementarlo. – freitass

0

Anche se non penso sia possibile, dovresti seriamente prendere in considerazione l'idea di fare una lezione.

Se si desidera un elemento casuale in un array casuale per tenere un certo valore, si può fare questo:

std::vector< std::vector<int> > m_vec; 

Poi avvolgere in una classe, per cui lo sviluppatore può impostare solo un numero:

void set(int foo) 
{ 
    m_vec[random()][random()] = foo; 
} 

C'è qualche motivo per cui vuoi una macro? Il nome variabile casuale sembra pericoloso, e se seleziona qualcosa già definito da qualche altra parte nel codice?

+0

In realtà non "voglio" una macro, ma il problema da risolvere è in una macro. La tua risposta mi ha dato un'idea, ho creato una classe per contenere i valori (gestendo una lista invece di dichiarare una variabile ogni volta). – freitass

7

Generare nomi univoci nel preprocessore è difficile. Il più vicino possibile è quello di manipolare __FILE__ e __LINE__ nel simbolo come suggerisce popcnt. Se hai davvero bisogno di generare nomi di simboli globali univoci, allora seguirò il suo suggerimento sull'utilizzo di qualcosa come M4 o uno script Perl nel tuo sistema di compilazione.

Potrebbe non essere necessario un nome univoco. Se la tua macro può imporre un nuovo ambito, puoi usare lo stesso nome poiché ombreggia semplicemente altre definizioni. Di solito seguo il consiglio comune di avvolgere macro nei loop do { ... } while (0). Funziona solo per macro che sono dichiarazioni, non espressioni. La macro può aggiornare le variabili usando i parametri di output .Per esempio:

#define CALC_TIME_SINCE(t0, OUT) do { \ 
    std::time_t _tNow = std::time(NULL); \ 
    (OUT) = _tNow - (t0); \ 
} while (0) 

Se si segue un few rules, si sono di solito abbastanza sicuro:

  1. Utilizzare leader sottolineatura o convenzioni di denominazione simili per simboli definiti all'interno della macro. Ciò impedirà il verificarsi di problemi associati a un parametro che utilizza lo stesso simbolo.
  2. Utilizzare solo i parametri di input una volta e circondarli sempre con parentesi. Questo è l'unico modo per far funzionare le macro con espressioni come input.
  3. Utilizzare l'espressione do { ... } while (0) per garantire che la macro venga utilizzata solo come istruzione e per evitare altri problemi di sostituzione testuale.
+2

L'utilizzo di caratteri di sottolineatura iniziali non è una buona idea, poiché i nomi così generati possono scontrarsi con i nomi riservati all'implementazione e in ogni caso sono riservati. –

+1

Infatti. È possibile che l'utente della macro voglia utilizzare un nome come _tNow. Ti suggerisco di utilizzare il nome completo della macro come prefisso per i nomi utilizzati dalla macro, in questo caso CALC_TIME_SINCE_tNow – SingleNegationElimination

+0

L'ambito locale per avere nomi univoci localmente è una buona idea –

3

Invece di fare in modo che il preprocessore crei un nome, è possibile consentire all'utente della macro di dargli un nome.

#define MY_MACRO(varname) int varname = getCurrentTime(); 
10

uso __COUNTER__ (funziona su gcc4.8, clang 3.5 e Intel ICC v13, MSVC 2015)

#define CONCAT_(x,y) x##y 
#define CONCAT(x,y) CONCAT_(x,y) 
#define uniquename static bool CONCAT(sb_, __COUNTER__) = false 
+1

Nice, non sapevo nulla su ##. –

+2

Questo non funziona, \ _ \ _ COUNTER \ _ \ _ non è espanso. L'output del preprocessore è: bool statico sb \ _ \ _ \ _ COUNTER \ _ \ _ = false; – JeffB

1

avevo bisogno di qualcosa di simile per un caso in cui non ho avuto alcun attrezzo di profilazione, ma volevo contare quanti thread si trovavano all'interno di un particolare blocco di codice e la quantità di tempo (tick) spesi in quel blocco di codice da ciascun thread, in questo caso ogni blocco aveva bisogno di una variabile statica univoca accessibile a tutti i thread, e avevo bisogno di riferirmi in seguito a tale variabile su incr (ho usato un'API di registrazione piuttosto che printf nel codice reale, ma funziona anche così). In un primo momento ho pensato che era molto intelligente nel modo seguente:

#define PROF_START { \ 
    static volatile int entry_count##___FUNCTION__##__LINE__ = 0; int *ptc = &entry_count##___FUNCTION__##__LINE__; \ 
    clock_t start, end; \ 
    start = times(0); \ 
    (*ptc)++; 

Ma poi ho capito che questo è solo stupido e il compilatore C si limiterà a fare questo per voi, a patto che ogni dichiarazione "statico" è il suo blocco:

#include <stdio.h> 
#include <sys/times.h> 

#define PROF_START { \ 
    static int entry_count = 0; \ 
    clock_t start, end; \ 
    start = times(0); \ 
    entry_count++; 


#define PROF_END \ 
    end = times(0); \ 
    printf("[%s:%d] TIMER: %ld:%d\n" , __FUNCTION__, __LINE__, end-start, entry_count); \ 
    entry_count--; \ 
    } 

Nota le parentesi aperta/chiusa in ciascuna macro. Questo non è strettamente thread-safe, ma per i miei scopi di profilazione potrei supporre che le operazioni di incr e decr fossero atomiche. Ecco un esempio di ricorsione che utilizza le macro

#define ITEM_COUNT 5 

struct node { 
    int data; 
    struct node *next; 
}; 

revsort(struct node **head) 
{ 
    struct node *current = *head; 
    struct node *next_item; 

    while (current->next) 
    { 
PROF_START 
    next_item = current->next; 
    current->next = next_item->next; 
    next_item->next = *head; 
    *head = next_item; 
PROF_END 
    } 
} 

rrevsort(struct node **head) 
{ 
    struct node *current = *head; 
    struct node *next_item = current->next; 

PROF_START 
    current->next = 0; 
    if (next_item) 
    { 
    *head = next_item; 
    rrevsort(head); 
    next_item->next = current; 
    } 
PROF_END 

} 

printnode(struct node *head) 
{ 
    if (head) 
    { 
    printf("%d ", head->data); 
    printnode(head->next); 
    } 
    else 
    printf("\n"); 

} 

main() 
{ 

    struct node node_list[ITEM_COUNT]; 
    struct node *head = &node_list[0]; 
    int i; 

    for (i=0; i < ITEM_COUNT - 1; i++) 
    { 
PROF_START 
     node_list[i].data = i; 
     node_list[i].next = &node_list[i+1]; 
PROF_END 
    } 
    node_list[i].data = i; 
    node_list[i].next = 0; 

    printf("before\n"); 
    printnode(head); 
    revsort(&head); 
    printf("after\n"); 
    printnode(head); 
    rrevsort(&head); 
    printf("before\n"); 
    printnode(head); 
} 

suggerimento Extra, il programma di cui sopra è una domanda comune intervista. Estratto da "nm -A":

macro:0804a034 b entry_count.1715 
macro:0804a030 b entry_count.1739 
macro:0804a028 b entry_count.1768 
macro:0804a02c b entry_count.1775 
1

Ecco una breve definizione di macro per generare il modello singleton sopra.

#define SINGLETON_IMPLIMENTATION(CLASS_NAME) static CLASS_NAME *g##CLASS_NAME = nil; + (CLASS_NAME *)instance { @synchronized(self) { if (g##CLASS_NAME == nil) g##CLASS_NAME = [self new]; } return g##CLASS_NAME; } 

#define SINGLETON_DECLARATION(CLASS_NAME) + (CLASS_NAME *)instance; 
20

provare quanto segue:

// This is some crazy magic that helps produce __BASE__247 
// Vanilla interpolation of __BASE__##__LINE__ would produce __BASE____LINE__ 
// I still can't figure out why it works, but it has to do with macro resolution ordering 
#define PP_CAT(a, b) PP_CAT_I(a, b) 
#define PP_CAT_I(a, b) PP_CAT_II(~, a ## b) 
#define PP_CAT_II(p, res) res 

#define UNIQUE_NAME(base) PP_CAT(base, __COUNTER__) 

__COUNTER__ si dice di avere problemi di portabilità. In tal caso, è possibile utilizzare __LINE__ e fintanto che non si chiama la macro più di una volta per riga o si condividono i nomi su unità di compilazione, si andrà bene.

+0

Questo sembra pazzo, ma in realtà funziona. Ho avuto esattamente questo problema: '__LINE__' si è espanso su se stesso, invece che su un numero. Ho omesso il 'PP_', e ora posso fare cose come:' #finefine FOR (ii, ll, uu) int CAT (FORlim, __LINE__) = (uu); for (int ii = (ll); ii

+0

E come fare riferimento a una variabile con un nome univoco appena formato? Di ', ho questo codice: int UNIQUE_NAME (nTest) = 100 ;. Come interrogare la variabile nTest0 più avanti nel codice? PP_CAT (base, __COUNTER - 1) non funziona. Grazie. – Roman

+0

per il motivo per cui abbiamo bisogno di riferimento indiretto in macro, leggere https://stackoverflow.com/a/13301627/264047 –

Problemi correlati