C++をC言語の上位互換たらしめているものの正体

# 概要
C++はC言語の上位互換である、というなんとなくの認識でこれまで実装していました。
ですが、それぞれの言語の言語仕様の対応状況を調査した結果、コンパイラが上手く隠してくれている部分があると分かりました。
その結果をもとに、[前回の記事](https://qiita.com/HiroInTFLab/items/8471071fd998b8a62c02)で取り上げたパーサーのパス率向上につなげました。
これらが何かの一助となれば幸いです。

# 環境やバージョン
+ Windows10 Pro
+ g++ 11.2.0（cygwin経由）

# 言語仕様
プログラミング言語には通常、その言語毎に仕様が定義されています。言語仕様については

https://thinkit.co.jp/free/article/0711/5/1/

を参照してください。私の雑な理解では、こう文字を入力すると、コンピュータはそれに基づいて計算して、こんな結果を返します、ということが事細かに定義されたものです。もちろん、C言語にもC++にも言語仕様はあり、それらは定期的に更新されています。
いずれも最新ではないものの、今回の記事の内容に関係するものとして、C言語であればC99の言語仕様は[ここ](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf)から、C++であればC++14の言語仕様は[ここ](https://isocpp.org/wiki/faq/cpp14)から参照できます。

# 初期化指示子
さて、前回構文解析したESP32のソースコードのうち、
```arduino-esp32-master\cores\esp32\FirmwareMSC.cpp```
でこんなエラーがANTLRのruntimeから出ていました。

```
line 77:6 mismatched input '.' expecting '}'
line 78:6 no viable alternative at input '.'
```

該当する行を含むブロックを示すと

```cpp
76: const esp_partition_pos_t running_pos  = {
77:       .offset = partition->address,
78:       .size = partition->size,
79: };
```

こうなっています。これは、指示初期化子と呼ばれるもので、構造体において変数を宣言しつつ、各メンバをドットで指定し、かつ、代入演算子で初期値を指定する、というもので、言語仕様では```指示初期化子```という名称で定義されているようです。この書き方を知って以降、当たり前のように使っているのですが、改めてC言語の言語仕様を調べてみると、

https://www.buildinsider.net/language/clang/01

C99から導入されているとわかりました。一方、C++の言語仕様によると

https://cpprefjp.github.io/lang/cpp20/designated_initialization.html

導入はC++20であることが分かりました。C++での日本語名はここでは```指示付き初期化```とされています。翻訳した人の違いか、C++では同じに見えて若干意味合いが違うのか、定かではありませんが、以降の説明では```指示初期化子```で統一します。

# g++でなぜエラーにならない？
C++の言語仕様での対応状況から、指示初期化子はC++20から導入にも関わらずESP32のソースコード
```arduino-esp32-master\cores\esp32\FirmwareMSC.cpp```
がESP32開発元でコンパイルできているのはなぜか、g++を使ってその理由を探ります。

## 言語仕様のバージョン確認
インストール済みのg++はcygwin経由で導入したもので、バージョン11.2.0です。
導入したg++がC++のどのバージョンの言語仕様をデフォルトとしているかを確かめる方法は

https://mikoto2000.blogspot.com/2020/08/gcc-std.html

が詳しく、ここで紹介されている方法を試したところ、
```define __cplusplus 201703L```
と出たきたので、C++17とみていいでしょう。
ますますエラーにならない理由がわかりません。

## コンパイルの準備
ということであれば、実際にコンパイルしてみるまでです。
ただし、コンパイラによって実行形式ファイルを作成したいわけではなく、コンパイルすることが目的なので、ここでは中間ファイルの生成ができれば良いです。

https://kaworu.jpn.org/cpp/g++

を参考に、中間ファイルを生成する```-c```オプションを使用します。
詳細は省略しますが、
1. コンパイルに必要なdefineの調査と指定
1. 不足しているinclude pathの調査と指定
1. 厳格な型の不適合を指摘するオプションの無効化

などの細かい作業によってコンパイルできる状況までエラーを抑え込んだ結果、cygwinのコマンドプロンプトに入力した内容は次のとおりになりました。

<details><summary>コマンドプロンプト</summary>

```
g++ -fpermissive -I"variants/esp32" -I"tools/sdk/esp32/include/driver/include" -I"tools/sdk/esp32/include/log/include" -I"tools/sdk/esp32/include/bootloader_support/include" -I"tools/sdk/esp32/include/app_update/include" -I"tools/sdk/esp32/include/spi_flash/include" -I"tools/sdk/esp32/include/lwip/include/apps" -I"tools/sdk/esp32/include/lwip/port/esp32/include" -I"tools/sdk/esp32/include/lwip/lwip/src/include" -I"tools/sdk/esp32/include/tcpip_adapter/include" -I"tools/sdk/esp32/include/esp_eth/include" -I"tools/sdk/esp32/include/esp_netif/include" -I"tools/sdk/esp32/include/esp_wifi/include" -I"tools/sdk/esp32/include/heap/include" -I"tools/sdk/esp32/include/newlib/platform_include" -I"tools/sdk/esp32/include/esp_timer/include" -I"tools/sdk/esp32/include/esp_system/include" -I"tools/sdk/esp32/include/soc/include" -I"tools/sdk/esp32/include/hal/esp32/include" -I"tools/sdk/esp32/include/hal/include" -I"tools/sdk/esp32/include/esp_hw_support/include" -I"tools/sdk/esp32/include/soc/esp32/include" -I"tools/sdk/esp32/include/freertos/include/esp_additions/freertos" -I"tools/sdk/esp32/include/freertos/include/esp_additions" -I"tools/sdk/esp32/include/esp_rom/include" -I"tools/sdk/esp32/include/xtensa/esp32/include" -I"tools/sdk/esp32/include/xtensa/include" -I"tools/sdk/esp32/include/freertos/port/xtensa/include" -I"tools/sdk/esp32/include/freertos/include" -I"tools/sdk/esp32/include/config" -I"tools/sdk/esp32/include/esp_event/include" -I"tools/sdk/esp32/include/esp_common/include" -DCONFIG_TINYUSB_MSC_ENABLED -c cores/esp32/FirmwareMSC.cpp
```
</details>

:::note info
当該ファイル中の一部箇所においてC++20を必要とする実装があったので、一部手直しした上で、次の検討に臨んでいます。
:::

## オプション指定で確かめる
ここまででやはりコンパイルできる状況となりました。
検討の準備が整ったので、ここからはg++のオプションをいくつか変更して、コンパイルの可否についてみてみます。
事前に検討したところから対象のオプションは、```std```と```pedantic-errors```としました。pedantic-errorsはGCC拡張を無効にするオプションで、それについては[こちら](https://qnighy.hatenablog.com/entry/2015/09/26/192104)の記事が詳しいです。
指示初期化子におけるコンパイル時のエラーの有無をまとめたものが以下になります。
|  | std=c++20 | std=c++17 | std=c++14 | std指定なし |
|:-:|:-:|:-:|:-:|:-:|
| pedantic-errors指定なし | エラーなし  | エラーなし  | エラーなし  | エラーなし |
| pedantic-errors指定あり  | エラーなし  | エラーあり  | エラーあり  | エラーあり |

参考までにg++が出力するエラー内容は次のようになります。

<details><summary>エラー内容</summary>

```
cores/esp32/FirmwareMSC.cpp:77:7: エラー: C++ designated initializers only available with ‘-std=c++20’ or ‘-std=gnu++20’ [-Wpedantic]
   77 |       .offset = partition->address,
      |       ^
cores/esp32/FirmwareMSC.cpp:78:7: エラー: C++ designated initializers only available with ‘-std=c++20’ or ‘-std=gnu++20’ [-Wpedantic]
   78 |       .size = partition->size,
      |       ^
```
</details>

pedantic-errors指定なし、つまり、GCC拡張が有効であることで、C++20指定でなくとも指示初期化子のコンパイルでエラーがでないとわかります。この結果から、g++はC99に基づいて実装されたソースコードであってもコンパイルできるように配慮した設計となっている、ということが実感できました。
この結果だけを持って言うのは大げさかもしれませんが、C++をC言語の上位互換たらしめているものの正体はコンパイラである、といえるのではないでしょうか。

# パス率改善へ
以上を踏まえ、GCC拡張の一部を前回作成したパーサーに組み込めば、パス率の改善が見込めそうです。
ここからは前回取り上げたパーサーの変更について説明します。

## CPP14パーサ + GCC拡張の一部 = ？
ところで、CPP14パーサにGCC拡張の一部を足したものはなんと呼ぶべきでしょうか。無理に名前をつける必要はありませんが、いろいろ調べた結果、

https://gcc.gnu.org/projects/cxx-status.html#cxx11

にある、```C++14 Support in GCC```が良さそうなので、以降はそれを使うことにします。

## initializerを見直す
C++14 Support in GCCとなるようCPP14Parser.g4に変更を加えていきます。
初期化にかかわるルールは```initializer```です。[C言語のルール定義](https://github.com/antlr/grammars-v4/blob/master/c/C.g4)を参考にして、次のように変更を加えました。

```diff
initializerList:
-	initializerClause Ellipsis? (
+	designation? initializerClause Ellipsis? (
-		Comma initializerClause Ellipsis?
+		Comma designation? initializerClause Ellipsis?
	)*;
+
+ designation
+    :   designatorList Assign
+    ;
+
+ designatorList
+    :   designator+
+    ;
+
+ designator
+    :   LeftBracket constantExpression RightBracket
+    |   Dot Identifier
+    ;
```

## 84%に上昇
[前回の記事](https://qiita.com/HiroInTFLab/items/8471071fd998b8a62c02)と同様の手順で、パーサーを生成し同じファイルに対して構文解析をかけた結果、パス率は84%となりました。
前回の約70%からはだいぶ向上した考えているものの、まだまだ向上の余地があるという状況です。```C++14 Support in GCC```を目指して次回も取り組んでいこうと思います。