2009-07-04 15 views
15

Tworzę makro w C++, które deklaruje zmienną i przypisuje jej pewną wartość. W zależności od sposobu użycia makra, drugie wystąpienie makra może przesłonić wartość pierwszej zmiennej. Na przykład:Jak generować nazwy zmiennych losowych w C++ za pomocą makr?

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

Druga motywacja do korzystania to uniknąć wybierając określoną nazwę zmiennej, tak aby być taka sama jak nazwa ostatecznie wybranego przez dewelopera przy użyciu makra.

Czy istnieje sposób generowania losowych nazw zmiennych w makrze w C++?

- Edit -

Znaczy niepowtarzalne, ale również losowo raz mogę wykorzystać moje makro dwukrotnie w bloku iw tym przypadku będzie generować coś takiego:

int unique_variable_name; 
... 
int unique_variable_name; 

W tym przypadku, aby były unikalne, obie nazwy zmiennych muszą być generowane losowo.

+2

Na pewno masz na myśli unikalne nazwy zmiennych, a nie losowe? –

+1

Jestem nieco zdezorientowany tym, jak to byłoby przydatne. Czy programiści później wykorzystają odwołania my_variable_ *? czy getCurrentTime() ma jakiś użyteczny efekt uboczny? – SingleNegationElimination

Odpowiedz

10

Dodaj M4 do swojego strumienia kompilacji? Ten język makr ma pewne funkcje stanowe i może z powodzeniem zostać wymieszany z makrami CPP. Prawdopodobnie nie jest to standardowy sposób generowania niepowtarzalnych nazw w środowisku C, choć udało mi się z powodzeniem go używać w taki sposób.

Prawdopodobnie nie chcesz losowego, BTW, w oparciu o sposób, w jaki zadałeś pytanie. Chcesz unikatowy.

Możesz użyć funkcji __FILE__ i __LINE__ w rozszerzeniu makr, aby uzyskać wyjątkowość, do której zmierzasz ... te metawnaki są definiowane w kontekście pliku źródłowego, więc należy uważać, aby uzyskać to, czego szukasz dla (np. zagrożeń więcej niż jednego makra na tej samej linii).

+3

Istnieje również makro __COUNTER__, które generuje nową liczbę całkowitą za każdym razem, gdy jest wywoływane, ale jest to niestandardowe. –

+1

Whoa, SO ma teraz formatowanie komentarzy! Tak czy inaczej, to naprawdę powinno być COUNTER z dwoma podkreśleniami poprzedzającymi i następującymi po nim. –

+4

To nie będzie dla mnie działać, ponieważ mogę użyć makro więcej niż jeden raz w tym samym pliku i odniesienie go później w innym makro. "__ COUNTER __" (wiem, że wszystko razem) może działać, ale potrzebowałbym znać aktualną wartość licznika bez zwiększania go. – freitass

0

Chociaż nie wydaje mi się, że jest to możliwe, powinieneś poważnie rozważyć zrobienie klasy z tego.

Jeśli chcesz element losowy w losowej tablicy posiadać pewną wartość, można to zrobić:

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

Następnie owinąć go w klasie, więc deweloper może tylko ustawić numer:

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

Czy istnieje jakiś powód, dla którego chcesz uzyskać makro? Nazwa zmiennej losowej brzmi niebezpiecznie, a jeśli wybierze coś zdefiniowanego gdzieś indziej w kodzie?

+0

Właściwie nie "chcę" to makro, ale problem, który należy rozwiązać, jest w makro. Twoja odpowiedź dała mi pewien pomysł, stworzyłem klasę do przechowywania wartości (zarządzanie listą zamiast deklarowania zmiennej za każdym razem). – freitass

7

Generowanie unikalnych nazw w preprocesorze jest trudne. Najbliżej można przesortować __FILE__ i __LINE__ do symbolu sugerowanego jako popcnt. Jeśli naprawdę potrzebujesz generować niepowtarzalne globalne nazwy symboli, to skorzystam z jego sugestii, aby zamiast tego użyć coś takiego jak M4 lub skrypt Perl w twoim systemie kompilacji.

Możesz nie potrzebować unikalnych nazw. Jeśli twoje makro może narzucić nowy zakres, możesz użyć tej samej nazwy, ponieważ będzie ona po prostu cieniem innych definicji. Zwykle podążam za powszechną radą owijania makr w pętlach do { ... } while (0). Działa to tylko w przypadku makr, które są wyrażeniami, a nie wyrażeniami. Makro może aktualizować zmienne przy użyciu parametrów wyjściowych :.Na przykład:

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

Jeśli zastosujemy się few rules, jesteś zwykle dość bezpieczne:

  1. Stosować wiodące podkreślenia lub podobne konwencje nazewnictwa dla symboli określonych w makro. Zapobiegnie to problemom związanym z parametrem wykorzystującym ten sam symbol.
  2. Parametry wejściowe należy używać tylko raz i zawsze otaczać je nawiasami. Jest to jedyny sposób, aby makra działały z wyrażeniami jako dane wejściowe.
  3. Należy użyć idiomu do { ... } while (0), aby upewnić się, że makro jest używane tylko jako instrukcja i aby uniknąć innych problemów z zastąpieniem tekstu.
+2

Korzystanie z wiodących podkreśleń nie jest dobrym pomysłem, ponieważ wygenerowane w ten sposób nazwy mogą kolidować z zastrzeżonymi nazwami implementacji i są w każdym przypadku zastrzeżone. –

+1

Rzeczywiście. Możliwe, że użytkownik makra może chcieć użyć nazwy takiej jak _tNow. Zasugeruję użycie pełnej nazwy makra jako prefiksu dla nazw używanych przez makro, w tym przypadku CALC_TIME_SINCE_tNow – SingleNegationElimination

+0

Lokalny zasięg unikalnych lokalnie nazw jest niezły pomysł –

3

Zamiast tego, że preprocesser tworzy nazwę, możliwe, że użytkownik makra poda ci nazwę.

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

użycie __COUNTER__ (działa na gcc4.8, dzyń 3.5 i 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

Miło, nie wiedziałem o ##. –

+2

To nie działa, \ _ \ _ COUNTER \ _ \ _ nie jest rozwinięte. Wyjście preprocesora to: statyczne urządzenie sb \ _ \ _ \ _ COUNTER \ _ \ _ = false; – JeffB

1

potrzebowałem czegoś podobnego do przypadku, w którym nie ma żadnych narzędzi do profilowania, ale chciałem zliczyć, ile wątków znajdowało się wewnątrz danego bloku kodu, a także ile czasu (ticks) spędził w tym bloku kodu przez każdy wątek, w tym przypadku każdy blok potrzebował unikatowej zmiennej statycznej dostępnej dla wszystkich wątków, i musiałem później odwołać tę zmienną do incr (użyłem API rejestrowania zamiast printf w rzeczywistym kodzie, ale to też działa). Na początku myślałem, że był bardzo mądry, wykonując następujące czynności:

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

Ale potem zdałem sobie sprawę, to jest po prostu głupie i kompilator C będzie po prostu to zrobić dla ciebie, tak długo, jak każdy „statyczne” deklaracja jest jego własny blok:

#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--; \ 
    } 

Uwaga na nawiasy otwierające/zamykające w każdym makrze. Nie jest to ściśle wątek bezpieczny, ale dla celów profilowania mogłem założyć, że operacje incr i decr były atomowe. Oto próbka rekursji, która używa makr. Dodatkowa podpowiedź, powyższy program jest typowym pytaniem wywiadowczym. Fragment z "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

Oto krótka definicja makra, która generuje powyższy wzór singletonu.

#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

Spróbuj wykonać następujące czynności:

// 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__ jest podobno mają problemy przenośność. Jeśli tak, możesz użyć wartości __LINE__ i tak długo, jak nie wywołasz makra więcej niż raz w linii lub nie podzielisz się nazwami w jednostkach kompilacji, wszystko będzie dobrze.

+0

To wygląda na zwariowane, ale tak naprawdę działa. Miałem dokładnie ten problem: '__LINE__' rozwija się do siebie, zamiast numeru. Pominąłem 'PP_', a teraz mogę robić rzeczy takie jak:' #define FOR (ii, ll, uu) int CAT (FORlim, __LINE__) = (uu); dla (int ii = (ll); ii

+0

A jak odwołać się do zmiennej o właśnie utworzonej unikalnej nazwie? Powiedzmy, mam ten kod: int UNIQUE_NAME (nTest) = 100 ;. W jaki sposób zapytać o zmienną nTest0 później w kodzie? PP_CAT (baza, __COUNTER - 1) nie działa. Dzięki. – Roman

+0

dlaczego potrzebujemy indirect w makro, przeczytaj https://stackoverflow.com/a/13301627/264047 –

Powiązane problemy