12

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.

Humam Helfawi
  • 19,566
  • 15
  • 85
  • 160
  • 1
    Can you provide the real use case for the sought functionality? In this example you could simply write `std::string x = "some_int";` – Leon Aug 01 '16 at 10:27
  • 4
    If you don't mind about macro, you can use [preprocessor's stringification](https://gcc.gnu.org/onlinedocs/cpp/Stringification.html#Stringification) – rocambille Aug 01 '16 at 10:27
  • 1
    You could use a makro `#define NAME(VAR) #VAR`. Also works if everything else, e.g `NAME(123) -> "123"` – Albjenow Aug 01 '16 at 10:27
  • @wasthishelpful nice option.. I did not know it is exist.. thanks – Humam Helfawi Aug 01 '16 at 10:29
  • @Leon... Yes I am writing a logger for cv::Mat object.. I want to pass only the cv::Mat variable to the logging method and want it to automatically save it on the disk using its variable name – Humam Helfawi Aug 01 '16 at 10:30
  • @Leon One use case I can think of is using it to throw `std::invalid_argument` with a function parameter name in a future-proof manner. (If the parameter name changes later down the line, you will get a compiler error, unless you also change the name, passed to exception constructor. With `std::string` you don't get any errors, if you forget to change the parameter name string.) So, basically, the same use case as for [C#'s `nameof` operator](http://stackoverflow.com/questions/31695900/what-is-the-purpose-of-nameof). – TerraPass Aug 01 '16 at 10:31
  • 1
    You ask for variable name but call your hypothetical solution `type_name`. Which is it? – Lightness Races in Orbit Aug 01 '16 at 10:36
  • @LightnessRacesinOrbit Sorry edited – Humam Helfawi Aug 01 '16 at 10:45
  • See, I don't really get the utility of this. If you already have to write `variable_name::value`, why don't you just write `"some_int"` and be done with it? I suppose you get compile-time checks against typos, but otherwise... – Lightness Races in Orbit Aug 01 '16 at 10:50
  • @LightnessRacesinOrbit as I commented above " I am writing a logger for cv::Mat object.. I want to pass only the cv::Mat variable to the logging method and want it to automatically save it on the disk using its variable name" I want to make the logger interface cleaner while I do not care too much what is inside it – Humam Helfawi Aug 01 '16 at 10:51
  • @iammilind Is there any chance we could reopen either this question, or [How to print a variable's name in C++?](http://stackoverflow.com/questions/9444988/how-to-print-a-variables-name-in-c)? I mean, the latter was closed as a duplicate of "Programmatic way to get variable name in C?", even though (at least IMHO) there may be a C++-specific way to better address this problem. – TerraPass Aug 01 '16 at 11:08
  • @TerraPass, OK I have reopened the Q, as it's not the exact duplicate of [What are the applications of the ## preprocessor operator and gotchas to consider?](http://stackoverflow.com/q/216875/514235), but it's the exact duplicate of [How to print a variable's name in C++?](http://stackoverflow.com/questions/9444988/how-to-print-a-variables-name-in-c). You may post the solution and let the others or **moderator** take the decision of closing this Q. – iammilind Aug 01 '16 at 11:09
  • @HumamHelfawi: But you already have to write the variable name in order to reference the variable. – Lightness Races in Orbit Aug 01 '16 at 11:16
  • 1
    @LightnessRacesinOrbit See my answer. We *do* have to write the variable name, but if we implement the stringification macro in a certain way, compiler will be able to whack us across the fingers with a compiler error, if we mistype/forget to change the variable's name. This is not the case if we simply use a string literal. – TerraPass Aug 01 '16 at 11:24
  • @TerraPass: As I alluded to earlier, that seems like a fringe benefit for the effort required, but I can see it I guess. The other problem is that an object may have multiple names so from a purist perspective this doesn't really make sense anyway. – Lightness Races in Orbit Aug 01 '16 at 11:25
  • 1
    @terra Parameter names at interface and in body of function are unrelated. So not future proofed. – Yakk - Adam Nevraumont Aug 01 '16 at 12:44

3 Answers3

13

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.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • 4
    @LightnessRacesinOrbit: Your comment indicates that this is not an answer. On the contrary, it does exactly what's asked for. A comment about the impracticality of the goal should go *on the question*, not on each answer. – Cheers and hth. - Alf Aug 01 '16 at 10:39
  • 1
    Maybe a compile time check of the argument for `NAME_OF` would be useful; something like `#define NAME_OF( v ) ((void)v, #v)`? – Niall Aug 01 '16 at 11:10
  • 2
    @Niall: Yes, I considered that, but didn't do it because it wouldn't work with type names then. However that way of thinking (that I did) is wrong; there's no need to automate everything. The programmer certainly knows whether the code is intended to deal with variable or type name, and can express this. – Cheers and hth. - Alf Aug 01 '16 at 11:48
10

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!
TerraPass
  • 1,562
  • 11
  • 21
  • Simple `(void*)&v` is enough, if the C++11 is not in the reach. Anyways, good observation. – iammilind Aug 01 '16 at 11:17
  • If the address-of operator is overloaded, a `(void)v` would be enough/better. – Niall Aug 01 '16 at 11:28
  • @Niall This breaks the case where we pass a literal: `NAMEOF(42)` no longer leads to a compiler error. – TerraPass Aug 01 '16 at 11:33
  • 1
    Doesn't break anything - it just prints the "name" of the literal as "42" - doesn't sound wrong. The OP didn't have anything on that not being a valid use case anyway; either way, it's a trade-off. – Niall Aug 01 '16 at 11:37
  • Thank you for your suggestions, I've added them to the answer. – TerraPass Aug 01 '16 at 11:57
  • @Yakk Then using `(void)variable`, as Niall suggested, would be the most reliable approach, correct? – TerraPass Aug 01 '16 at 12:49
5

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
Community
  • 1
  • 1
Leon
  • 31,443
  • 4
  • 72
  • 97