Tomoyuki Aota
Posted on December 19, 2018
(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
Posted on December 19, 2018
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.