C++でC#のnameof的なことをする

###### (An English translation is available [here](https://dev.to/tomoyukiaota/exploring-c-equivalent-of-cs-nameof-operator-1p8c).)


# はじめに

C#には[nameof演算子](https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/nameof)という、変数、型、メンバーの文字列を取得する演算子があります。
[nameof演算子](https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/nameof)のページで紹介されている例としては、ログを取る際にメソッドの名前を渡す例があります。その際に文字列リテラルで渡すと、静的解析の際にメソッドの名前として扱われないので、リネームの際に自動で追従しません。[^1]
ここで、nameofを使うと、次のようになります。

```csharp
void f(int i) {  
   Log(nameof(f), "method entry");  
}
```
このように書くと、nameof(f)のfも識別子として静的解析の対象になるので、IDEのリネーム機能を使う際に、リネーム対象として取り扱ってくれます。

この記事では、C++でも同様の機能がないのか、ということについて述べます。

# `__func__`
C++では関数のボディで関数名を取得できる識別子として、[`__func__`](https://cpprefjp.github.io/lang/cpp11/func.html)があります。
例えば、次のように使います。

```cpp
void f()
{
  std::cout << "This function is " << __func__ << std::endl;
}
```
関数`f`を呼び出すと、`This function is f`が出力されます。

`__func__`は関数のボディ内でその関数名を取り出せますが、C#の`nameof`はどの関数でも、どのクラスでも、どの変数でも、識別子さえあれば使えます。C++でももっと汎用的なものがあればよいのですが・・・。

# マクロを定義する
C++の関数ライクなマクロでは、仮引数に`#`を前置すると、実引数に渡された値の文字列リテラルが取り出せます。例えば、次のコードは、`5+5 = 10`が出力されます。

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

よって、次のNAMEOFマクロを定義すれば、C#のnameofに近いことができます。

```cpp
#define NAMEOF(name) #name
```

使い方は次のとおりです。

```cpp
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;
}
```

次の出力が得られます。

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

ここで、myFunctionに対してリネームをかけると、NAMEOFの中のmyFunctionに対してもリネームをしてくれました。（ReSharperのリネーム機能で確認しています。）

・・・ですが、「C#のnameofに近いこと」と言ったのは訳があります。
ここで、定義されていない`myNonexistentWhatever`という識別子を渡したとします。

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

これは、コンパイルが通り、`myNonexistentWhatever`と出力されます。これは、マクロの展開はテキストの編集であり、`myNonexistentWhatever` が何であるのかということは考慮されないためです。マクロが展開されると`"myNonexistentWhatever"`という文字列リテラルになるので、上の例では、C++としてコンパイル可能なコードになります。
すなわち、最初にNAMEOFに識別子を渡すコードを書く際に、typoをしていないかどうか確認する必要があります。

と、欠点を述べましたが、少なくとも文字列リテラルをそのまま書くよりはマシだと思います。

このNAMEOFマクロを更にC#のnameof寄りに発展させたものとして、[bravikov/nameof](https://github.com/bravikov/nameof)というライブラリがあります。「C#のnameof寄り」というのは、例えば、`enum Foo {Bar}`に対して`nameof(Foo::Bar)`と書くと、Fooを省いて"Bar"と評価されるようになっていることです。

# おわりに
この話題は、ググってもあまり出てこず、NAMEOFマクロについては、[StackOverflowのコメント](https://stackoverflow.com/questions/38558180/does-qts-moc-c11-have-an-equivalent-of-cs-nameof-operator#comment64507414_38558180)をもとに、実験・考察したものです。ですので、他にも良い方法がないか気になります。ご教授いただけると助かります。




# 参考
https://stackoverflow.com/questions/14351971/what-does-x-inside-a-c-macro-mean
https://www.cprogramming.com/tutorial/cpreprocessor.html
https://wandbox.org/permlink/w2tGPxN8UHknzNuT

[^1]: ReSharperのリネーム機能では文字列リテラルも検索対象にできますが、結局、開発者が目で見てリネーム対象かどうか決める必要あります。