C++入門者向けコード実行エディタをつくった話


こんにちは、TumoiYorozuです。

現在は東京大学の博士 1 年生をしています。私は中学 1 年の頃からプログラミングをやっていて、特にアルゴリズムが大好きです。AtCoder をはじめとする 競技プログラミング にも取り組んでいて、高校生のときは情報オリンピックにも参加し、大学生のときは ICPC 国際大学対抗プログラミングコンテストに参加していました。現在はこれらの裏方をしています。




今回の記事では、

- 全くプログラミングを触れたことが無いような中高生を対象に
- 主にAtCoderや情報オリンピック、ICPCへの参加を考えている方を想定した
- C++を入門する際に最適な、ブラウザで使用できるコード実行エディタ

を制作した話と紹介です！

良ければこの記事の LGTM、また [GitHubリポジトリ](https://github.com/TumoiYorozu/SimpleCppEditor) の Star よろしくお願いします！


#制作物
こちらのページになります！
[Simple C++ Editor](https://tumoiyorozu.github.io/SimpleCppEditor/)

ページを開くと以下のように、コードとプログラムの入出力などだけが簡潔にまとめられたページが開きます。このページ上でコーディング・実行することができます。
![1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188347/4e85c737-72de-dc1f-e462-9ce99d325f24.jpeg)


# なぜ作ったの？
私は情報オリンピック関係の活動として、@drken さんらと一緒に、競技プログラミングを始めたばかり、興味を持ったばかりの中高生を対象に、C++の入門を教える活動、及び講師のリーダーをこの1年間行っています。2021年12月現在、この講座には **これまでのべ180人の中高生が参加** していただきました。
C++の入門とは、C++ どころか本当にプログラミングさえ行ったことが無い生徒に、if文やfor文、string や配列などを教えて、情オリ1次予選突破を目指すレベルです。

C++に限らずプログラミング入門の最初の関門は **†環境構築†** なわけですが *[要出典]* 、この講座ではブラウザ上でコードが実行できる [CS Academy](https://csacademy.com/workspace/) や [C++ shell](http://cppshell.com/)を使用してきました。このおかげで環境構築と言う入門者には非本質なのに挫折しやすいポイント[^1]をスキップして、最初にコードを実行してプログラミングを通じてコンピューターで計算させる体験を提供してきました。

ブラウザ上でのコード実行は、競技プログラミング熟練者でも AtCoder のコードテストなどのブラウザ上のコード実行環境を使用している人を考えると、入門したあとでも使える素晴らしいものです。
しかし指導を通じて入門者を観察してみると既存ツールではまだまだ改善の必要を感じ、今回の[Simple C++ Editor](https://tumoiyorozu.github.io/SimpleCppEditor/)を制作しました。

# どんなところが嬉しいの？

C++などがブラウザ上で実行できる他のサービスとしては、例えば以下のものがあります。

- AtCoder のコードテスト
- [CS Academy Workspace](https://csacademy.com/workspace/)
- [Wandbox](https://wandbox.org/)
- [C++ shell](http://cppshell.com/)
- [paiza.io](https://paiza.io/)

これらと比較して Simple C++ Editor は

- ソースコード自動保存機能
- 標準出力を順次表示するので、無限ループなど実行に時間がかかってるプログラムでもすぐに表示される。
- 行番号だけじゃなくて、ソースコード上にもコンパイルエラー表示
- 一部よくあるエラーメッセージは、英語に追記で易しめな日本語表示を追加
- Segmentation Fault, オーバーフローなどの実行時エラーも捕捉

などの利点があります。少し詳しく解説します。

## 1.ソースコード自動保存機能
CS Academy や C++ shell ではソースコードをブラウザに保存する機能が無いので、ブラウザを再読込するとコードが消えてしまいます。プログラミングを入門する中高生のPCは非力めな環境で参加する人も少なくなく、ブラウザが固まってしまったり、操作ミスで再読み込みなどしてしまいコードが消失してしまう事故が多発していました。
提出直前であれば直前のコードが正しそうであれば「実質AC!」と伝えることはできますが、それ以外のときは1から書き直さないといけなく、意欲を下げてしまう原因の1つでした。
自動保存機能のおかげでこの様な事故を防ぎ、また次回エディタを開いたときに前回のコードが残っているので、1から書き直さなくてもコードを流用などができるようになるなどの利点があります。

## 2.標準出力が順次表示される
オンライン実行環境のほとんどはコードを投げてその標準出力を得るバッチ式です。この場合、for文の講義などで無限ループを書いてしまった場合、実行時間制限で終了するまで結果が帰ってこないことになります。
Simple C++ Editor では wandbox の標準出力などを stream を使用して順次手元に送ってくれる機能を使用しているので、時間のかかるプログラムを実行してしまったときでも順次結果を表示させることができます。

## 3.行番号だけじゃなくて、ソースコード上にもコンパイルエラー表示
CS Academy や C++ shell などでは、コンパイルエラーを解析して行番号にエラー表示をすることはできますが、行内のどこにエラーがあるかは教えてくれません。

![2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188347/66c92448-5ee8-211d-5633-76cc63781127.jpeg)

本来コンパイルエラーには行番号の他にもエラーの開始位置の情報も入っているのですが、終了位置の情報が入っていないので一般的な実行環境ではこうなっているのでしょう。しかし Simple C++ Editor ではコンパイルエラーを頑張ってパース(解釈)[^2]して、エラーの詳しい位置をコード上に表示しています[^3]。
![3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188347/0b1a2057-d2e6-0179-6138-0fb59a3ed475.jpeg)

入門者に教えるときは、エラー発生時の対処を自分でもできるよう、直接原因を教えるのではなく、なんでエラーになるか考えてみよう、と指導しています。行番号だけだとピンとこない生徒も沢山いたのですが、この様に赤線を引いてくれればすぐに `;` 忘れだなと気づいてくれます。エラーの原因がずっと分からないのは学習意欲の大きな妨げなので、これは重要です。

## 4.エラーメッセージに日本語表示を追加
上の画像を見てもらうと分かるのですが、エラーの場所にマウスカーソルを置くと、エラーメッセージを表示してくれます。そこまでは良いのですが、なんと日本語表示も追記してくれており、更に単なる和訳だけではなく `どこかで「;」を忘れていませんか` と、何が悪かったかの提案もしてくれます。

Simple C++ Editor は裏で wandbox にコードを実行してもらっているのですが、メッセージは全て英語で提供されます。中高生にとって英語のメッセージはなれていない人も多く、学習意欲低下の原因の一つなのですが、ここで典型的なメッセージには和訳をつけることで、だいぶエラーへの抵抗が減ります。
更に、大抵のエラーメッセージは初心者にとって英語を理解できたとしても「だからなに？？」みたいな物が多いのですが、改善案も提供することで、何をすれば良いのかすぐに分かる仕様です。

改善提案メッセージにも様々あり、特にC++初心者入門講座の受講生は `std::endl` を `std::end1` と、エルをイチに書き間違えることが3人に2人くらいは発生していたのですが、この様なケースも「`endl`の間違いではありませんか」と提案してくれます。

![6.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188347/7910e26f-f11c-15da-f6f7-c400d3f36856.jpeg)


## 5.Segmentation Fault, オーバーフローなどの実行時エラーも捕捉
これは結構嬉しい機能だと個人的には思っています。
例えば以下のように、フィボナッチ数を求めるコードがあったとします。

```c++
#include <iostream>
using namespace std;

int main () {
    int a = 0;
    int b = 1;
    for(int i = 0; i < 50; i++) {
        int c = a + b;
        cout << b << endl;
        a = b;
        b = c;
    }
}
```
このコード、45番目のフィボナッチ数 1,836,311,903 を求めるまでは問題ないのですが、46番目の 2,971,215,073 は int で表現できる $2^{31}-1$ を超えてしまっているのでオーバーフローして -1,323,752,223 になってしまいます。
オーバーフローはプログラミング熟練者でもよくやる割に気づきにくいバグであり、これに涙した情オリ参加者も多いと思います。

しかし、このコードを Simple C++ Editor で実行すると、以下のようになります。
![4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188347/0e4eea7a-e224-e993-ffd9-487bbd8ae374.jpeg)
なんと実行時のオーバーフローを検出して、メッセージを表示してくれました。

また、オーバーフローと並んで典型的なバグである Segmentation Fault (配列外参照)、以下のコードは `a` の長さが 5 なので、`a[0]` から `a[4]` まではアクセスして構いませんが、`a[5]`にアクセスするとバグの原因です。

```c++
#include <iostream>
using namespace std;

int main () {
    int a[5] = {3, 1, 4, 1, 5};
    cout << a[0] << " ";
    cout << a[5] << endl;
}
```

厄介なのは、配列外参照をしたとしても必ず Segmentation Fault エラーが起きるかと言うとそうではなく[^4]、実行自体は行えて変な計算結果になることも少なくありません。特に 1 つはみ出した程度だとエラーが起こらないことが多々ありますが、バグを埋め込むときはこういった些細なことが多いです。
実際、wandbox で上のコードを実行すると、`3 32765` と、変な値を表示してプログラムはエラーを起こさずに終了しました。

しかし Simple C++ Editor で実行すると、以下のようにエラーの発生場所、何番目にアクセスしようとしたかが表示されます。
![5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188347/ceb81e57-6a86-e81e-3046-76a6e0b02039.jpeg)

これは競プロ中堅者でも結構嬉しい機能だと思います。

#終わりに
いかがでしたでしょうか。
**競技プログラミングを始めたばかりの人に伝えたいこと Advent Calendar** ということで、**競技プログラミングを始めたばかりの人(初心者 of 初心者) に伝えたいエディタの話** と言う趣旨で書きました。
ぜひ周りの「競技プログラミングを始めたばかりの人」に、この [Simple C++ Editor](https://tumoiyorozu.github.io/SimpleCppEditor/) を勧めてみてください！

またこのエディタは [GitHub上](https://github.com/TumoiYorozu/SimpleCppEditor) に全てのソースコードを公開しています。[gh-pages ブランチの Download ZIP](https://github.com/TumoiYorozu/SimpleCppEditor/archive/refs/heads/gh-pages.zip) リンクなどでPCのローカルに保存しても使えますし、HTMLやJavaScriptファイルなどの一式をWebページに置くだけでそのまま使用することもできます。なので自分好みにアレンジして自由に使用していただくこともできます。

また改善点があれば、GitHub にコードをプルリクエストなどで送っていただけると大変ありがたいです。コントリビュート是非お待ちしております。

良ければこの記事の LGTM、また [GitHub](https://github.com/TumoiYorozu/SimpleCppEditor) の Star よろしくお願いします！




[^1]: もちろん環境構築もプログラミングを自力で進めていく上では重要な力です。
[^2]: 頑張った
[^3]: たまに長さ計算がバグってるけど、それでもだいぶわかりやすい。
[^4]: Segmentation Fault エラーは「配列外参照」と言う意味ではなく、正確にはそのプログラムが使用してはいけない場所のメモリを触ったときに発生するエラーです。配列外を参照してもそのプログラムが他の変数で使用しているなどでたまたま使用していい場所であったら、Segmentation Fault にはなりません。また、配列外参照で書き換えた先のメモリがプログラムの実行に必要な場所であったならば(例えばどの関数から呼んだかを記録する場所だったら)、関数の実行終了時に変な場所に戻ってしまい、そこを実行しようとして Segmentation Fault になることもあります。