kolmas.tech note

雑記と思索、偏った技術の覚え書き

今更WebAssembly・続 ー Cの関数をJavaScriptから呼ぶ

前エントリの続き。 blog.kolmas.tech

ライブラリ的なものを作る

前エントリで触れた通り、私がWebAssemblyを知ったきっかけは、Cで実装されたライブラリをクライアントサイドJavaScriptから利用しているものを見た事である。即ち、Cで実装された関数をJavaScript側から任意のタイミングで呼び出せるようにしている。前エントリのHello worldは、単純にCのmain関数が一回実行されただけであった。ここでは、この任意のCの関数を任意のタイミングで呼ぶ、という点に着手したい。

何はともあれ、まずは呼び出される側のCの関数=ライブラリ的なものを作る。

int add(int a,int b){
    return a+b;
}

int sub(int a,int b){
    return a-b;
}

シンプルに、int型の二引数を取って、その和を返すadd関数と、差を返すsub関数を定義した。

前エントリでも参照したMDN Web Docsの記事によれば、JavaScript側から任意のタイミングで呼び出す関数には、EMSCRIPTEN_KEEPALIVEを付けるそうだ。これは<emscripten/emscripten.h>に定義されている。

#include <emscripten/emscripten.h>

EMSCRIPTEN_KEEPALIVE int add(int a,int b){
    return a+b;
}

EMSCRIPTEN_KEEPALIVE int sub(int a,int b){
    return a-b;
}

これを前回と同様にemccコマンドでコンパイルする。前エントリに引き続き%はプロンプトである。

% emcc samplelib.c -o samplelib.html

一方、以下のEmscriptenの公式ドキュメントには違う方法が説明されている。 emscripten.org Cのソースコードは元のまま1で、emccコマンドの-sEXPORTED_FUNCTIONSオプションで対象となる関数を指定する。元の関数名の先頭にアンダースコアを付けて指定する。カンマで区切って複数関数を指定できる。

% emcc samplelib.c -o samplelib.html -sEXPORTED_FUNCTIONS=_add,_sub

個人的にはこちらの方が、元のCのソースコードをそのまま使えているという点で好みである。以降の例ではこちらの方式を使うことにする。一応、本エントリ以降の記述については、どちらの方法でコンパイルしても同様に働く事は確認している。

実際に呼び出す・弄り倒す

コンパイルの結果、前エントリのHello worldの例と同様にsamplelib.wasmsamplelib.jssamplelib.htmlが出来ているので、例によってウェブサーバ経由でHTMLファイルにアクセスする。これも前エントリと同様にコンソール風画面が出てくるが、今回はmain関数がないので、そこには何も表示されない。もっとも、作成したaddsub両関数にしても、標準出力に何かを書き出したりしていないが。

emccコマンドで出力されるJavaScript、ここではsamplelib.jsファイルはグルーコードと呼ぶらしい。WebAssemblyとJavaScriptの間を繋ぐグルー=接着剤。グルーコードは手ずから作ることも当然出来るらしい2が、Emscriptenが出力したものをそのまま使うのが手軽であるようだ。これにはModuleというオブジェクトがあり、このオブジェクトがWebAssembly関連の諸々を担っている。先のHTMLにアクセスしているブラウザ3のコンソールで弄ってみよう。

何か出来てる。
早速、このModuleオブジェクトの中に_add_subなるメソッドが見つかる。冒頭にアンダースコアが付いている4が、先程作成したCの関数そのもののようである。呼び出してみよう。
想定通りの動作、ではある。
Module._add(3,2)の返り値が5、Module._sub(3,2)の返り値が1、どちらも想定通りの動作である。

こうなるとちょっと遊んでみたくなる。Cと比べたらラフな型システム5であるJavaScriptから、色々な引数を焚べてこのメソッド、ひいてはCの関数を呼び出してみようではないか。

成程面白い実行結果。
C側の関数は整数型の引数を取るものだが、そこに文字列を焚べてみたところ0扱いされているようである。明らかに型変換できない引数を焚べると別の値で代替されるのだろうか。この辺りをグルーコードが処理しているという事だろう。実数を焚べてみた例では整数に丸められている様子だが、これについてはC的にも普通の挙動であるから、グルーコード側とC=WebAssembly側のどちらで変換されているのかは特定できない。
数値型に変換できそうな文字列は変換してくれている。
Module._add("3","2")の返り値が5になるあたり、グルーコードは、出来る限り型変換しようとしてくれてはいるようである。Number関数かparseInt関数あたりでも使っているのだろうか。そしてNaNは0扱いである、と。

折角だから和と差だけでなく、四則演算を揃えてみよう。C側で以下のようにmul及びdiv関数を新しく定義し、再度コンパイル6する。

int add(int a,int b){
    return a+b;
}

int sub(int a,int b){
    return a-b;
}

int mul(int a,int b){
    return a*b;
}

int div(int a,int b){
    return a/b;
}

となれば、やはり次に試してみたいのは0除算である。引数の型変換は問題なく通過するだろうから、C=WebAssembly側でエラーになる筈である。

WebAssemblyファイルの特定位置でのエラー、となっている。
Chromeの開発者ツールでWebAssembly内でエラーが発生した箇所まで見る事が出来る。
WebAssemblyという名前そのものと言えばまさにそうであるが、如何にもアセンブリ言語らしさを感じる風合いである。これを読み解くのは一筋縄ではいくまい。


  1. 即ちEMSCRIPTEN_KEEPALIVEを付けず、ひいては<emscripten/emscripten.h>#includeすることもない。本当に冒頭に掲載したままのCのソースコードそのもの。
  2. 参考。qiita.com
  3. 検証環境はGoogle Chrome。
  4. コンパイル時の-sEXPORTED_FUNCTIONSオプションでもアンダースコアを付けている。そういう文化?なのか。
  5. プログラマ視点から見る型システムがラフであるという事は、処理系にしてみたらよっぽど高度な処理をしているという事なのであって、その点でラフと言ってしまうのは心苦しい節はある。
  6. 当然、コンパイル時の-sEXPORTED_FUNCTIONSオプションには、今回追加した二関数も追加する。

今更WebAssembly ー とりあえずHello world

WebAssemblyというものを知ったのは数年前である。仕事で扱ったとあるライブラリ1がCで実装されていて、そのJavaScript環境向けラッパーにおいて利用されていたのだ。今日日そんなものがあるのか、と感心したものだが、軽く調べてみたら2015年には発表されていたらしい。周回遅れである。 developer.mozilla.org ja.wikipedia.org

ブログ副題で「雑記と思索、偏った技術の覚え書き」を名乗っているくせに、更に言えば.techドメインなど使っているくせに、久しく技術ネタを書いていないので、たまにはそういう話題も扱いたい所。丁度良いネタである。n番煎じも良い所であろうが。

WebAssembly

WebAssemblyが何であるかは上掲のサイト等で既に解説されているので、今更私が新たに書くようなことはない。MDNの解説によればこうである。

WebAssembly は現代のウェブブラウザーで実行できるコードの一種です。ネイティブに近いパフォーマンスで動作する、コンパクトなバイナリー形式の低レベルなアセンブリー風言語です。さらに、 C/C++、C# や Rust などの言語のコンパイル先となり、それらの言語をウェブ上で実行することができます。 また、JavaScript と並べて実行するように設計されており、両方を一緒に動作させることができます。 --MDN WebAssembly

要は、C等の一般にウェブブラウザ上で動作させる事が想定されない言語によるプログラムであっても、WebAssemblyにコンパイルしてやればウェブブラウザ上で実行可能になるのである。クロスコンパイルという言葉も昔からある訳で、道理ではある。一方で、ウェブにおける一昔前のクライアントサイド開発の世界から見たら、すごい事になっているな、という感想も持つ。今日日、幅広なブラウザがWebAssemblyの処理系を持っているようである。 caniuse.com

C等のソースコードをWebAssemblyのバイナリにコンパイルするクロスコンパイラには、Emscriptenというものを使うのが主流であるようだ。 emscripten.org

EmscriptenでHello worldする

初めて扱うプログラミング環境においては、何はともあれまず最初にHello worldせねばなるまい。以下のCによるソースコードを準備する。Cのソースコードとしては、極めて標準的なHello worldである。

#include <stdio.h>

int main(){
    printf("Hello world!\n");
    return 0;
}

これをEmscriptenを使ってコンパイルする。以下のようにコマンド実行する。尚、本エントリにおける検証環境はDebianであり、Emscriptenはaptで導入。%はプロンプト。

% emcc hello.c -o hello.html

これだけ見ると、emccコマンドの「なり」はgccに似ている。ソースコードを保存したファイルを引数に取り、-oオプションで出力先を指定する。ここに.htmlで終わるファイル名を指定すると、WebAssemblyのバイナリに加えてそれを呼び出すJavaScriptプログラム、また上記ファイル名のHTMLが出力される。上記例であれば、以下のようである。

% ls -l hello.*
-rw-r--r-- 1 kolmas kolmas    72  5月  6 13:57 hello.c
-rw-r--r-- 1 kolmas kolmas 22038  5月  6 13:58 hello.html
-rw-r--r-- 1 kolmas kolmas 55140  5月  6 13:58 hello.js
-rwxr-xr-x 1 kolmas kolmas 14508  5月  6 13:58 hello.wasm

WebAssemblyのバイナリである.wasmファイルはCORS2ポリシの適用対象であるため、ローカルに配置してアクセス3しても動作しない。適当に用意したウェブサーバに上記で出力されたファイルを公開して、それを介して件のHTMLにアクセスすると以下を得る。

コンソール風の見た目の画面にHello world!表示。

ウェブページ上に作られたコンソール風画面に、先程Cで作成したプログラムが標準出力に対して出力している「Hello world!」が表示されている。

続・Cの関数をJavaScriptから呼ぶ

単にC言語で書かれたプログラムを実行する事は上記のように出来たが、私がWebAssemblyの事を知ったきっかけは冒頭で述べた通り、Cで実装されたライブラリをウェブ環境上に移植したものを見た事である。そのような場面では、Cのプログラムで定義されている関数を任意のタイミングでJavaScriptから呼び出さなければならない。これについては次エントリにまとめることにする。

余談 ー MDN

この手のことを調べたり勉強したりするのにあたっては、MDN Web Docsの資料は極めて役に立つ。 developer.mozilla.org MDN=Mozilla Developer Networkの事だと思っていたが、今はその頭文字であるという意味は持たないらしい。 ja.wikipedia.org


  1. コレ。github.com
  2. オリジン間リソース共有。
  3. 即ちfile:始まりのURLによるアクセス。

スルメとサラミ

スルメとサラミ、どちらも長男・次男の好物である。特に長男の療育帰り、車を運転しつつ、止まるたびに後席に座っている長男に一つずつ渡して食べさせてやっている。塩分の多さに思うところはあるが、ご褒美的な位置付けである。療育施設近隣にイトーヨーカ堂があって、いつも療育に長男を預けている裏で買い出しをしている。そこでこれら長男のご褒美も買っている。長男の言うスルメは、その商品名としては「あたりめ」である。 www.sej.co.jp 似たようなものとして、伍魚福の「一夜干焼いか」もよく食べる。これは長男のネーミングによれば「イカ」。 www.gogyofuku.co.jp サラミは以下等。これに関しては長男も「サラミ」と呼ぶ。 www.natori.co.jp 酒を飲むわけでもないくせに、酒のつまみ的な食べ物が好きなのだ。1

尚、以降の本エントリの表記は、長男の表現で言うところの「スルメ」「イカ」「サラミ」の定義に従う。

サラミをスルメと間違える

スルメやイカは大分以前から食べていたが、サラミを導入したのは最近である。ご褒美おやつの最初期はスルメであって、その後イカも食べる事が分かってからはイカばかりにしていた2。ところがある日、たまたまスルメもイカも売り切れていて、療育帰り長男のご褒美おやつ用に初めてサラミを買ってみた。その時点では、サラミ自体は他の機会で何度か食べさせた事があり、長男が好む事も分かっていたが、療育帰りでは初めてである。

長男を車に乗せた後、まず「今日はイカが無かったんだ」と言い、次いで「代わりにサラミがあるぞ」と言ってサラミを見せてみた。そうしたら「サラミ食べます」「サラミください」と言ってきたので一安心し、とりあえず一本渡してから療育施設を出発した。普段から、帰宅中にご褒美おやつを渡すタイミングは、信号等の理由で車が止まっている時、かつ長男が要求してきた時だけにしている。動いている最中に渡すのは危険運転に片足突っ込んでいるし、また、単にこちらから渡すのではなく長男から要求を発させたいのである。このルールは長男も理解しているようで、車が止まるたびに「〇〇食べます」とか「〇〇ください」などと言ってくる。3

さて件のサラミの日、出発後初めて車が止まった時、予期していた通り長男から要求があった。しかしその要求は「スルメください」であった。「スルメは無いんだよ、サラミならあるよ」と返したら「サラミください」と言い直してきたので、サラミを渡した。次のタイミングでは、最初「スルメください」と言いかけたが、途中で気づいたのか「サラミください」に言い直してきた。その後も何度か同様の言い直しパターンがあり、最後の頃にはすんなり「サラミください」と言うようになっていた。

長男には、与えられたものや与えられた状況が気に食わない場合、その時に得られようが無い代替を求める駄々を捏ねてくる節がある。しかしこの事象は、それには当てはまらないように思える。上述の通り長男はサラミも好きであり、「気に食わない」ものでは無い。そもそも、駄々捏ねの場合はこちらが「無いよ」と言ったところですんなりと聞き入れたりしない。指摘されてすぐに訂正したり、もしくは自分から言いかけた内容を引っ込めて言い換えたりしてきたあたり、単純にサラミをスルメと間違えたのではないかと見ている。

しょっぱい仲間・ご褒美おやつ仲間

では何故サラミをスルメと間違えたのだろうか、という点が気になる。これらは、少なくとも大人視点からは、かなり違うものである。

両方ともしょっぱいものではある。以前のエントリで少し触れたが、「しょっぱい」のような具体的実体を持たない概念を理解させるためには、それが与えられた時にその言葉掛けをしてやるのが良い、という事は療育の先生にも言われている。過去エントリを自分で書いておきながら、普段あまり実践できていない点ではあるのだが。実際、まだ「しょっぱい」という言葉と概念は長男には入っていないように思える。その状況において、味の方向性の近さ故に長男がサラミとスルメを間違えているとしたら、これは「しょっぱい」概念を導入するチャンスではありそうである。

長男にとってのご褒美おやつの一種である、という点もサラミとスルメの共通点ではあり、そこから連想して間違えた可能性も考えられる。特に療育帰りの車中におけるご褒美おやつとしてサラミを食べるのは初めてだった、という事は影響しうるだろう。この場面で「サラミ」という言葉が出てくるという事が、長男にとってかなり意外性の高いものであった可能性は大いにある。その点では、上述の通りスルメも、療育帰り車中のご褒美おやつとしては久しく食べさせていないのだが。それでも、この場面で食べていた実績があるにはある。

ただ、これら二つの分類に基づく考え方だと、何故イカとは間違えなかったのか、という事を説明できない。イカもしょっぱいものだし、ご褒美おやつの一種でもあり、しかも療育帰り車中にて頻繁に食べているものでもある。それにも関わらず、この時長男が間違えて発言した内容は常に「スルメ」であり、イカとサラミを間違える事はなかった。

S○R○M○

そのような事も考えながら車を運転しつつ、一方で、なんとなくスルメとサラミが似ているような気がする、ということも思っていて、何故だか分からないでいた。帰宅後しばらく考えていたら、この二つは言葉として音が似ているのだという事に気づいた。まず第一に、両方とも三音で構成されている。しかも、発音する時の口の動きが、とても似ている。

スルメもサラミも、母音は異なるが、子音だけ取り出すと共に「SRM」である。シを除くサ行音(S)の発音は、上の歯の裏に舌を当てて息を出す。ラ行音(R)の発音は、上の歯茎を舌で弾く。マ行音(M)は、唇を閉じて溜めた音を唇を開いて放つ。この口周りの動きの流れが、スルメもサラミもそっくりである。本題から逸れるが、同様の子音の並びを持つ「すり身」等も同じ口の動きをする。

更に、母音は異なるとはしつつも、そのパターンというか、リズム感は同じである。スルメの母音は「UUE」、サラミの母音は「AAI」で、構成する音自体は違うが、最初の二音が同じで次に違う一音、というパターンが共通している。上述の子音の一致と合わせて、二つの言葉の響きを似通ったものにしているように感じられる。一方、アクセントは異なる。少なくとも私と長男の発音4においては、スルメは二音目「ル」以降にアクセントがあり、サラミは最初の一音目「サ」にアクセントが乗る。ただ、その違いにも関わらずこれら二つの言葉に音声的類似性を感じるという事は、その感覚にアクセントが与える影響は大きくないらしい。

もし、この音声的類似性、ひいては発音する時の口の運動の類似性のために長男がサラミをスルメと間違えていたのだとしたら、それはとても面白いなと思った。言葉の理解や獲得に、自らの発声運動が強く関与しているという事である。それはとてもありえそうな話で、言語発達関連の教本等読んだら普通に書かれている事なのかもしれない。仮にそうだとしたら、器質性の構音障害がある子の言語発達はどうなるのだろうか。その手の調査も調べたら見つかりそうな気がする。興味深い分野である。

結局理由は分からないけれど

しょっぱい仲間だからなのか、ご褒美おやつ仲間だからなのか、はたまた音が似ていたからなのか、長男がサラミをスルメと間違えた理由が実際に何だったのかは分からない。何で間違えたのか教えて欲しいくらいだが、それを聞いてみて答えが返ってくるようであれば何の苦労もない訳で。

その点、検証しづらいというよりほぼ出来ない状態ではあるのだが、長男の不思議行動を起点とした知的探究は面白い。だいぶ以前のエントリで触れた事でもあるが、久々にそれを思い起こす今回の「スルメとサラミ」事案であった。


  1. これに関しては私自身もそうで、酒は飲まないくせにつまみは好きである。
  2. 相対的に塩分控えめだったので。
  3. 車が動いている時に要求してくることもあるので、その時は「車が止まるまで待ってな」などと返している。
  4. 一般的にも同様なアクセントでなかろうかとは思うが。

Karl Fazerミルクチョコレート

先日、再びヨーロッパ方面の海外出張に行っていた。その前の出張から二ヶ月そこらしか経っておらず、かなり珍しい頻度である。行先は日本からの直行便もある都市1だったが、最近の諸事情で航空券の値段が馬鹿みたいに高くなっていて、とても選べない。経由便の選択肢がいくつかある中で、今回はフィンエアーのヘルシンキ経由になった。それでも、かつてと比べたら遥かに高額なフライトではあったが。

帰路ヘルシンキ空港にて。土産の類は買い込んだ後。

ポスドクをしていた時期のうち、2017年から2018年にかけて、九ヶ月ほどフィンランドに留学していた。それ以来、ほぼ八年ぶりのフィンランド上陸であった。単なる乗り継ぎで、空港から出る余裕など全く無い旅程ではあったが、免税店等で懐かしい雰囲気に当てられ、出張先での土産に加えて諸々買って回った。

Karl Fazer

中でも大量に買い込んだのが、フィンランドの有名な食品企業Fazer社の代表的ミルクチョコレート、Karl Fazerである。 www.fazer.com

Fazer社のみならずフィンランド全体において代表的なチョコレートであるとされる。初めて食べたのは留学中に参加したとあるカンファレンスで、休憩スペースの大皿に大量に盛られていた。一口サイズのチョコレートがキャンディのように個包装されていて、このような場面で扱いやすいものだと思う。食べてみたらとても好みで、それ以来、時折スーパーで買っては寮の自室で食べていた。当時付き合っていた彼女=今の妻に送った事もある。

最も標準的と思われるキャンディ型のKarl Fazer。他に板チョコとかチョコバーのような形でも売られている。

個人的な感想としては、美味なのは上述の通りである。更に、口に含んで最初は甘味が強めだと感じるのだが、その甘さにはくどさが全く無く、食べ終えた後に甘ったるさが残らない。おかげで、一つだけ食べて止めるというのが難しく、少なくとも三、四個は一度に食べてしまう。箱を開けると結構な数が入っているのだが、そんな訳で、食べ始めると一箱くらいすぐに無くなってしまう。

標準的な箱、およそ40個入り。妻と二人で食べて、帰国後二日で一箱空いた。徳用の大箱もあるのだが空港免税店には売ってなかった。

フィンランド土産として自信を持って推奨できるものである。今回は単に経由地でしかなかったので仕方がないが、可能なら空港免税店ではなく市中で買った方が安い。高級ブランドというよりは生活に身近なチョコレートであり、大体どこのスーパーでも置いているし、値段もそれほどしない。八年前の留学当時は一箱5.5ユーロ程度で買っていたように記憶している。今日日、少し値上げされているのだろうか。今回の空港免税店での値段は、一箱9ユーロ程度であった。2

Fazer社のその他製品

Fazer社と言ったら上述のKarl Fazerだと思うし、そのパッケージの青色からFazer blueとも呼ばれるくらい認知された製品である。しかしそれ以外の製品・ブランドもある。

日本ではGeishaブランドのチョコレートが有名だろうか。キャンディ型のKarl Fazerと同じサイズのチョコレートで、中にナッツ系のフィリングが入っている。製品名の由来は日本語の「芸者」であるらしい。個人的にはスタンダードなKarl Fazerの方がより好みではあるが、こちらも美味である。 www.fazer.com

Karl Fazerブランドのブラックチョコレートがあるというのは、今回初めて知った。ブラックチョコレートを好む私の母3のリクエストである。カカオ70%で、ブラックチョコレートの中ではかなり食べやすい部類。

黒いパッケージ。キャンディ型のフォーマットは通常のKarl Fazerと同じ。

Fazer社は菓子の他に、グループ内でパン等も作っている。スーパーでFazerのライ麦パンを買って食べていた記憶。 www.fazergroup.com

ついぞ挑戦する事はなかったが、フィンランドには、しばしば「世界一まずい菓子」と呼ばれるサルミアッキというものがある。現地では普通の菓子の一つとして受け入れられているそうだが。Fazer社はこれも作っている。挑戦する勇気は無い。 www.fazer.com

フィンランド行ったらKarl Fazer

改めて、本エントリの主張はKarl Fazer美味いという事である。しかも肩肘張った存在では無い、気軽に買えるチョコレートである。フィンランド土産に良い。個包装なので会社等でのばらまきにも使いやすい。

ただ、気軽に買えるのは現地だからであって、私にとって留学時代の馴染みの味であるにも関わらず、普段は縁遠い存在である。今回買ってきた分も早々に無くなってしまうだろうし、その後はまた焦がれる日々に戻るのである。またヘルシンキ経由の出張でもあると良いけれど。

余談 ー フィンエアーはとばっちりも良い所

かつてヘルシンキは日本から9時間そこらのフライトで到着できる場所だったのだが、今は13時間程度かかる。かつてのルートはロシア上空を飛んでいくものだったが、ウクライナ事案以来これが使えない。更には最近のイラン情勢で中東も危うい。今回の往路は北極上空を飛んで地球の北側を「跨ぐ」感じのルートであった。帰路は、ヘルシンキから南下しつつウクライナ辺りを避けて回り込み、更に中東を針の穴を通すように抜け、ユーラシア大陸を横断するルートであった。かつてのルートと比べたらどちらも非効率極まりない。

元々フィンエアーは、ロシア航路による極東方面とヘルシンキの地理的近さを活かして、ヨーロッパ方面の乗り継ぎ客を取り込んでいた。実際、極東路線を多く持ち、日本路線の定期便も多い。そこに昨今の情勢により迂回が必要になっている事、フィンエアーにとってはとばっちりとしか言えまい。一出張者としては、フィンエアーヘルシンキ経由を選ぶ合理的な理由4が少なくなって、Karl Fazerが更に遠い存在になるという問題になる。


  1. 何度か出張した事のある行先で、直行便を使ったこともある。当時の感覚からしたら、今の航空券の値段はとても信じ難い。
  2. 留学当時は1ユーロ120〜130円台だったので、昨今の円安によっても感覚が違ってくる節はある。ほぼ1.5倍になっている訳で。
  3. 八年前の留学時、両親が一度フィンランドに遊びに来た。その時にこのブラックチョコレートを土産に買って帰ったらしい。
  4. 今回に関しては、会社指定の代理店が出してきたルートの中で一番安かった。