C++11/14/17インタプリタ環境 Jupyter-Cling

# Jupyter-Cling

以前C++インタプリタClingについて、「[C++11インタプリタ Clingのビルド](http://qiita.com/mugwort_rc/items/3dead38e953ebb294051)」という記事を書きました。

C++のインタプリタなんだからインタプリタ環境のJupyterから呼び出せるんじゃないかと[Jupyter kernels](https://github.com/jupyter/jupyter/wiki/Jupyter-kernels)のページを確認したところ、案の定ありました。

## Clingインストール

[CERNのサイトでバイナリも配布](https://root.cern.ch/download/cling/)されていますので**通常はこちらを使うのが良いでしょう**。

バイナリ版を使用する場合は[次に](#jupyter)進んで下さい。

### Cling Packaging Tool

Clingのビルドに何やらよさげなスクリプトが用意されているので次の手順でClingをビルドします。
なお、Clingのビルドの際にClangをビルドするため、非常に時間がかかります。

```bash
wget https://raw.githubusercontent.com/root-project/cling/master/tools/packaging/cpt.py
chmod +x cpt.py
./cpt.py --check-requirements && ./cpt.py --create-dev-env Debug --with-workdir=./cling-build/
```

なお、`./cpt.py --check-requirements`のところでCMakeが既に入っていないとコケます。
CMakeさえ入っていれば後は自動でaptを呼んで必要なライブラリを取得してくれるようです。

`cling-build/ling-Ubuntu-16.04-x86_64-0.5~dev-4681e80`といったフォルダができるので、任意の場所（`/opt/cling`等）にシンボリックリンクを張るなどします。

## Jupyter

`/share/cling/Jupyter/kernel/`にJupyterのkernelが用意されています。

```bash
pip install -e .
jupyter-kernelspec install --user cling-cpp17
jupyter-kernelspec install --user cling-cpp14
jupyter-kernelspec install --user cling-cpp11
```

jupyterを起動する際、clingにパスを通す必要がありますので注意して下さい。

```bash:run.sh
#!/usr/bin/env bash
PATH=/opt/cling/bin:$PATH jupyter notebook
```

## Hello World

![image.png](https://qiita-image-store.s3.amazonaws.com/0/26144/6e45d4f4-0ac4-2bb2-29cc-5ee794f001a6.png)

## 気になったところ

### c++17が起動しない

c++17を選択すると次のようなエラーを吐いてkernelが起動しません。

エラーを見た感じc++1zしか対応していないようです。（Ubuntu 16.04由来？）

```
error: invalid value 'c++17' in '-std=c++17'
note: use 'c++98' or 'c++03' for 'ISO C++ 1998 with amendments' standard
note: use 'gnu++98' or 'gnu++03' for 'ISO C++ 1998 with amendments and GNU extensions' standard
note: use 'c++11' for 'ISO C++ 2011 with amendments' standard
note: use 'gnu++11' for 'ISO C++ 2011 with amendments and GNU extensions' standard
note: use 'c++14' for 'ISO C++ 2014 with amendments' standard
note: use 'gnu++14' for 'ISO C++ 2014 with amendments and GNU extensions' standard
note: use 'c++1z' for 'Working draft for ISO C++ 2017' standard
note: use 'gnu++1z' for 'Working draft for ISO C++ 2017 with GNU extensions' standard
In file included from input_line_2:1:
/opt/cling/include/cling/Interpreter/RuntimeUniverse.h:162:26: warning: default template arguments for a function template are a C++11 extension [-Wc++11-extensions]
      template <class T, class = T (*)() /*disable for arrays*/>
                         ^       ~~~~~~~
input_line_2:3:62: error: expected function body after function declarator
extern "C" int __cxa_atexit(void (*f)(void*), void*, void*)  noexcept;
                                                             ^
input_line_2:5:55: error: use of undeclared identifier '__cxa_atexit'
extern "C" int atexit(void(*f)())  throw ()  { return __cxa_atexit((void(*)(void*))f, 0, __dso_handle); }
                                                      ^
python3: cling-build/cling-src/tools/clang/lib/AST/DeclBase.cpp:1334: void clang::DeclContext::removeDecl(clang::Decl*): Assertion `(DC->hasExternalVisibleStorage() || Pos != Map->end()) && "no lookup entry for decl"' failed.
```

応急処置として次のような修正を加えます。

```diff:/share/cling/Jupyter/kernel/cling-cpp17/kernel.patch
diff -u a/cling-cpp17/kernel.json b/cling-cpp17/kernel.json
--- a/cling-cpp17/kernel.json	2017-09-06 04:46:58.764971000 +0900
+++ b/cling-cpp17/kernel.json	2017-09-06 07:16:59.802765356 +0900
@@ -4,6 +4,6 @@
       "jupyter-cling-kernel",
       "-f",
       "{connection_file}",
-      "--std=c++17"
+      "--std=c++1z"
   ]
 }

```

```diff:/share/cling/Jupyter/kernel/clingkernel.patch
diff -u a/clingkernel.py b/clingkernel.py
--- a/clingkernel.py	2017-09-06 04:46:58.744971000 +0900
+++ b/clingkernel.py	2017-09-06 07:14:08.954707308 +0900
@@ -92,7 +92,7 @@
     flush_interval = Float(0.25, config=True)
 
     std = CaselessStrEnum(default_value='c++11',
-            values = ['c++11', 'c++14', 'c++17'],
+            values = ['c++11', 'c++14', 'c++17', 'c++1z'],
             help="C++ standard to use, either c++17, c++14 or c++11").tag(config=True);
 
     def __init__(self, **kwargs):
```

もう一度インストールを行えばc++17のkernelが起動します。

```bash
pip install -e .
jupyter-kernelspec install --user cling-cpp17
```

### clingが落ちれば容赦なく死ぬ

clingが落ちるコードを実行すると容赦なくkernelが落ちます。

ログを一切出さずに再起動するため、どこが悪いのか判別できず、ある程度の勘所と根気が必要です。
しかし、コンソールでclingが落ちると全ての入力が失われる一方で、Jupyterでは入力履歴がセルで残るため、やり直しが非常に簡単です。

C++の学習などの用途でJupyterを使わない手は無いのではないでしょうか。