d.sunnyone.org
sunnyone.org

ページ

2015-07-13

bsdtarでzip展開時の先頭ディレクトリを削る

zipに限らずアーカイブにはこんな形でファイルが入っていることがある。
hoge-0.1.2/README.md
hoge-0.1.2/configure
hoge-0.1.2/src/
hoge-0.1.2/src/main.d

これをunzipで普通に展開すると、当然ながらhoge-0.1.2ができるが、バージョンが入っているのがうれしくないことがある。ここで、tarの「--strip-components」オプションと、zipを展開できるbsdtarを組み合わせることで、hoge-0.1.2を外して展開することができる。
$ bsdtar xvf hoge-0.1.2.zip --strip-components 1
x README.md
x configure
x src/
x src/main.d

どうでもいいTipsでした。

2015-07-04

Loqui 0.6.1 リリース(タブ補完機能の追加)

0.6.1を出しました(0.6.0はバグがあったので、すぐに0.6.1をリリース)。前回が2013-03なので、2年ぶりくらいですね。

そこまで大きな変更はないのですが、タブ補完機能は純粋に新機能なので、新しいバージョンにしてみました。現在のアカウントのチャンネル名と、現在のチャンネルのnick名を補完できます。かぶっていた場合はステータスバーに候補を出すので、意味ないから消してたという人がいたら、一度出してみてください(UIとしてはイマイチですが、邪魔にならず、しかも一番楽に作れたので...)

launchpadがGitに対応しているらしいので、本当はgitに移行しようかなとも思ったんですが、過去のリンクを切ってまでこれから変更していくこともないかなと。

昨日フォントが変えられず困っている方がいたので、確認してみたところ、チャンネルバッファのフォントが変更できないバグがあったので直したのが0.6.0のきっかけです。フォントは~/.loqui/stylercのGlobalFontの行を以下のように変更することで、変えることができます。

GlobalFont=Takao Gothic 14

2015-06-13

RustとWindowsのワイド文字列(LPCWSTR / LPWSTR)

最近?のWindowsでC FFIで文字列を受渡しをするとなると、ほぼ確実にでてくるのがワイド文字列(「Unicode」文字列)。それをRustでどう取り扱うかについて。

最初に感想を書いておくと、今のところ、Rustでのwchar_t *なNULL終端された「UTF-16」文字列の取り扱いは、char *なNULL終端されたUTF-8文字列に比べるとあまり親切な取扱いではない。しかし、そこまで大変でもなかった。たぶんUTF-8じゃないマルチバイト文字列を扱うよりラク。

前提知識

コードを読む前の前提の話について、箇条書きで書くとこんな感じ:
  • Rustのふつうの文字列は、ざっくり言うとimmutableなstrと、mutableなString。
  • std::str/Stringは、UTF-8文字列の型。NULL終端ではない。
  • std::ffi::CStr/CStringは、UTF-8なchar *(NULL終端あり)をRustの世界で扱えるようにするラッパーの型。コピーが起きにくいようになっている。
  • std::ffi::OsStr/OsStringは、Windowsのワイド文字列も扱えるようにした文字列の型(NULL終端でない)
  • unixとwindowsのそれぞれにOsStrExt / OsStringExt traitがあり、OsStr/OsStringの出入りに使える。
  • Windowsは、std::os::windows::ffi::OsStrExt と std::os::windows::ffi::OsStringExt (ソースを見るに1.0 stableなのにdoc.rust-lang.orgにない...)

変換関数

と、いうことで、便利なCStr/CStringは使えないものの、OsStr/OsStringを経由させればOK。ただし、NULL終端あり/なしの変換にちょっと小細工がいる、という感じ。

コードはこちら。

「from_wide_ptr」は、from_wide()が&[u16]を受け取るので、スライスを作っていて、スライスを作る大きさを\0までの長さで計算している。

「to_wide_chars」は、encode_wide()でIteratorを得て、NULLだけを返すIteratorをSome(0).into_iter()で作ってchain()でくっついたIteratorを生成し、collect()でVecにしている。

一応呼び出し側のサンプルはこちら→https://gist.github.com/sunnyone/1d9e081df7eada21911d
(コンパイル方法はRustでDLLを作るの記事を読んでね)

注意書き

trait作ってprimitiveにimplしたほうがいいかもしれない。そこは実際に自分が使うときにやるかも(真面目にOsStringExtに実装してpull reqしたら取り込んでもらえる?プロセスの堅さどうなんだろ...)

「from_wide_ptr」は、効率が悪いと思いつつ利便性は一番これがいいかなと思ってto_string_lossy()をownedにした(コピーした)Stringを返しているけども、これがいつでも最適解だとは思っていない。OsStringからはlosslessにencode_wide()できると言っているので、ただ引き回すだけならOsStringそのままがいいかもしれないし、厳密性が大事なやつは不正なバイト列があったとき(たぶんペアになってないサロゲートペアとか)に変換に失敗するinto_string()のほうがいいかもしれない。取り扱っているコード次第だと思う。

2015-06-12

C++のラムダ式を利用してお手軽にOutputDebugString()を呼び出す

Windowsでデバッグ出力をしたいと思って、OutputDebugString()を呼ぼうとすると、printf的なフォーマットがないので、ちょっと文字列に数値なんかをつけたいと思うと面倒。sprintfを呼んでしまうというのもひとつだけども、バッファサイズをどうしようとか考えたくない。boost::formatがあればまぁそれをさらっと使えばいいと思うけども、今回のは「boost持ってくるほどのものでもなぁ」というサイズのコードのとき。

で、stringstream使えばいいかなということでベタで呼び出すとこうなる(OutputDebugStringWのほうがいいと思うけど、そこは適宜読み替えで)。
std::ostringstream str;
str << "Num: " << i << std::endl;
OutputDebugStringA(str.str().c_str());

sprintf呼ぶよりはいいと思うのだけど、何回も呼ぼうとするとstr変数がすでにあったりなかったりでややこしい。それで今回は、ラムダ式を使う。

このようなユーティリティ関数を用意して:
void outputDebug(std::function<void(std::ostringstream&)> func) {
 std::ostringstream str;
 func(str);
 OutputDebugStringA(str.str().c_str());
}

こう呼び出す:
outputDebug([&](std::ostringstream& s) { 
 s << "Num: " << i << std::endl;
});

どうだろう?ostringstreamの型名を書くのがちょっと悲しいけど、最初よりはいいんじゃないかなと思う。

C++らしさというのは全然わからないので、仰々しくならずにできる、もっといいやりかたがあればぜひ教えてほしい。

2015-06-07

RustでDLLを作る

期待通りの方法で作れた。ちなみに:
c:\>rustc --version
rustc 1.2.0-nightly (613e57b44 2015-06-01) (built 2015-06-02)
今回のは特にfeature使ってないから1.0でいけるかな?

DLLの作り方

まずプロジェクトを作る。
cargo new rustdlltest

Cargo.tomlを書き換えて、[lib]を作る。詳しくはhttp://doc.crates.io/manifest.htmlを参照。

lib.rsに、no_mangleとextern "C"をつけて、関数を記述する。

詳しくは、Rust bookの「Foreign Function Interface」を参照。

あとは、
cargo build
か
cargo build --release
で、target\debugあるいはtarget\releaseにfoo.dllができている。

Dependency Walkerで見てみると、いろいろな関数がexportされているが、その中で指定したhelloがexportされていることがわかる。


呼んでみる

楽なのでC#から呼んでみるには、こんな感じ。


結果はこう:
>RustLibSample.exe
A negative count is not supported.
Result for -1: False
Hello, World: 0
Hello, World: 1
Hello, World: 2
Result for 3: True

問題ない。boolってなによと思ってintにしてみたら、0と1らしい。

2015-05-30

waifu2xのAvisynthプラグインを作った

Windowsで動くC++版を書かれた方がいる(waifu2x-converter-cpp)という話を聞いて「Avisynthに乗せられるんじゃね?」ってことで、実際に乗せてみました。これで動画も変換できます。

いろいろ更新がありそうなので、ダウンロードリンクや使い方を書いたページを用意しました。詳しくはこちらを見てください。
http://waifu2x-avisynth.sunnyone.org/


ファイルに書き出さなくていいとか、モデルのローディングを初回しかしないとか、中の入出力部分の処理をちょいちょい変えたりしているので、各フレームの画像をコマンドで変換するより若干より速いかと思います。


現時点では、見ての通りびっくりする速度でした(Core i5-3570S。ページ書いたとき、Haswellのマシンだと勘違いしてましたがIvyでした)。特にがんばってはいないので、最適化する余地はたくさんあると思います。しかし、物理で殴ってでも欲しい人がいるかもしれないので、出してみました。

2015-05-17

「RustからPythonを呼び出す」資料と補足

Rust 1.0 Release記念祝賀LT会 お疲れ様でした。そして1.0.0リリースおめでとうございます。今回は、その祝賀会で「RustからPythonを呼び出す」という話をしたので、その補足エントリです(資料は最下部に配置)。

rust-cpythonの使い方

RustからPythonを呼び出すには、rust-cpython crate が使えます。使い方は、githubに書いてあるとおりですが、以下のような感じで簡単に使えます(恐らく非Windows/x64 or Windows/x86,x64なら:資料参照)(2015/5/27追記:下部追記参照)

$ cargo new newapp
$ cd newapp

(Cargo.tomlを編集)
[dependencies.cpython]
version = "*"

(src/main.rsを作成)
extern crate cpython;

use cpython::{PythonObject, Python};

fn main() {
    let gil_guard = Python::acquire_gil();
    let py = gil_guard.python();
    let sys = py.import("sys").unwrap();
    let version = sys.get("version").unwrap().extract::().unwrap();
    println!("Hello Python {}", version);
}

$ cargo run

作りたかったもの

なぜこのような話になったかというと、DLLを作成することで拡張できるアプリケーションがあるのですが、その拡張する内容自体、LLでちょいちょい変えていきたいので、LLとつなぐブリッジとなるDLLを作りたかった(作りたい)のです。Pythonなのは、アプリ埋め込みがしやすく、統計処理が得意なライブラリを持っているから。あと、coroutineが使いたいので、Python 3.4以降が使いたいです(ここはRustでもまだできていないところですが、python3-sysがあるので、どっか切り替えればなんとかなるんじゃないかと思っているところ)

考えた言語たち

Rustを使いたいからというよりも、Rustが向いているのではと思ったからです。以下、考えた言語たち。

C#

今回はWindowsの世界だとわかりきっているので、私的にはC#で書けると一番楽です。しかし、C#のDLLはmanaged (CLRで動作する) DLLなので、普通のC関数として呼び出すことはできません。一応、その向きのために「Unmanaged Exports」というのがあるようですが、コンパイルできませんでした。多分「Can't create unmanaged dll using C# and Robert Giesecke's Unmanaged Exports tool」という記事にある問題だと思うのですが、試してません。動いても動かすにはCLRがついてくるはずなので、フットプリントが大きくなり、単なるPythonブリッジにはちょっと大きいという点が気になります。

C

いろいろなもののベースになっているはずなので、Cに限った話ではないのですが、PythonのC APIは親切に提供されているので、Cで書くのも悪くないと思います。ないがしろになりがちそうなC拡張のドキュメントがしっかり書かれていて、和訳もあるという親切な環境です。悪くないといえば悪くないのですが、リソース管理系の話だとか、objectiveな操作が書きにくいとか、ヘッダファイルが別で冗長とか、いろいろあるので、他にベターなものがあれば避けたいなと。

D

Win32 DLLs in Dという話も書いてあるし、これは結構ありかなと思っています。pydというPython bindingもあるみたいです。CPython 2.6+とあるので、2系しか考えてない気がしますが、Python 3系いけるかどうか。

ただ、GCがついてくるので、そのへん意識しないといけないですね。

Free Pascal (Object Pascal)

どこかでこれでDLLを書いてる例を見かけて、意外とありなのかなと思ったりしたりしました。Python4DelphiというPythonバインディングもあるようです。今からPascal覚えるのかーというところですね。

Rust

ここでRustを思い出して、よさそうだなと。#![create_type = "dylib"]と#[no_mangle]でDLLは作れそうだし、GCがないのでランタイムのオーバーヘッドも小さそうだし。そしてrust-cpythonを動かしてみたら、動いた ← 昼間ココ というわけです。

Pythonそのもの

呼び出そうと思っているのはPythonそのものなので、DLLがPythonで書けるとベターです。できないと思っていたのですが、Cythonを使うのはどうという話があり、こっちのほうがいいかも? ← 今ココ です。Pythonではないけど、専用のサポートがあるし、読む人もなじみやすそうですね。

とこんなかんじですが、他にもおすすめがあれば教えてください。

資料

ともあれ、資料はこちらになります。



. o O (もうちょっとうまいこと話せたらよかったのだけど、スタートがうまくしゃべれなかったせいでぐだぐだになってしまった...)

[2015/5/27 追記]
資料にあるLinux/x86でビルドできない罠ですが、Linux/x86対応をpull request出したらマージされたので、Linux/x86でもgitのmasterはビルドできるようになりました。Cargo.tomlもgitのほうを向ければビルドできるようになると思います。