在编译时获取变量名的标准方法

A standard way for getting variable name at compile time

本文关键字:标准 方法 变量名 获取 编译      更新时间:2023-10-16

在C++11或更高版本中是否有某种方法可以实现与类似的行为

int some_int;
std::string x=variable_name<some_int>::value; //Theoretical code 
std::cout << x;

结果应该是:

某些

如果没有,是否有编译器特定的方法可以做到这一点?我的目标是MSVS。

您询问:

在C++11或更高版本中,是否有某种方法可以实现与类似的行为

int some_int;
std::string x=type_name<some_int>::value; //Theoretical code 
std::cout << x;

结果应该是:

某些

是的,您只需使用预处理器的字符串运算符#:

#include <iostream>
#define NAME_OF( v ) #v
using namespace std;
auto main() -> int
{
    int some_int;
     //std::string x=type_name<some_int>::value; //Theoretical code 
    auto x = NAME_OF( some_int );
    (void) some_int;
    cout << x << endl;
}

如果你想问一些不同的问题,那么请发布一个新问题,因为这个问题现在已经得到了回答(修改问题会使这个答案无效)。


作为真实世界用法的一个示例,这里的宏将变量及其名称传递给测试函数:

#define TEST( v ) test( v, #v )

如果您想要编译时检查有问题的名称是变量或类型名称,那么您可以简单地应用sizeof,例如在逗号表达式中:

#define NAME_OF( v ) (sizeof(v), #v)

有没有sizeof的区别在于,这是否保证完全在编译时完成,而不是可能在运行时生成代码来做一些事情。

为了避免可能的警告,您可以在void:中添加伪转换

#define NAME_OF( v ) ((void) sizeof(v), #v)

为了使这也适用于函数名,您可以添加一个typeid:

#define NAME_OF( name ) ((void) sizeof(typeid(name)), #name)

完整示例:

#include <typeinfo>
#define NAME_OF( name ) ((void) sizeof(typeid(name)), #name)
void foo() {}
#include <iostream>
using namespace std;
auto main() -> int
{
    int some_int;
    (void) some_int;
     //std::string x=type_name<some_int>::value; //Theoretical code 
    auto v = NAME_OF( some_int );
    auto t = NAME_OF( int );
    auto f = NAME_OF( foo );
    #ifdef TEST_CHECKING
        (void) NAME_OF( not_defined );
    #endif
    cout << v << ' ' << t << ' ' << f << endl;
}

不过,检查并不是100%完美,因为仍然可以将函数调用传递给NAME_OF宏。

正如其他人所指出的,您确实可以使用宏来"字符串化"变量名。但是,您可以使用以下定义,而不是简单地将其定义为#define NAMEOF(variable) #variable
#define NAMEOF(variable) ((decltype(&variable))nullptr, #variable)

正如您所看到的,它使用了逗号运算符。这个表达式的左边部分什么也不做,只是执行从nullptr到指向variable类型的指针的(毫无意义的)转换,转换结果会立即被丢弃。右边的部分只是返回字符串化变量的名称。

为什么这比在宏中简单地使用#variable要好

多亏了decltype()运算符,只有当您向NAMEOF宏传递某种类型的变量而不是任意字符串或文字时,整个过程才会编译。考虑以下示例:

double value = 523231231312.0095;
cout<< NAMEOF(value) << endl;    // value
cout<< NAMEOF(value1) << endl;   // Compiler error: 'value1' was not declared in this scope
cout<< NAMEOF(42) << endl;       // Compiler error: lvalue required as unary '&' operand

因此,如果在未来的重构过程中修改value变量的名称,您不会忘记修改使用其名称的位置,因为编译器会对您大喊大叫,直到您修复该变量的NAMEOF的每次使用。

在MinGW-W64(gcc v.2.0)上测试


在评论中,@iammilind@Niall提出了另外两种定义该宏的方法,它们不依赖于C++11特定的decltype()运算符:

#define NAMEOF(variable) ((void*)&variable, #variable)

// Unlike other definitions, this one, suggested by @Niall,
// won't get broken even if unary & operator for variable's type
// gets overloaded in an incompatible manner.
#define NAMEOF(variable) ((void)variable, #variable)
// On the other hand, it accepts literals as parameters for NAMEOF,
// though this might be desired behaviour, depending on your requirements.
NAMEOF(42);    // 42

@Leon的建议下使用这样一个宏,根据您的评论,我们得到:

template<class T>
void foo(T var, const char* varname)
{
    std::cout << varname << "=" << var << std::endl;
}
#define FOO(var) foo(var, NAMEOF(var))
int someVariable = 5;
FOO(someVariable);           // someVariable = 5
FOO(nonExistingVariable);    // compiler error!

根据注释,您需要它来将变量的值及其名称传递到函数中。这必须在宏的帮助下完成:

#include <iostream>
template<class T>
void foo(T var, const char* varname)
{
    std::cout << varname << "=" << var << std::endl;
}
#define FOO(var) foo(var, #var)
int main()
{
    int i = 123;
    double d = 45.67;
    std::string s = "qwerty";
    FOO(i);
    FOO(d);
    FOO(s);
    return 0;
}

输出:

i=123
d=45.67
s=qwerty