A standard way for getting variable name at compile time

牧云@^-^@ 提交于 2020-12-02 23:21:06

问题


Is there some way in C++11 or higher to achieve a similar behavior to:

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

Result should be:

some_int

If not, is there a compiler specific way to do it? I am targeting MSVS.


回答1:


You ask:

Is there some way in C++11 or higher to achieve a similar behavior to:

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

Result should be:

some_int

Yes, you can just use the preprocessor's stringizing operator #:

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

If you're asking for something different, then please post a new question since this one has now been answered (amending the question would invalidate this answer).


As an example real world usage, here's macro to pass a variable and its name to a test function:

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

If you want a compile time check that the name in question is a variable or type name, then you can simply apply sizeof, e.g. in a comma expression:

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

The difference between having sizeof or not, is whether this is guaranteed to be done purely at compile time, versus possibly generating code to also do something at run time.

To avoid a possible warning you can add a pseudo-cast to void:

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

And to make this work also for a function name you can add a typeid:

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

Complete example:

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

The checking is not 100% perfect, though, because it's still possible to pass a function invocation to the NAME_OF macro.




回答2:


As others have pointed out, you can indeed use a macro to "stringify" the variable name. However, instead of simply defining it as #define NAMEOF(variable) #variable, you can use the following definition:

#define NAMEOF(variable) ((decltype(&variable))nullptr, #variable)

As you can see, it uses a comma operator. The left part of this expression does nothing but performs a (pointless) conversion from nullptr to a pointer to variable's type, the result of which gets immediately discarded. The right part simply returns the stringified variable's name.

Why is this better than simply using #variable in the macro?

Thanks to the decltype() operator, the whole thing will only compile if you pass a variable of some sort and not some arbitrary string or a literal to NAMEOF macro. Consider the following example:

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

Because of this, if during future refactoring you modify the name of value variable, you won't forget to also modify places, where you use its name, since compiler will scream at you, until you also fix every usage of NAMEOF for this variable.

Tested on MinGW-W64 (gcc v5.2.0)


In the comments, @iammilind and @Niall have suggested two other ways to define this macro, which don't rely on C++11-specific decltype() operator:

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

...or...

// 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

Using such a macro with @Leon's suggestion, based on your comments, we get:

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!



回答3:


As follows from the comments, you need it for passing into a function both the value of the variable and its name. This must be done with the help of a macro:

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

Output:

i=123
d=45.67
s=qwerty


来源:https://stackoverflow.com/questions/38696440/a-standard-way-for-getting-variable-name-at-compile-time

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!