はじめに
諸事情あってWebassemblyを使ってみたくなった。ざっと見た感じWindowsで触るのは苦痛そうなので素直にUbuntu18.04から触っている。
さて、C++17でstd::optional
が追加されたが、これをWebassemblyで使いたい。
std::vector
なんかだとemscripten::register_vector
があってこれを使えばいいが、optionalには用意されていない。
ならば自分で書くか。
参考にするもの
emscripten::register_vector
を参考に書くことにする。実装は
にある。
できたもの
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指定もそこで外せるのでヘルパー関数はいらない。
使いかた
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
のようなコマンドで
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で計算する
これ。C++から例外投げるのは捕捉が面倒そうだったのでoptionalを使いたかった。
マイナンバーのチェックデジット自体は前に
で書いているんですが。