2016年10月28日金曜日

GdkPixbufを高速にリサイズ(cairo)


GdkPixbufのサイズ変更を、元画像500x500px から 出力画像100x100~10x10pxで行うと、どうやらとても遅くなるようです。



GDK_POINTER_MOTION_HINT_MASK を使わずに motion-notify-eventをそのまま再描画に投げている私も悪いのですが。
とはいえ GDK_POINTER_MOTION_HINT_MASK はGtk3で思った通りにならず、アプリケーションがフリーズするようになっていました。

将来的には描画前のレンダリングをマルチスレッド化するつもりではいます。しかしシングルスレッドだと競合などを考えずに済み楽であるため、どうにもならなくなるまではシングルスレッドで行くつもりです。

というわけで、単純にリサイズ機能を高速化して対応。このあたりも、cairo任せにするといろいろオブジェクトを確保するコストを考えてもずっと早いのではないかというカンが当たった感じです。


なお、 gdk_pixbuf_scale_simple()をそのまま、画質を GDK_INTERP_HYPER からバイリニア指定に変えるのは試してみましたが、ほとんど効果なしでした。

なお画質は不明。見た目にわからないですし悪くないですが、アプリケーションが将来的に画質を指定するようになったら、リサイズ機能を見直そうと思います。

呼び出し例は以下。
====
/*

 
GdkPixbuf *pb = gdk_pixbuf_scale_simple(

 

 

 
pixbuf,

 
 
 
(int)w, (int)h,
 
 
 
GDK_INTERP_HYPER);
*/

 
GdkPixbuf *pb = _pv_copy_new_pixbuf_scale(
 
 
 
pixbuf,
 
 
 
(int)w, (int)h);
====

実装は以下。
====
static GdkPixbuf *_pv_copy_new_pixbuf_scale(GdkPixbuf *pb_src, double w_dst, double h_dst)
{
 
cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w_dst, h_dst);
 
pv_assert(surface);
 
cairo_t *cr = cairo_create (surface);
 
pv_assert(cr);

 
double w_src = gdk_pixbuf_get_width(pb_src);
 
double h_src = gdk_pixbuf_get_height(pb_src);

 
cairo_matrix_t m = {
 
 
w_dst/w_src, 0,
 
 
0, h_dst/h_src,
 
 
0, 0,
 
};
 
cairo_set_matrix(cr, &m);

 
gdk_cairo_set_source_pixbuf (cr, pb_src, 0, 0);
 
cairo_paint (cr);

 
GdkPixbuf *pb = gdk_pixbuf_get_from_surface(surface, 0, 0, w_dst, h_dst);
 
pv_assert(pb);

 
cairo_surface_destroy (surface);
 
cairo_destroy (cr);

 
return pb;
}
====

作ったPixbufは呼び出し元で開放しましょう。
以上です。

2016年10月15日土曜日

BLADE v7 liteについて(スマホ買いました)

BLADE v7 liteを買って、半年使ったので、本日レビューを書き散らします。
半分くらいAndroid6.0レビューみたいになるかも。
 
NTT-Xで2万円弱で買った。

BLADE v7 Liteと付属物

BLADE v7 Liteを選んだ理由は、Android6.0が使える端末で他に手頃なものが無かったから。
同価格のASUSのZenPhoneのほうがCPUが少し良いので、そちらでも良いかと思った。ノートPCはASUSなので揃えてみたかったし。
だが最終的に、ZenPhoneにはGPSに問題があるという話と、やはりAndroid5.xではなく6.0にしたいということでBLADE v7 liteにした。


開いてすぐBLADE v7 lite端末


多言語でオモテウラ有りのペライチ、マニュアルはこれっきり。


この端末、届いてから3週間近くSIMを待っていた。SIM契約のアテが外れたから。
初期設定でSIM設定をスキップすると、後が面倒そうだったので、WiFi運用などもせずバッグに放り込んでいた。この間ほぼ無充電だったが、2週間くらい放っておいてもBLADE v7 liteは普通に起動した。電波を掴んでいなかったからかもしれないが。

鎮座するBLADE v7 lite


途中で一度、Y!MobileのSIMが差し込まれた。
(店頭での動作確認。きちんとブラウザでWebサイトが見れることを確認した。通話は未確認)
Y!Mobileはパケット繰越しないと言われたためSIMを買わなかった。
UQMobileのSIMは試していない。店頭で契約しようとしたら、店員に「未対応端末では壊れるかもしれないからやめたほうがいいです」と静止されたから。これについては感謝している。次があればまたUQMobileを検討しよう、と思ったくらい。

閑話休題。

その後、MineoのSIMを契約して家で刺して、動いた。
Mineoの設定はあらかじめ登録されている。ユーザ名とパスワードも共通のものだったようで、デフォルトのままで動いた。SIMのMNP切替中に、何度か"モバイルデータの有効化"だったかを無駄にON/OFFしていたが、ともかく1時間も置いておいたらインターネットも通話もできるようになった。

なお、microSDは、秋葉原で1000円程度で買った32GBのTOSHIBA製を新規購入して入れた。

前の端末は3.5年前のAndroid4.2端末だった。電池がヘタっていたのと、ブラウザ(chromeその他問わず)が3タブほど開くと、タブ切り替えの度にページ再読み込みするのが不満だった。

BLADE v7 liteにして、ブラウザ再読み込みはかなり減った。chromeは前の端末でも最新版だったと思うし、CPU/memoryも前の端末とあまり違わないので、Android6.0の性能なのだろうと思う。

chromeの表示が豆腐になる問題も、システムアップデートを実行したら消えた。システムアップデートのサイズは忘れてしまったが、500MBも無かったと思う。

買ったばかりの端末だからか、電池の減りも少ない。前の端末は、行きにブラウザを開きつつ音楽を聞いていただけでも、会社で充電しないと帰宅中に電池切れでシャットダウンしていた。BLADE v7 liteにしてからはそれがない。試していないが、1日充電しなくても持つと思う。

以下、気にしていなかったのだけれど、新端末になってよかった点がふたつ。

前の端末ではほとんど読んでいなかった『通知』をよく見るようになった。前の端末で通知は不要なものが入り混じって読みづらかったし、整理する方法もわからなかった。今はSMSもGMailもSkypeメッセージも通知から知るし、ダブルクリックで飛んで読む。
前の端末は通知と思わしきLEDが常に点滅していて、未読のためか消えずに不愉快ですらあった。今はそういうことはない。新端末のLEDが光るとき、その色が何を示すのかは未だに知らないままだが。特に困ってはいない。

標準キーボードがよくなっていた。Simejiなどは入れたことがない。誤入力を起こさない工夫か
フリックなどの入力で指を動かす距離が少し増えたと思う。私が買ったBLADE v7 liteを触った友達も同じことを言っていたので、感覚としては間違いないと思う。
唯一残念なのは、Qwertyキーボードで日本語が打てないこと。これは設定変更でできるようになるのか、後で調べるつもり。今はチャットなどはQwertyキーボードでアルファベットだけ入力し、エセ英語で返事している。元から携帯であまり文字入力しないし。

テザリングの手間は変わっていない。あまり使われない機能なのだろう。あるいは、UIの位置を浅くすると、誤認識でテザリングが起動しそうとかあるのだろうか。
テザリングON/OFFボタンのアプリを探そうかとも思った。が、一部にAndroid6.0対応できているアプリがあるようなので、もう少し経って、必要になってからその時に探そうかと思う。

Android6.0からアプリケーションの権限が詳細に設定できるようになったが、こちらの恩恵はまだ受けていない。一部のアプリで細かく聞かれるようになっただけとも言える。現在はとりあえず、LINEに連絡先を読ませる許可を与えていない。はず。

BLADE v7 liteに付属するメールアプリケーションに登録アカウントを解除できない問題があるとのことだが、使っていない。GmailなのでGmailのアプリケーションを使っている。Mineoのメールアドレスは使っていないし、スマホに紐付けしているメインのメールアドレス以外は、すべてPCで扱うので。

この前実家に帰った際に歩きまわったが、ZenPhoneは知らないが、BLADE v7 liteはGPSは問題なかったと思う。
ポケモンGOはダウンロードして少しだけ動かしたが、まだ最初のポケモンをゲットしていないので動作確認としてはよくわからない。少し歩くとピカチュウ出るって本当ですか。


この1週間くらい、運用してきたレビューとしては以上の通り。

気になった現象は以下。

このレビューを書いている今日、帰りに音楽プレイヤーの音楽が止まった。
(再生表示のまま。再度再生すると続きが鳴った)
また、今日はじめて、Skypeクライアントがタップに反応しなくなったことが気になる。

音楽プレイヤーはアプリケーション側の問題かもしれないので、後でプレイヤーを変えてみる。Skypeクライアントの問題は、剥がれかけの保護フィルムのせいかもしれない。

保護フィルムは、買った時のものをまだ剥がしていないだけ。操作はできるが、剥がす前提なので一週間と持たず剥げてくる。
昨日、ヨドバシ.comでBLADE v7 lite専用の保護フィルムを注文したので、明日あたり届くと思う。

マイナAndroid端末は、保護フィルムもすぐに在庫されなくなるはず。前の端末もそうだった。といっても、前の端末と同じように、後半はフィルム無しで運用するつもりでいる。

安いAndroid端末の気楽さは、iPhoneなどと違って、壊れたらすぐに新しい端末に買い換えてしまえることだと思う。画面の割れたiPhoneを使い続けるよりは良いと思っている。
ケースには入れないつもり。保護する必要はないし、厚くしたくない。
ギリギリポケットに入るサイズなので、ポケットに入れて運用するつもり。
Android、特にマイナなAndroid端末は、ケースも汎用品などで限られてくる。iPhoneのようにたくさん有るケースを選びたい向きには向かない。



というわけで、今のところ BLADE v7 lite を気に入っている。
思えば、これまで携帯は仕方なく嫌々で買っていた。携帯電話を積極的に自分で選んだのは初めてである気がする。

2016年10月8日土曜日

Windows環境でMakeする際に起こるエラーと対処


Make オルタネイティヴは数あれど、小さな実験プロジェクトを始めるときに、簡単に使えるMakeは未だにビルドツールとして有力な選択肢だと思います。

MakefileはUNIX環境、というかCLIツール群があることを前提にしているところがあります。Windows環境ではUNIXコマンドがそもそも無かったりするため、Makefileが可搬ではないです。



GitHubに公開しているmkwin_mnでは、mkdir -p と rm -f を解決しました。
以下には ls find が無いことにより発生する問題もあり、wildcard関数による解決は満点とは言いがたいので、誰か対処してくれないかなと思っています。
Cygwin入れろと言われると返す言葉がないです。(MinGWといろいろ共存面倒そう、という言い訳がある。きちんと調べて対処すべきなのだろうけれど。)


 # ls, findが無いことにより起こるエラー


====
 process_begin: CreateProcess(NULL, sh C:\Users\nuka\Documents\etaion_20160920_22
h17m\etaion\library\gtk+-bundle_3.6.4-20130921_win32\bin\xml2-config --cflags --
libs, ...) failed.
cc1.exe: error: ./: No such file or directory [-Werror=missing-include-dirs]
source/pv_color.c:130:1: fatal error: opening dependency file object/pv_color.d:
 No such file or directory
 }
 ^
cc1.exe: all warnings being treated as errors
compilation terminated.
Makefile:40: recipe for target 'object/pv_color.o' failed
mingw32-make: *** [object/pv_color.o] Error 1
 process_begin: CreateProcess(NULL, ls source/*.c, ...) failed.

====

ルールによるビルドのために、ソースディレクトリ以下の *.c ファイルを探させます。Makefileを書くなら、大抵はこれをやらせます。
このとき、Make組み込みの wildcard 関数でなく、 shell lsshell find のように外部コマンドを呼び出していると、コマンドの無い環境でエラーが起こります。

wildcard()は子ディレクトリを再帰的に探索してはくれません。なので、以下のようなサブディレクトリのあるソースディレクトリ構成に対応できない問題が有ります。
src/
         + engine/
         + enemy/
サブディレクトリがそうそう増えないもので、階層も深く掘らないならば、サブディレクトリごとにwildcardを書いて、結果を加算していく



$(wildcard **.c) などとやると、子ディレクトリ内以下へ再帰的にマッチできるよ』
というStackOverflowに書いてあった小技。これの投稿の下に付いているコメント欄でも出来ない報告が書き込まれていましたが、わたしの環境ではUbuntu LinuxのGNU Make4.1でも使えなかったです。いったい何処由来のネタなのだろう。

閑話休題。



 # 出力先ディレクトリの生成に失敗したことにより起こるエラー


====
process_begin: CreateProcess(NULL, object, ...) failed.
make (e=2): wウスt@CェゥツゥワケB
Makefile:41: recipe for target 'object/pv_color.o' failed
mingw32-make: [object/pv_color.o] Error 2 (ignored)
 source/pv_color.c:130:1: fatal error: opening dependency file object/pv_color.d:
 No such file or directory
 }
 ^
compilation terminated.
Makefile:41: recipe for target 'object/pv_color.o' failed
mingw32-make: *** [object/pv_color.o] Error 1
====


出力先として想定ているobjectディレクトリが作れず、書き出し先が存在しないため、ファイルを作れなくて起こるエラーです。
mkdir -pが無いことが原因です。
GitHubに公開しているmkwin_mnでは、mkdir -p相当の機能を用意することで対処しています。
他に、objectなど、必要なディレクトリがあらかじめわかっているなら、object/.gitkeepなどを置いておいて、書き出し先ディレクトリを予めプロジェクトリポジトリに含めておくという手もあります。
その場合は誤って削除してしまわないよう注意。

以上です。

Gtk3でコンボボックス(ドロップダウン) Widgetを作る

Gtk3で、ComboBox(DropDown)を作るサンプルコードです。

これまでGtk3では公式ドキュメントを反面教師として、単体動作するコードをサンプルとして提示してきたつもりなのですが、今回は諦めました。すいません。なんというか、実用を目指すプロジェクトからコードを一部だけ、動く状態に抜き出すって無理ですね。



====
        // self->combo_linejoin = gtk_combo_box_new();
    //    GtkListStore *liststore = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
        GtkListStore *liststore = gtk_list_store_new(1, G_TYPE_STRING);
        int num = get_num_stroke_linejoin_infos();
        for(int i = 0; i < num; i++){
            const PvStrokeLinejoinInfo *info = get_stroke_linejoin_info_from_id(i);
            gtk_list_store_insert_with_values(liststore, NULL, -1,
                            0, info->name,
    //                        1, info->linejoin,
                            -1);
        }
        self->combo_linejoin = gtk_combo_box_new_with_model(GTK_TREE_MODEL(liststore));
        g_object_unref(liststore);
        GtkCellRenderer *column = gtk_cell_renderer_text_new();
        gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(self->combo_linejoin), column, TRUE);
        gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(self->combo_linejoin), column,
                    "text", 0, NULL);
        gtk_combo_box_set_active(GTK_COMBO_BOX(self->combo_linejoin), 0);
        gtk_box_pack_start(GTK_BOX(self->box_linejoin), self->combo_linejoin, false, true, 3);
    }


====

ついでに使っている独自型などは以下。

====
 /*! @brief StrokeLinejoin */
typedef enum{
    PvStrokeLinejoin_Miter,
    PvStrokeLinejoin_Round,
    PvStrokeLinejoin_Bevel,
}PvStrokeLinejoin;

typedef struct{
    PvStrokeLinejoin linejoin;
    const char *name;
    int cairo_value;
}PvStrokeLinejoinInfo;

int get_num_stroke_linejoin_infos();
const PvStrokeLinejoinInfo *get_stroke_linejoin_info_from_id(PvStrokeLinejoin linejoin);


====

ドロップダウンリストの内容をGtkListStoreで作ります。それをGTK_TREE_MODEL形式でcombo_box_new_*()関数に流しこめば完了です。
一番重要なのはGtkListStoreがGtkTreeModelに変換できること、そしてそれがComboBoxに流し込めることです。

Python版コードなどを見てみると、GTK_TREE_MODELのところはタプルとか作ってそのまま流し込んでいて、確かに動的言語なら辞書配列が簡単に作れるし、それがコンボボックスの材料になるのも納得ですが、C言語だとこのなんだかしっくり来ない謎型変換をするコードになっています。

参考にしたのはこちら。
http://stackoverflow.com/questions/16630528/trying-to-populate-a-gtkcombobox-with-model-in-c
参考コードのほうが綺麗で完結していて読みやすいですね。
参考コードはコンボボックスの選択肢に色を付けるという愉快なサンプルコードになっていますが、これもしかしてここからオブジェクト取り出して選択した要素を一意に特定してコールバック処理の際に処理を分岐するとかの用途を想定しているんじゃないかな、私はコールバックの中でアクティブな選択肢の昇順を取ってきて、キャストなどもせず乱暴に元データのIndexに置き換えています。いずれはgpointer *data に選択肢のポインタそのまま投げ込んで判定するつもりだけれど、といった感じです。

====
g_signal_connect(self->combo_linecap, "changed",
            G_CALLBACK(_cb_changed_linecap_with_combo), NULL);


static void _cb_changed_linecap_with_combo(GtkComboBox *widget, gpointer user_data)
{
    EtStrokePanel *self = stroke_panel;
    assert(self);

    self->stroke.linecap = gtk_combo_box_get_active(GTK_COMBO_BOX(self->combo_linecap));

    _update_focus_elements_from_local();
}

====

以上です。

Gtk3アプリケーションをWindows環境でビルドした際に出たエラーと対処

ここでは、私の環境とビルド内容で出たGtk3固有のエラーとその対処について。Windows7 x64 と MinGWにて。

最初はビルド環境の構築その他を扱おうかと思ったのですが、他サイトでも少しづつ情報があるのでやめました。なんとなくできると思います。
Gtk2 Windows開発環境キットをGitHubにて公開しているので参考にどうぞ。
https://github.com/MichinariNukazawa?tab=repositories



今回は、gtk+-bundle_3.6.4-20130921_win32を使いましたが、見返してみると、
gtk+-bundle_3.10.4-20131202_win32.zip
とか、それなりに新しいのがありました。が、それでも2013年...。
その他バージョンなどダウンロードはこちらから。
http://win32builder.gnome.org/
この公式Gtk3パッケージ、バージョンを見ればわかるとおり、すでに非常に古いものとなっています。


 #  Gtk3バージョンが古いからか、未実装関数的なものがある?

====
 source/et_layer_view.c: In function 'et_layer_view_init':
source/et_layer_view.c:146:2: error: implicit declaration of function 'gtk_text_
view_set_monospace' [-Werror=implicit-function-declaration]
  gtk_text_view_set_monospace (GTK_TEXT_VIEW(self->text), TRUE);
  ^
cc1.exe: all warnings being treated as errors
Makefile:42: recipe for target 'object/et_layer_view.o' failed
mingw32-make: *** [object/et_layer_view.o] Error 1
====

対処:この関数だけWindows環境を判定してCソースにて #IFDEF 切って無効化しました。


 # SVG画像ファイルを読もうとすると失敗する。

実行時、それもSVG画像を読もうとしてはじめて発生するので、ちょっと厄介。
文字化けはGtk3の出力で別問題、今回は無視しています。
====
C:\Users\nuka\Documents\etaion_20160920_22h17m\etaion>make run
./build/etaion_vector.exe
debug: et_tool_init()[59]: 4
error: et_tool_init()[71]: 'サュシサ「ク・シォェュソセシセ: Z:/srv/win32builder/fixed_364/build
/win32/lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-svg.dll: `Z:/srv/win32b
uilder/fixed_364/build/win32/lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-s
vg.dll': ョ「ク・シォヲ、セ€'
BUG: main()[81]:
Makefile:50: recipe for target 'run' failed
mingw32-make: *** [run] Error -1

C:\Users\nuka\Documents\etaion_20160920_22h17m\etaion>
 、
 'Unable to load image-loading module:
 ./build/etaion_vector.exe
debug: et_tool_init()[59]: 4
error: et_tool_init()[71]: 'Unable to load image-loading module: Z:/srv/win32bui
lder/fixed_364/build/win32/lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-svg
.dll: `Z:/srv/win32builder/fixed_364/build/win32/lib/gdk-pixbuf-2.0/2.10.0/loade
rs/libpixbufloader-svg.dll': 指定されたモジãƒÂ
¥Ã£Æ’¼ãƒ«ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。'
BUG: main()[81]:
Makefile:50: recipe for target 'run' failed
mingw32-make: *** [run] Error -1
====

libpixbufloader-svg.dll が見つからないことが原因、とのことです。

gtk+-bundle_3.6.4-20130921_win32\lib\gdk-pixbuf-2.0\2.10.0\loaders.cacheを見ると、
====
 "Z:/srv/win32builder/fixed_364/build/win32/lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-svg.dll"
====
などと書いてあるので、相対パスなどで *svg.dll のロードをするよう書き換え。

====
C:\Users\nuka\Documents\etaion_20160920_22h17m\etaion>
 "library/gtk+-bundle_3.6.4-20130921_win32/lib/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-svg.dll"
====

以上です。

2016年10月1日土曜日

mkwin_mnを作りました (Makefile in Windows toolkit)

mkwin_mnとは

小規模なMakefileを、Windows環境で動作させるための互換用ファイル群です。
主にMakefileの $(MKDIR_P) と $(RM) のWindows版を提供しています。
というかそれだけです。

用途に対して実用的であれば良しとしています。Unix CLIとの互換性は、高くはありません。完全コンパチを目指すつもりもないです。



ダウンロードする

GitHubで公開しています。
あなたのプロジェクトの中から git clone などしてください。
$ git clone https://github.com/MichinariNukazawa/mkwin_mn

## 使い方

ダウンロードしてきて、あなたのプロジェクトの Makefile にインクルードするだけです。簡単ですね。
include mkwin_mn.Makefile
非Windows環境との切り分けは、呼び出し側のMakefileでやってもらうようにしました。
そのほうが、環境のフラグに好きな方法を使えるから良いだろうと考えています。

Windows環境のmakeによる Makefile実行で、変数からの機能呼び出しにより mkdir -prm -f  (rm -rf) のようなことができるようになります。

具体的な使い方の例は、添付してある example_project を参考にしてください。

## mkwin_mnのターゲット

小規模プロジェクトの手書きのMakefileがターゲットです。

循環定義的ですが、小規模プロジェクトとは、今どきMakefileを手書きしているようなプロジェクトを指します。

## 動機

開発の動機は、現在Ubuntu環境で作成中のGtk3アプリケーションをWindowsでビルドしようとしたら make clean が機能しなかったから、です。

## mkwin_mnのアプローチ

2016年現在、GNU Makeを置き換えうる、次世代ビルドツールは乱立しています。
新言語には新ビルドツールが付いていて当たり前の雰囲気です。
次世代ビルドツールは当然のようにUnix/Windowsコンパチなので、どれかに移行できればWindows非互換問題は片付きます。

私はMakeはそれなりに黒魔術だと思うので、より良くサポートされた汎用ビルドツールがあるなら、積極的に切り替えたいです。

しかし、次世代ビルドツールはどれも普及度がイマイチで、Web上にエラー解決の情報が不足しています。文法エラーなどで詰まると、そのまま死ぬ。
その点、Makeは優秀です。ほかの追随を許しません。実環境で30年近く叩かれまくっているので、ワークアラウンドとナレッジが溜まっています。大抵の問題は、検索してStackOveflowなどを読めば解決します。


Windows環境に足りないもの(MKDIR_P, RM)を足してやる、というのがmkwin_mnのアプローチです。
既存のMakefileに(ほとんど)手を加えません。
GNU Makeのエコシステムに乗ったままでいられる、という他にはないメリットがあります。

## 実装の詳細

ざっくり実装について。ここから下は適当に流し書きしています。

win:MKDIRはUnixセパレータ'/'を指定しても大丈夫。
win:RMとwin:RMDIRは'\'で渡してやらないと駄目。
というわけで、文字列置換。
bat変数でも、bash変数やMakefile変数と似たような置換の文法が使えた。
ただし、batで変数を扱うには魔法が必要だった。
この魔法が何という文法なのかわからない。検索しようが無い。

今回batファイルを書きましたが、Windowsバッチはよくわかりません。
C++における江添氏のような文法解説がほしい。MSのエバンジェリストの方々はきっとAzureでPowerShellでMSSQLをどうこうするのが忙しいでしょうから期待できないですが。

!cons.batは私のバイブル「30日でできる OS自作入門」由来。
MinGWのmakeにエイリアスをかける拡張をしています。



『Makefileのコマンド変数をWindows用に置き換えて、バッチを呼び出す』
という大枠にしました。
『Unix CLIと同じexeファイルを用意する』
というアプローチは、いろいろ面倒であることが予想されたので、早々に諦めました。

バッチはbatとvbsで少し悩みました。多少は早くて軽くていろいろな環境で動くだろうと思って、batにしました。
vbsのほうが複雑な機能を書きやすいはずなので、もっと複雑な処理が必要になったら、その時はvbsで書き直すかもしれません。
どちらにしろ速度は大して出ないでしょうし、速度を求めるようなモノでもないと思います。

今後について

開発動機は現状で満たしてしまったので、今後の機能追加は無いかもしれません。

リポジトリのTODOにTestと書いてある通り、とりあえず作って、開発中のGtk3アプリケーションのプロジェクトには十分使えることを確認しただけです。
そういう物だとわかった上でお使いください。プルリクエスト、利用報告があれば嬉しいです。

2016年9月2日金曜日

組み込みJavaScriptエンジン Duktape 非公式日本語訳(途中)









Cアプリケーションに組み込める、フリーのJavaScriptエンジンを探していて、Duktapeというプロジェクトが見つかり、興味があったのでドキュメントを読んでいました。
以下は、途中までですが、読みながら翻訳していたものを公開します。



 == 以下、翻訳版注意書き ==





この文章は、アプリケーション組み込み向けEcmascript(Javascrip)エンジンであるDuktapeのドキュメントの非公式日本語訳です。
この翻訳は、自分がDuktapeについて把握することを目的として行ったものです。全体を通して翻訳が雑です。というか誤訳を含みます。翻訳ミスしても実際のサンプルコードと自作コードで確認すれば気づくだろう、だからいいや、というレベルです。訳しづらかったところを飛ばしていたりもします。翻訳ミス前提で読んでください。
日本語としてメチャクチャなので、頭の中で再度訳しながら読むことになるかと思います。
ライセンスは原文に準じます。原文が許す限り、新しい翻訳のベースなど自由に使っていただいて構いません。とはいえ、新規に訳したほうが早いでしょう。
指摘・修正はメール(michinari.nukazawa@gmail.com)またはtwitterに頂けると嬉しく思います。

原文
http://duktape.org/guide.html


== 以下、翻訳文 ==

version 1.4.0 (2016-01-10)

## このドキュメントの範囲

このガイドの内容は、あなたのプログラムでDuktapeを使いはじめるためのイントロダクションです。あなたが基礎知識をすでに持っているならば、わかりやすい<a href>API reference</a>にてAPI構成を一覧できます。<a href>Duktape Wiki</a>の内容は、さらなる概要と使用例、ベストプラクティスです。

このドキュメントはDuktapeの内部構造について扱いません。(もしあなたが我々と共にDuktapeを弄ることに興味があるようならば、Duktape repoをご覧ください。)

## Duktapeとは何か?

Duktapeは、移植性とコンパクトなフットプリントに注力した、組み込み可能なEcmascript E5/E5.1エンジンです。Duktapeにより、あなたのC/C++プログラムへ、スクリプティング機能を簡単に拡張することができます。あなたはEcmascriptの中へ、プログラムの中心的な制御フローを作ることができ、同時に高速動作するC関数を使い、重い処理を取り扱うことができます。

(訳注:適当訳)EcmascriptとJavascriptは、大まかに等価ですが、ブラウザ向け拡張などが付いている場合もあって等しくはありません。Duktapeも例外ではなく、よく使われるprint(),alart()を持っています。なので、我々はEcmascriptの全体を実装したと言い表しています。

## Conformance
 (略)
## Features
 (略)
## Goals
 (略)

## ドキュメント構成

<a href>Getting started</a>は、あなたがダウンロード、コンパイル、そしてDuktapeをあなたのプログラムに導入するまでを取り扱っています。これの内容は確固とした例によって、あなたがどのようにあなたのプログラムにスクリプティング機能を導入するかを含んでいます。
<Programming Model><Stack types><C types>で語られているのは、Duktapeヒープのコアコンセプト, context, value stacks, Duktape API, Duktape C関数 です。Dukatapeのスタック構造とC型システムのラッパーについて概要を語っています。

Duktapeが実装しているEcmascript機能についていくつかのセクションで語っています: Type algorithms (for custom types), Duktape built-ins (additional built-ins), Ecmascript E6 features (features borrowed from ES6), Custom behavior (behavior differing from standard), Custom JSON formats, Custom directives, Buffer objects, Error objects (properties and traceback support), Function objects (properties), Debugger, Modules, Logging, Finalization, Coroutines, Virtual properties, Internal properties, Bytecode dump/load, Threading, Sandboxing.

<Performance>では(略)

<Comparison to Lua>では(略)

## Getting started
(略)
## Downloading
(略)
## Command line tool
(略)

## Integrating Duktape into your program

コマンドラインツールは、簡単なサンプルプログラムでDuktapeの組み込み方法を示しています。
組み込んでDuktapeをあなたのプログラムで使う方法はシンプルです:duktape.c, duktape.h, and duk_config.hを追加してビルドし、Duktape APIをあなたのプログラムのどこかから呼ぶ。

Duktapeの配布物に含まれるとても単純なサンプルプログラムの、hello.cがそれを示します。コンパイルとテストをこのプログラムで試すには():

The distributable contains a very simple example program, hello.c, which illustrates this process. Compile the test program e.g. as (<Compiling>の compiler optionも参照してください):
(サンプル略)
テストプログラムは、Duktape contextを作り、任意のEcmascriptコードを実行します:
(サンプル略)

Duktapeは組み込み可能なエンジンであるため、あなたのプログラムの基本のコントロールフローを変更する必要はありません。一般的な使い方は:
・プログラムを起動した際に、Duktape contextを作る(または、スクリプティングが必要となったタイミングで作る)。(訳注:適当訳)通常は、あなたのスクリプトがロードされる間に初期化が終わり、必要なタイミングでそれを実行することができます。
・一意な箇所があなたのプログラムの中にあり、そこでscriptを実行する、またscript内の関数をそこに挿入して呼ぶ。
・script内関数を呼ぶ。最初にpushして呼び出した引数はDuktapeコンテキストのvalue stacksに入れられてDuktape APIから使われる。これは、Duktape APIが実際に呼び出された際に使われる。
・ひとつのscriptを最後まで実行する。戻り値を渡す方法を制御でき、あなたのプログラムへ(Duktape API呼び出しの)戻り値として渡すか、Duktapeコンテキストのvalue stackに置くことができる。Cコードはこれらの戻り値をDuktape APIを通して使うことができる。

単純なサンプルプログラムを見てみましょう。プログラムはstdinを使った行の読み込みをCのmainloop内で行い、呼び出したEcmascriptの助けにより行文字列を変換して、結果を出力します。

スクリプトコードは"process.js"です。サンプルの文字列変換関数は、プレーンテキストの行文字列をHTMLに変換し、アスタリスク'*'で囲まれた文字を太字(bold)にします。
(サンプルコード略)
Cコードは"processlines.c"で、Duktapeコンテキストを初期化し、scriptを実行し、スクリプトが変換する処理対象の行文字列をstdinから読み込み、processLine()関数を行文字列ごとに実行します。
(サンプルコード略)

サンプルコード内のDuktape機能呼び出し箇所を、部分ごとに見て行きましょう。(適当訳)さらなる情報が欲しいならば、<Programming model>に書いてあります。
(サンプルコード略)
まずDuktapeコンテキストを生成します。コンテキストはEcmascriptの変数を変換し、value staskに入出力(pushing,popping)します。以降のDuktapeAPI動作の呼び出しではvalue stackが付き、入出力(pushing,popping)と実行がスタック上で行われます。製品コードには、望むならば、duk_create_heap()によってfatal error handlerをセットすることができます。<Error handling>を参照することで、エラーハンドリングのベストプラクティスについての議論を見ることができます。
(サンプルコード略)
評価(eval)呼び出しで"process.js"ファイルを読み込み、それがスクリプトをコンパイルし実行します。scriptに登録されているprocessLine()関数はEcmascriptグローバルオブジェクトへ、後で使うために格納されます。実行の際、スクリプトエラー・シンタックスエラーはハンドリングされ、fatal errorはハンドリングされません。もしエラーが起これば、エラーメッセージは強制的に安全に使うduk_safe_to_string()を通して提供され、それ以上に深刻なエラーにはなりません。実行結果の文字列は強制的にconst char * (ポインタ)に変換されるためリードオンリで、NULL終端のUTF-8エンコーディング文字列であるため、printfで直接使うことができます。
(サンプルコード略)
1行目は、Ecmascriptグローバルオブジェクトをvalue staskにpushしています。2行目はグローバルオブジェクトからprocessLineプロパティを探索しています("process.js"スクリプトファイル内に定義されている)。引数の-1はvalue stackのインデックスで、マイナス値はスタックを先頭から始め、よって-1はグローバルオブジェクトを一番先頭にスタックします。
(サンプルコード略)
Pushして行文字列が入っている文字列ポインタをvalue stackに入れます。文字列長は自動で、NULL終端で測られます(strlen()のように)。Duktapeは文字列のコピーを作成してstackにpushしますので、この関数の呼び出し後に文字列バッファを開放・書き込みしても構いません。
(サンプルコード略)
ここでvalue stackに入っているものは:グローバルオブジェクト,processLine関数,行文字列。
duk_pcall()メソッドは関数を実行し、指定された数の引数をvalue stack上から与え、それらを関数,引数,戻り値に置き換えます。ここで実行後のvalue stackに入っているものは:グローバルオブジェクト,実行結果 です。実行時のエラーは保護されており、取得してprint出力できます。duk_safe_to_string() APIを呼び出して安全エラーを表示することができます。最終的に、結果(またはエラー)がvalue stackからpopされます。
(サンプルコード略)
Duktapeコンテキストを破棄し、コンテキストのすべてのリソースを開放します。この呼び出しはvalue stackとその上の参照をすべて開放します。このサンプルではvalue stackの左端にglobal objectが入っているというくわだてです。これは問題ありません:メモリリークは起こりません、もしheapを破棄する際にvalue stackが空でも。

コンパイルは次のように。
(サンプルコード略)
テスト実行(process.jsがカレントディレクトリに必要)
(サンプルコード略)

## EcmascriptからCコードを呼び出す(Duktape/C bindings)

この導入サンプルでは、どのようにEcmascriptからCコードを呼び出して実行するか、難しくないCコードをEcmascriptから簡単に呼び出す方法を示します。

Ecmascriptは例にもれずしばしば、逆のシチュエーションとして中からCコードを呼び出したくなります。事例としては、スクリプト内で何らかの処理をとても多く実行する場合や、最適化されない低レベルbyte操作や文字列操作です。最適化されたCヘルパが呼び出し可能であり、あなたはすばらしいEcmascript言語の中で、あなたのスクリプトのロジックを記述して、その中で性能要求がクリティカルな部分をCコード呼び出しにすることができます。別の推測では、ネイティブなライブラリへのアクセスに使われるでしょう。

あなたが書くネイティブC関数の実装は標準的なC関数を、特殊な呼び出し規約の Duktape/C バインディングに適合させたものです。Duktape/C 関数は1つの引数を持ち、Duktapeコンテキストと1つの戻り値としてエラーまたは数値戻り値を持ちます。関数が呼び出し時の引数を得たり戻り値をセットする際にはDuktapeコンテキストのvalue stackに対して、Duktape APIを用いてそれを操作します。我々がさらに深くDuktape/CバインディングとDuktapeAPIを見るのはもう少し後にしましょう。サンプルは:
(サンプルコード略)
行ごとに見ていきます:
(サンプルコード略)
value stackのインデックス0番(スタックの先頭から、関数呼び出し時に指定された最初の引数)の内容が数値(number)であるか確認しています、この場合はこれは数値(number)です;もしそうでなかったなら、errorを投げて、値を返しません。もし内容が数値(number)であるならば、その数値をdouble型で返します。
(サンプルコード略)
引数を2乗して、value stackにプッシュします。
(サンプルコード略)
関数呼び出しからreturnして、これ(訳注:return 1)が指し示すものは(1つの)戻り値がvalue stackの先頭に存在することです。あなたはreturn 0を返すこともでき、これは戻り値が無いことを示します(DuktapeのデフォルトではEcmascriptのundefinedを返す)。負の値の戻り値は、errorが発生し、自動的に投げられたことを示す:これはエラーの投げ方としては単純なものです。なお、あなたがstackから値をポップして返すことを必要としないならば、Duktapeはあなたに関数からのreturnを自動で提供することができます。Programming modelにて詳細を参照ください。

我々の素数判定コードはネイティブコードによりEcmascriptで記述してあるアルゴリズムを高速化することができます。さらにテストプログラム側では1000000以下から素数を探すことに加えて値の下位が'9999'で終わる数値を探します。Ecmascript版プログラムは以下の通り:
(サンプルコード略)
雑記として、このプログラムはネイティブ関数を呼びますが、もしそれが使えない場合にはEcmascript版にフォールバックします。これにより、Ecmascriptコードは(訳注:今回のサンプルプログラム以外の)他のプログラムからも使用可能になります。一方で、もしこの素数判定プログラムを他のプラットフォームへ移植する場合でもネイティブコードは再コンパイル以外の変更は必要なく、ヘルパ関数を移植するまで遅いだけです。このプログラムの場合、ネイティブなヘルパ関数を検出するのはスクリプトを読み込む際です。あなたは一方でヘルパ関数の検出を任意のタイミングにフレキシブルに変更することができます。

ネイティブヘルパ関数には機能としてprimeCheckEcmascriptと等価なものを素直に実装しました。そこにmainを追加した"primecheck.c"は以下の通りです:
(サンプルコード略)
新しい関数呼び出しがあるので、行毎に見ていきます:
(サンプルコード略)
この2行の呼び出しは2引数をネイティブヘルパ関数が要求するためです。もし値がEcmascriptの数値型ではなかった場合、エラーを投げます。もしこれらが数値であった場合、数値を整数に変換し、それぞれローカル変数のval,limに格納します。index 0が第一引数でindex 1が第二引数です。

技術的には、duk_require_int()が返すのはduk_int_t型です;これは間接的にintへ変換されますがそれはintの長さが16bitまでしかない環境では問題が発生します。一般的なアプリケーションであれば、あなたはこれを心配する必要はありません。さらなる詳細はC typesを参照ください。
(サンプルコード略)
value stackにEcmascript falseをプッシュします。Cコードからreturn 1した場合、呼び出し元のEcmascript側ではこれを戻り値falseが返されたと解釈します。
(サンプルコード略)
1行目は、この後に、Ecmascriptグローバルオブジェクトをvalue stackにpushしています。2行目は、Ecmascript関数オブジェクトを作成し、それをvalue stackにpushしています。関数オブジェクトは、Duktype/C関数であるnative_prime_checkをバインドしてます:この時にEcmascript関数を、Ecmascript側から呼ぶために作成することで、C関数を呼び出せるようになります。第二引数の(2)は、このC関数が呼び出される際にvalue stackに必要な引数の数を示しています。もし呼び出し元がこれより少ない引数で関数を呼び出した場合、足りない引数はundefinedで埋められます; もし呼び出し元がこれより多い引数で関数を呼び出した場合、余った引数は自動で無視されます。最後の3行目で実行されていることは、関数オブジェクトをグローバルオブジェクトに格納しつつ、"primeCheckNative"という名前を付けながら関数オブジェクトをスタックからpopして取り出しています。
(サンプルコード略)
このときvalue stackにはすでにglobal objectがスタックの先頭に格納されています。1行目はprimeTest関数をglobal objectから探索しています(ロード済みのスクリプト内で定義されています)。2-4行目は、primeTest関数を引数無しで呼び出し、保護されたエラーが起こっていないかチェックしています。5行目は、実行結果をpopしてスタックから取り除いています; ここで戻り値は必要でないため。

コンパイル方法は以下:
(サンプルコード略)
実行します("prime.js"がカレントディレクトリに必要です):
(サンプルコード略)
さらに素数チェックに必要な時間を測って、スピードアップがEcmascriptと比べてどの程度有効か見てみましょう。あなたがこれを試すには"prime.js"を編集してネイティブヘルパ関数を無効にします。
(サンプルコード略)
再コンパイルして再実行すると:
(サンプルコード略)

## プログラミング・モデル

## 概観

Duktapeによるプログラミングは直裁的には以下の通りです。
・Duktapeソースコード(duktape.c)とヘッダ(duktape.hとduk_config.h)をあなたのビルドに追加
・Duktapeのheap(ガベージコレクション領域)を作成し、context(スレッドハンドラ的なもの)を初期化することを、あなたのプログラム内で行う
・必要なEcmascriptスクリプトファイルを読み込み、Duktape/C関数を準備しておく。Duktape/C関数はC関数をEcmascriptコード側から呼び出すことで、妥当な性能を出すことやネイティブライブラリへのアクセスを提供する。
・Duktape APIを用いて、Ecmasccript関数を妥当なタイミングで呼び出す。Duktape APIは関数と変数へのアクセスを提供する。変数はC変数型とDuktape内部形式(Ecmascript互換)を相互変換することができる。
・Duktape APIは一方で、Duktape/C関数(をEcmascriptから呼び出した)から、関数呼び出し時の引数および、戻り値へアクセスする方法を提供する。

これらすべてのステップを見ることが、我々の提供するさらなる情報にアクセスするのに必要です。

Heapとコンテキスト
Duktape heapは、ガベージコレクションを提供する単一の領域です。heapは文字列のために確保される領域、Ecmascriptオブジェクトやその他の変数に必要な領域、ガベージコレクション情報などに使われます。heap上のオブジェクトは、heapの先頭に持っている管理に必要な情報により、リファレンスカウント、mark-and-sweepガベージコレクション、オブジェクトのfinalizationなどを行います。Heapオブジェクトはリファレンスなどにより、ガーベージコレクションから見た到達可能性グラフ(reachability graph from a garbace collection perspective)を作ります。インスタンスに対しては、Ecmascriptオブジェクトへの参照を、キーと値によるオブジェクトとプロパティのセットにより提供します。あなたは複数のheapを持つことができ、オブジェクトは異なるheap上に直接存在することができません; (適当訳)あなたは異なるheap間では値をserializationしなければなりません。

Duktapeコンテキストは、Ecmascriptの"実行スレッド"としてDuktape heap内に生成されます。コンテキストはduk_context *によりDuktape APIから参照可能で、Duktape内部のコルーチン(a form of a co-operation thread)により組織化されます。いくつかのコンテキストを、共通のglobal objectを(訳注:ベースの?)環境として組織化することができます; コンテキストは任意のglobal objectを共有することができる一方で、異なる環境を持つことができます。コンテキストハンドルはほとんどのDuktape API呼び出しが要求し、そこからDuktapeコルーチンでvalue stackへアクセスします: 値の挿入と探索、関数の呼び出し、など。

コルーチンたちはcall stackにより実行を制御され、関数呼び出しの追跡、ネイティブまたはEcmascriptの分類、などをEcmascriptエンジン内で行います。(適当訳)コルーチンたちは一方でvalue stackを持ち、すべてのコルーチンのアクティブなコールスタックをstoreします。


== 以下、翻訳未終了 ==