Webassemblyでもstd::optionalが使いたいのでemscripten binding書きます

# はじめに

諸事情あってWebassemblyを使ってみたくなった。ざっと見た感じWindowsで触るのは苦痛そうなので素直にUbuntu18.04から触っている。

さて、C++17で`std::optional`が追加されたが、これをWebassemblyで使いたい。

`std::vector`なんかだと`emscripten::register_vector`があってこれを使えばいいが、optionalには用意されていない。

ならば自分で書くか。

# 参考にするもの

`emscripten::register_vector`を参考に書くことにする。実装は

https://github.com/kripken/emscripten/blob/8654549be4f7d8ea8fe8970229bf8767b4a5d154/system/include/emscripten/bind.h#L1460-L1499

にある。

# できたもの

```cpp:register_optional.hpp
#ifndef EMSCRIPTEN_BINDINGS_REGISTER_STD_OPTIONAL_HPP_
#define EMSCRIPTEN_BINDINGS_REGISTER_STD_OPTIONAL_HPP_
#include <optional>
#include <emscripten/bind.h>
template<typename T>
emscripten::class_<std::optional<T>> register_optional(const char* name) {
    using emscripten::val;
    using OptionalType = std::optional<T>;
    bool (*has_value)(const OptionalType&) = [](const OptionalType& op){ return op.has_value(); };
    val (*value)(const OptionalType&) = [](const OptionalType& op) { return val(op.value()); };
    void (*reset)(OptionalType&) = [](OptionalType& op) { op.reset(); };
    return emscripten::class_<OptionalType>(name)
        .template constructor<>()
        .function("has_value", has_value)
        .function("value", value)
        .function("reset", reset)
        ;
}
#endif //EMSCRIPTEN_BINDINGS_REGISTER_STD_OPTIONAL_HPP_
```

もっとメンバ関数あるだろとか言わない。

なんか`register_vector`はだいぶ面倒なことしていたけど、lambda式は関数ポインタに変換できるし、noexcept指定もそこで外せるのでヘルパー関数はいらない。

# 使いかた

```cpp:index.cpp
#include "register_optional.hpp"
class OptionalTest{
public:
    static std::optional<double> foo(int n) {
        if(n == 0){
            return std::nullopt;
        }
        else{
            return { 7.2 / n};
        }
    }
};
EMSCRIPTEN_BINDINGS(my_class_example) {
    emscripten::class_<OptionalTest>("OptionalTest")
        .constructor<>()
        .class_function("foo", &OptionalTest::foo);
    register_optional<double>("optional<double>");
}
```

みたいに書くと

```
em++ -std=c++17 -O2 -s DISABLE_EXCEPTION_CATCHING=0 --bind -o index.js index.cpp
```

のようなコマンドで

```html:index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>bind_test</title>
    <script>
        var Module = {};
        Module.onRuntimeInitialized = () => {
                function ready(loaded) {
                    if (['interactive', 'complete'].includes(document.readyState)) {
                        loaded();
                    } else {
                        document.addEventListener('DOMContentLoaded', loaded);
                    }
                }
                ready(() => {
                    console.log(Module.OptionalTest);
                    const op = Module.OptionalTest.foo(2);
                    console.log(op);
                    const foo = document.getElementById("foo");
                    if(op.has_value()){
                        foo.innerText = op.value();
                    }
                    else{
                        foo.innerText = "none";
                    }
                })
            }
    </script>
    <script src="index.js"></script>
</head>
<body>
    <p id="foo">aaa</p>
</body>
</html>
```

こんなHTMLおいておけばいい感じに使える。

# 余談

一体これなにに使いたかったかというと、

[マイナンバーのチェックデジットをWebassemblyで計算する](https://yumetodo.github.io/individual_number_check_digit_calcurator/)

これ。C++から例外投げるのは捕捉が面倒そうだったのでoptionalを使いたかった。

マイナンバーのチェックデジット自体は前に

[C++でマイナンバーのチェックデジットを計算する](https://qiita.com/yumetodo/items/600ca0df422010cbc4c1)

で書いているんですが。