如何构建本地唯一标识符名称?

5

我希望能够动态创建一个唯一的变量名。

这是我的代码:

int call(int i)
{
    return i;
}
 
#define XCAT3(a, b, c)    a ## b ## c
 
#define CALL_2(arg, place, line) int XCAT3(cl, place, line) = call(arg);
 
#define CALL_1(arg)    CALL_2(arg, __FUNCTION__, __LINE__)
 
int main(int argc, char* argv[])
{
    CALL_1(1); /* this is line 20 */
    return 0;
}

这在GCC中起作用 (http://ideone.com/p4BKQ),但不幸的是在Visual Studio 2010或2012中不起作用。

错误信息为:

test.cpp(20): error C2143: 语法错误: 缺少 ';' 在 'function_string'之前

test.cpp(20): error C2143: 语法错误: 缺少 ';' 在 'constant'之前

test.cpp(20): error C2106: '=': 左操作数必须是l值

如何在C++中创建即时唯一的变量名?

解决方案:

#define CAT2(a,b) a##b
#define CAT(a,b) CAT2(a,b)
#define UNIQUE_ID CAT(_uid_,__COUNTER__)

int main(int argc, char* argv[])
{
    int UNIQUE_ID = 1;
    int UNIQUE_ID = 2;
    return 0;
}

2
https://dev59.com/s3NA5IYBdhLWcg3wH6EW - Derek
2个回答

6

对于唯一标识符,许多实现提供了__COUNTER__预处理器宏,该宏在每次使用时扩展为递增的数字。

#define CAT(a,b) CAT2(a,b) // force expand
#define CAT2(a,b) a##b // actually concatenate
#define UNIQUE_ID() CAT(_uid_,__COUNTER__)

auto UNIQUE_ID() = call(1); // may be _uid_0
auto UNIQUE_ID() = call(2); // may be _uid_1

+1,这很好知道,但我真的想知道我的代码有什么问题 :| - Felix Dombek
1
@Felix:正如所说,__FUNCTION__会产生一个字符串字面量,例如"main",而不是可以用作标识符的内容。实际上,你的代码根本不应该编译通过,即使在GCC上也不行。 - Xeo
接受此答案,因为这正是我需要的,但是:我在Visual Studio 2008默认命令行项目和VS2012中启动了另一个项目,在它们两个中都没有展开__COUNTER__。完整源代码:`#define UNIQUE_ID uid##COUNTERint main(int argc, char* argv[]) { int UNIQUE_ID = 1; int UNIQUE_ID = 2; return 0; }会出现错误error C2374: 'uid__COUNTER__' : redefinition; multiple initialization`你有什么想法吗? - Felix Dombek
@Felix:好问题。尝试使用#define EXPAND(Arg) Arg#define UNIQUE_ID() _uid_ ## EXPAND(__COUNTER__) - Xeo
额外的间接层起作用了!非常感谢你的帮助! - Felix Dombek
显示剩余2条评论

2

您需要推迟令牌粘贴,直到在宏CALL_LATER2中递归扩展参数placeline之后。您可以通过将##操作移动到单独的宏中来实现这一点 - 只要##不出现在CALL_LATER2的主体中,其所有参数都将被预扫描以查找宏:

#define XCAT3(a, b, c)    a ## b ## c
#define CALL_LATER2(fun, h, place, line) \
    auto XCAT3(calllater, place, line) = \
        call_later((fun), (h));

然而,这仍然不能满足您的需求,因为 __FUNCTION__ 展开为带有 " 字符的字符串,而不是可粘贴到标识符中的内容。相反,您需要基于 __LINE__ 创建名称,并确保在不同编译单元中可能会出现重复的事实不会成为问题(如果它们局部于某个函数,那应该没问题,或者您可以将它们放入匿名命名空间中)。

这对我仍然不起作用 - 请查看我的更新问题。 - Felix Dombek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接