ページ

2016-04-21

waifu2xAvisynthをY8に対応させた

要望があったので、気分転換に実装して、v0.0.2をリリースした。
http://waifu2x-avisynth.sunnyone.org/

あまり更新することは考えていなかったのだけど、おまけのようなU/V planeの処理を省けば良いだけだったので。

最初のリリースが2015/5/30なので、もうほぼ1年になるのか、というのが衝撃。正直この実装はwaifu2x-converter-cppとAvisynthのつなぎをしただけで、たいしたことはしていないのだけど、それでも今でもStarされたりするあたり、知名度というものはすごいなぁと思う次第である。

2016-04-13

gtk-rs のチュートリアル的文書を書いた

Gtk+のRustラッパーであるgtk-rsの入門を書いた:RustとGtk+で開発するGUIアプリケーション

ほんとはもう少しやるといいことがあるのだけど(GResource、スレッド対応など)、まぁとりあえずここまで書いてあればGUIアプリケーション書いたことある人であれば感じは掴めるのかなと。ということでRev0.1。

(少なくともいまのところは)ニーズはほとんどないと思うけど、やる気があるうちにある程度形にしておかないと絶対書かないので。自分がこれから書いていくアプリケーションを読み進めるにあたっての基礎知識となれば。

2016-04-10

メタプログラミングについて話してきました

ゆるスタック勉強会 #20 で「メタプログラミングとは」という内容で話してきました。「メタプログラミングって何?イメージがつかない」という話を受けて、イメージをつくようにするのがこの話の目的です。



本当はそれぞれの言語のやりかたについて手順を書けばよかったのですが、力つきたので、実際のライブラリから例を持ってくる方向性にしました。話している間はコードや別の資料も見ながら話していたので、このスライド自体というよりは、各スライドでポイントしているライブラリの実装あるいは類似するライブラリの実装を読んでもらうのがイメージをつかみやすいんじゃないかと思います。

それぞれの言語でニワカなところがあるので、全方位からツッコミを受けてもおかしくない内容だと思っています。何かあれば補足していきたいのでぜひどうぞ。

2016-04-01

Rust と C言語 をコールバックで行き来する(Cブリッジが必要なVer)

Rustは C FFI が強いので、はっきりしたstructとCの関数を呼び出すなんていうケースでは、それっぽいstructとexternでの関数の定義を書けば呼び出せる。コールバックもできる。
他言語関数インターフェイス

しかし、以下のようなケースではちょっとやりにくい。
  • 必要な関数の呼び出しにCマクロが必要
  • opaqueなstructの下のほうのメンバにアクセスが必要
要はC言語を解釈してもらえると楽だよねという話なので、「Cでラッパー書いてautotoolsだMakefileだなんだかんだ、ようやくつなげる」となるかと思いきや、このようなCラッパーが必要な状況についてもRustには補助がある。今回はそのやりかたについて紹介する。

自分が必要だったところがC世界からのコールバック呼び出しだったので、それについても触れる。基本的には、RustのアプリケーションがCライブラリを使うという想定。コールバックなので、行ったり来たりなのだけどね。

やりかたの概要

やりかたは概ね以下の通り。
  1. ラッパー関数をCで用意する
  2. CargoのビルドスクリプトでCソースのコンパイル方法を指示する
  3. ラッパー関数をexternで定義する
  4. ラッパー関数を呼び出すstruct & traitを定義する
  5. 実際に呼び出す
実際のソースはhttps://github.com/sunnyone/rust-bridgesampleに置いてあるので、ソースがあればいいという方はこちらでどうぞ。

やりかた

ラッパー関数の準備

Rustから呼び出したい関数を、普通の関数呼び出しにできる形で定義する。本来は何かのコードのラッパーの想定なのだが、そっち側の知識が必要となると話がややこしくなるので、ここでは単なるCソースを例として使う。



上記がsrc/bridge.cに置かれているとする。

Cargo.toml + build.rsにCソースのコンパイル方法の指示する

ここがこの記事のキモ。cargoに、置いたCソースのコンパイル方法を指示する必要がある。

まず、Cargo.tomlにビルドスクリプトが「build.rs」にあるよ、ということを指示する。またコンパイラの実行の面倒を見てくれる「gcc」crateをbuild dependenciesに入れる(gccと言っているが、MSVCも対応しているらしい。試してないが)。

[package]
(略)
build = "build.rs"

[build-dependencies]
gcc = "0.3"

[dependencies]
libc = "0.2"

dependenciesのlibcは、ビルドスクリプト的には必要ないが、C FFIではほぼ使うのでここで入れておく。

上記で定義した「build.rs」に、コンパイラの呼び出しを定義する。

extern crate gcc;

fn main() {
    gcc::compile_library("libbridgesample.a", &["src/bridge.c"]);
}

このあたりの記述、詳しくはCargoのドキュメント「Build Script Support」を参照。
本来ライブラリのリンクの指示も必要だが、ここでは一旦省く。詳しくは上記ドキュメントを参照(下でもすこし触れる)。

ラッパー関数をexternで定義する(いわゆる-sys部分)

ここからはもうRust onlyでのFFIと一緒。externでfnとstructを定義する。opaqueなstructをenumを定義するのがイディオムだそうだ。

Cargoの流儀的には、FFIのためのRustらしくない定義はなんちゃら-sysというcrateに定義するほうがそれらしい。この部分だけの差し替えがしやすいから、とのこと。

ラッパー用のstructを用意する

ffi層を叩く、Rust 的構造にあわせた呼び出し部を用意する。今回は、コールバックをクロージャでもらい、それを実際のCコールバックから呼ぶ形にした。

「外側」と「内側」の2つのstructを作る

Rust世界のコード用のデータを入れておく「内側」と、その「内側」とC関数からもらったポインタを格納しておく「外側」を用意する。


必ずしもこうでなくてよいが、こうしておくことで「内側」をBox(ヒープ)で持つことが出来、ポインタに変換してC関数のポインタに渡すことができる。

git2-rsのsrc/transport.rsでやっていたので参考にした。

Drop traitを実装しポインタが自動で捨てられるようにする

後始末大事。


コールバックとしてC側に渡す関数をexternで定義する

今回はライフタイムをRustが持っているので、キャストするだけ。ここでは、Rust側がライフタイムを握っているのでポインタを渡しているが、もしC側がライフタイムを握るようであれば、Box::into_rawBox::from_rawをうまく使うといいと思う。ライフタイムの制御から外せる。mem::forget というのもあるかな。


ラッパーを実装

あとはffi関数を呼び出すようMyStructにimplする。


呼び出しコードを書く

実際呼ぶコードはこんなかんじになる。 結果はこんな感じ。
mystruct_new
Hello, World: 11
Hello Result: 12345
mystruct_free
これでだいたい使えるはず。

おまけ:pkg-configでライブラリを参照させる

本来ラップする対象があるはずなので、リンク対象やincludeパスを指定する必要がある。基本的には、println!()で、cargoが必要な情報を渡してあげればよいが、build-dependenciesでpkg-config crateを使うと、pkg-configの結果を使える。例えばこんな感じ。 with-glib branchで使ってみてある。実際のところは、panicはやっつけなのでちゃんと分岐させたり、pkg-configに関わらず環境変数で設定できるようにしたりと、工夫がされていることが多いので、詳しくはそこらのライブラリのコードを参照。例えば、glib-sys crateはこんな感じ