DEV Community 👩‍💻👨‍💻

Tomoyuki Aota
Tomoyuki Aota

Posted on • Updated on

Exploring C++ equivalent of C#'s nameof operator

(A Japanese translation is available here.)

In C#, there is nameof operator which can be used to get the string name of a variable, type, or member.
One of useful cases is to log a message with the name of the method. In this case, if we use the string literal of the method name, IDE's rename feature will not automatically rename the string literal because it is not recognized as an identifier.
If we use nameof, it will be like this:

void f(int i) {
   Log(nameof(f), "method entry");  
}

If we use nameof like this, f in nameof(f) will be treated as an identifier, so it will be renamed when renaming the method.

In this article, I'm going to explorer features in C++ which are similar to C#'s nameof operator.

__func__

C++ has __func__ which can be used in the body of a function. __func__ is a pre-defined identifier which holds the function name.
An example is like this:

void f()
{
  std::cout << "This function is " << __func__ << std::endl;
}

When f is called, This function is f will be displayed.

__func__ can retrieve the function name within its body, but C#'s nameof is more versatile because it can be used with any method, function, and variable as long as the identifiers are available.

Define a macro

In a function-like macro, when # is placed right before the parameter, the string literal of the argument value is retrieved. For example, this code displays 5+5 = 10.

#define PRINT(int) printf(#int " = %d\n",int)
PRINT(5+5);

Therefore, the following NAMEOF macro will behave similar to C#'s nameof.

#define NAMEOF(name) #name

The usage will be like this:

struct MyStruct
{
    int myMemberVariable = 0;
};

class MyClass
{
public:
    int myMemberFunction() { return 0; }
};

int myGlobalVariable = 0;

void myFunction()
{
}

int main()
{
    std::cout << NAMEOF(MyStruct) << std::endl;
    std::cout << NAMEOF(MyStruct{}.myMemberVariable) << std::endl;
    std::cout << NAMEOF(MyClass{}.myMemberFunction) << std::endl;
    std::cout << NAMEOF(myGlobalVariable) << std::endl;
    std::cout << NAMEOF(myFunction) << std::endl;
}

The code above will display the following:

MyStruct
MyStruct{}.myMemberVariable
MyClass{}.myMemberFunction
myGlobalVariable
myFunction

If I use rename feature to myFunction, the myFunction in NAMEOF(myFunction) is renamed. (I used ReSharper's rename feature.)

However, I said "NAMEOF macro will behave similar to C#'s nameof". NAMEOF macro is not exactly the same as C#'s nameof operator.

If we pass an undefined identifier, myNonexistentWhatever, like this:

    std::cout << NAMEOF(myNonexistentWhatever) << std::endl;

Build succeeds, and myNonexistentWhatever will be displayed. This is because macro expansion is just like editing plain text, which means that myNonexistentWhatever will not be treated as an identifier. When macro expansion is done, the evaluation result is "myNonexistentWhatever" (a string literal), so it will be a valid C++ code.

Therefore, we need to confirm that there is no typo when using NAMEOF macro in the first place.

That being said, using NAMEOF macro is better than simply writing string literal.

There is a library, bravikov/nameof, which enhances the functionality to be more akin to C#'s nameof. For example, when we write nameof(Foo::Bar) for enum Foo {Bar}, it will be evaluated as "Bar" without preceding Foo.

Lastly

I googled this topic but could not find a lot of information. NAMEOF macro in this article is taken from a comment on StackOverflow and this article is created by elaborating it. Therefore, I'd be grad if some convention/tips/whatever are shared regarding this topc.

Reference

Top comments (0)

🌚 Friends don't let friends browse without dark mode.

Sorry, it's true.