2009-07-04 251 views
15

我在C++中創建了一個聲明變量併爲其賦值的宏。根據宏的使用方式,宏的第二次出現可以覆蓋第一個變量的值。例如:如何使用宏在C++中生成隨機變量名?

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

其他動機的是使用以避免選擇特定名稱的變量,以便它是相同的最終通過使用宏開發人員選擇一個名稱。

有沒有一種方法可以在C++宏中生成隨機變量名?

- 編輯 -

我的意思是獨一無二的,但也是隨機的,一旦我可以用我的宏兩次塊,在這種情況下,它會產生類似:

int unique_variable_name; 
... 
int unique_variable_name; 

在這種情況下,唯一的變量名必須是隨機生成的。

+2

當然,你的意思是獨特的變量名稱,而不是隨機的? – 2009-07-04 15:23:00

+1

我有點困惑這將如何有用。程序員之後會使用my_variable_ *引用嗎? getCurrentTime()有一些有用的副作用嗎? – SingleNegationElimination 2009-07-04 18:23:53

回答

10

將M4添加到您的構建流程中?這種宏語言具有一些有狀態的功能,並且可以成功地與CPP宏混合。這可能不是在C環境中生成唯一名稱的標準方式,儘管我已經能夠以這種方式成功地使用它。

根據您提出問題的方式,您可能不想隨機,順便說一句。你想獨特

您可以在宏擴展中使用__FILE____LINE__以獲得您似乎正在進行的唯一性...這些元變量在源文件上下文中定義,因此請注意確保獲得所需的內容用於(例如,同一行上的多個宏的危險)。

+3

還有__COUNTER__宏每次調用時都會生成新的整數,但它是非標準的。 – 2009-07-04 15:03:55

+1

哇,現在有評論格式!無論如何,這應該是真正的COUNTER,在它之前和之後有兩個下劃線。 – 2009-07-04 15:04:47

+4

這對我不起作用,因爲我可能在同一個文件中多次使用這個宏,並在稍後的宏中引用它。 「__ COUNTER __」(我知道它們都在一起)可能會工作,但我需要知道計數器的當前值而不增加它。 – freitass 2009-07-05 19:47:25

0

雖然我不認爲它甚至可能,但你應該認真考慮從這個課程中脫穎而出。

如果你想在一個隨機排列的隨機元素,以保持一定的值,你可以這樣做:

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

然後在類包裝,所以開發商只能設置一個數字:

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

你有什麼理由讓它成爲宏嗎?隨機變量的名字聽起來很危險,如果它選擇了已經在代碼中其他地方定義的東西呢?

+0

其實我並不是「想」它是一個宏,但是要解決的問題是宏。你的回答給了我一個想法,我創建了一個類來保存這些值(管理一個列表,而不是每次都聲明一個變量)。 – freitass 2009-07-05 19:42:08

7

在預處理器中生成唯一名稱很困難。您可以得到的最接近的結果是將__FILE____LINE__劃分爲符號popcnt。如果您確實需要生成唯一的全局符號名稱,那麼我會按照他的建議,在您的構建系統中使用類似M4或Perl腳本的東西。

您可能不需要唯一的名稱。如果你的宏可以施加一個新的範圍,那麼你可以使用相同的名稱,因爲它只會影響其他定義。我通常遵循do { ... } while (0)循環中包裝宏的常見建議。這隻適用於語句的宏 - 不是表達式。宏可以使用輸出參數來更新變量。例如:

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

如果按照few rules,你通常是非常安全:

  1. 使用前導下劃線或爲宏內定義的符號相似的命名約定。這將防止與使用相同符號的參數相關聯的問題發生。
  2. 只能使用一次輸入參數,並始終用圓括號括起它們。這是使表達式作爲輸入工作的唯一方法。
  3. 使用do { ... } while (0)成語來確保宏僅用作語句並避免其他文本替換問題。
3

而不是讓預處理程序創建一個名稱,你可能會讓宏用戶給你一個名字。

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

使用__COUNTER__(適用於gcc4.8,鐺3.5和英特爾的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

我需要的,我沒有任何分析工具的情況下,類似的東西,但是我想要計算特定代碼塊內有多少線程以及每個線程在該代碼塊中花費的時間量(tick)。在這種情況下,每個塊都需要一個可供所有線程訪問的唯一靜態變量,我需要稍後將該變量引用到incr(我在實際代碼中使用了日誌API而不是printf,但這也適用)。起初我還以爲我是通過以下操作十分巧妙:

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

但後來我意識到,這只是愚蠢和C編譯器將只爲你做這個,只要每一個「靜態」的聲明是其自身塊:

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

請注意每個宏中的開放/關閉括號。這不是嚴格的線程安全的,但爲了我的分析目的,我可以假設incr和decr操作是原子的。這是一個使用宏的遞歸示例

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

額外提示,上述程序是一個常見的面試問題。摘自「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

這裏是一個簡潔的宏定義來生成上面的單例模式。

#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

嘗試以下操作:

// 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__據傳有便攜性的問題。如果是這樣,您可以改爲使用__LINE__,只要您不是每行超過一次調用宏或在編譯單元之間共享名稱,就會很好。