2009-07-04 250 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__,只要您不是每行超过一次调用宏或在编译单元之间共享名称,就会很好。