UTF-8の文字列をコンソールに出そうとしたけど出せなかった

なんとなくTwitterのタイムラインをWin32 APIバリバリで取得してみようと頑張ってたんですが、どうも文字を出力するところで納得がいかなかったので憂さ晴らし。

WindowsのコンソールでUTF-8の文字列を出力しようとするとき、以下のようなパターンがあります。

  • Shift JISに変えて表示
    1. MultiByteToWideCharでUNICODE
    2. WideCharToMultiByteでShift JISに
    3. printfなどで表示
  • UNICODEに変えて表示
    1. setlocale(LC_ALL, "japanese")
    2. MultiByteToWideCharでUNICODE
    3. wprintfなどで表示
  • そのまま表示

これの3つ目がどうやってもできませんでした。setlocale(LC_ALL, "japanese_japan.65001")で出来んじゃないか?と思ったんですが、リファレンスにしっかり断り書きがありました。

The set of available languages, country/region codes, and code pages includes all those supported by the Win32 NLS API except code pages that require more than two bytes per character, such as UTF-7 and UTF-8.

Download Visual Studio 2005 Retired documentation from Official Microsoft Download Center

万事休す……と思ったんですが、chcpコマンドを使うとUTF-8の出力ができるので、ここにヒントがあるんじゃないかと考えました。chcpコマンドは以下のように使います。

chcp 65001

こうすると、出力がUTF-8になります(実際はフォントの都合で色々うまくいきませんが)。まずchcpコマンドをDependency Walkerで開き、依存している関数をチェック。

どうも SetThreadLocale 関数が気になるので、調べてみました。結果、使い方はほとんどsetlocaleと変わらない様子なので、とりあえず

//setlocale(LC_ALL, "japanese_japan.65001");
SetThreadLocale(65001);

してみました。で、結果はというとまたも失敗。GetLastErrorさんいわく「パラメータが間違っています。 」だそうです。で、他に関数ないかなーと調べてみたところ、見つけたのがこれ。

(マングルされてるけど)名前がそのものだー。というわけでMSDNさんにこの関数について聞いてみたら、この関数を使うためのインタフェースIQueryCodePageの最低動作環境がなんとWindows 7……

というわけで、setlocaleちゃんとしろ!とか思いつつ、このあたりで調査はあきらめました。文字出力したいだけならUNICODEに変換して終わりですし。

boost 1.39.0 で入った Boost.Signals2 がよさげ

先日リリースされたboost 1.39.0で新しく追加されたBoost.Signals2がいい感じです。すでにBoost.Signalsというものがありましたが、これはBoostのビルドが必要でした。しかしSignals2ではヘッダだけで事足ります。さらに、Signalsと比べてスレッドセーフという利点も。

Qtのsignal/slotを知って以来、あれを使ったイベントハンドラがどこでも使えればなぁと思ってたので、今回のヘッダのみバージョンはとても助かります。signal/slotモデルがどういったものか分からない場合は、サンプルあたりを見てみてください。

Boost.Signals2を使うことで、以下のように割とQtに近い書き方ができるようになります。

MainForm mainForm;
ListView list;
Button button;

list.selected.connect(boost::bind(&MainForm::selectListItem, &mainForm));
button.clicked.connect(boost::bind(&MainForm::submit, &mainForm));

チュートリアルの初級者向けの部分だけ翻訳してみました(Boost Signals2 Tutorial)。気が向いたらそれ以外も翻訳します。

CentOS 5のXenでdomUをkickstartしたときのメモ

公式リファレンスを参考にやってみました。dev02という名前のdomUを作ります。

[root@localhost ~]# mkdir /srv/xen
[root@localhost ~]# dd if=/dev/zero of=/srv/xen/dev02.img oflag=direct bs=1M count=16384
16384+0 records in
16384+0 records out
17179869184 bytes (17 GB) copied, 651.88 seconds, 26.4 MB/s

まずdomUで使うイメージをddコマンドで作ります。容量は16GBです。リファレンスには、この方法ともう1つ、イメージを初期化せずに時間をケチるやり方が書いてありますが、その方法でやったらパフォーマンスがかなり悪かったので、最初に初期化してやったほうがよさそうです。

[root@localhost ~]# cat /var/www/html/devserver-ks.cfg
# Kickstart file for development server
install
url --url http://ftp.riken.jp/Linux/centos/5/os/i386/
lang en_US.UTF-8
network --device eth0 --bootproto dhcp
rootpw --iscrypted ほにゃらら
firewall --enabled --port=22:tcp
authconfig --enableshadow --enablemd5
selinux --enforcing
timezone --utc Asia/Tokyo
bootloader --location=mbr --driveorder=xvda --append="console=xvc0"
user --name=rch850 --groups=wheel --password=ふにゃらら --iscrypted

# partitioning
clearpart --all --initlabel --drives=xvda
part /boot --fstype ext3 --size=100 --ondisk=xvda
part pv.2 --size=0 --grow --ondisk=xvda
volgroup VolGroup00 --pesize=32768 pv.2
logvol swap --fstype swap --name=LogVol01 --vgname=VolGroup00 --size=1024
logvol / --fstype ext3 --name=LogVol00 --vgname=VolGroup00 --size=1024 --grow
reboot

%packages
@development-tools
@editors
@japanese-support
@text-internet
@core
@base
@server-cfg
device-mapper-multipath

kickstartファイルはこんな感じで。ディレクトリ名を見て分かるとおり、作業中の環境ではhttpdが動いています。各項目についてはこちらの記事が詳しいです。パスワード部分はopenssl passwd -1で。

[root@localhost ~]# virt-install --name=dev02 --ram=1024 --vcpus=1 \
--file=/srv/xen/dev02.img \
--nographics --paravirt \
--location=http://ftp.riken.jp/Linux/centos/5/os/i386/ \
--extra-args='ks=http://192.168.10.247/devserver-ks.cfg'

とどめのコマンドです。これでしばらく待てばインストールが終わります。192.168.10.247は作業中の環境のIPアドレスとなっています。

Redmineインストールメモ

先日購入したML110 G5にRedmineを入れてみたので、その際の作業メモ。ベースはほぼすっからかんなCentOS 5.3です。

まずは必要なパッケージをインストール。gccRubyビルドのためです。また、Rubyをビルドするときにzlib-develとopenssl-develが入っていないとRubyGemsでつまづきます。

sudo yum -y install gcc zlib-devel openssl-devel

次にInstalling Redmineで勧められているRuby 1.8.7をソースからビルドし、インストールします。

wget http://core.ring.gr.jp/archives/lang/ruby/ruby-1.8.7-p160.tar.gz
tar xzvf ruby-1.8.7-p160.tar.gz
cd ruby-1.8.7-p160
./configure && make
sudo make install
cd ..

次にrequiredと書かれているRubyGemsをインストールします。これもソースから(本家インストール方法)。

wget http://rubyforge.org/frs/download.php/55066/rubygems-1.3.2.tgz
tar xzvf rubygems-1.3.2.tgz
cd rubygems-1.3.2
sudo ruby setup.rb
cd ..

RakeRubyGemsを使って簡単インストール。

sudo gem install rake -v=0.8.3

次にMySQLRedmine用のDBを作り、アクセス権限を設定します。

create database redmine character set utf8;
GRANT ALL ON redmine.* TO redmine@localhost IDENTIFIED BY 'xxxxxxxx';

最後に肝心のRedmineインストール。

wget http://rubyforge.org/frs/download.php/54503/redmine-0.8.3.tar.gz
tar xzvf redmine-0.8.3.tar.gz
cd redmine-0.8.3
cp config/database.yml.example config/database.yml
vim config/database.yml
# username, password を編集
# socket: /var/lib/mysql/mysql.sock を追加
rake db:migrate RAILS_ENV="production"
rake redmine:load_default_data RAILS_ENV="production"
# Select language: bg, ca, cs, da, de, en, es, fi, fr, he, hu, it, ja, ko, lt, nl, no, pl, pt, pt-br, ro, ru, sk, sr, sv, th, tr, uk, vn, zh, zh-tw [en]
# と質問されるのでjaと回答

database.ymlにsocketを追加する部分は本家インストール方法に書かれていませんが、これを設定しないとrakeで「/tmp/mysql.sockがない」と言われます(参考)。もちろんこれはMySQLの設定に依存してるわけですが、そっちを書き換えるよりもRedmine側を合わせるほうが適切でしょう。

Box2Dについて話してきます

縁があってFlex3勉強会第66回@北陸(福井)で話すことになりました。前回の金沢開催のときも出たのですが、今回は地元、もとい、現住所の福井開催とのことなので、ここはひとつ。ちなみに前回の懇親会で出身地どこですかーアンケートをしたんですが、そのときは北陸三県で福井が一番少なかったです。まぁ人口比的にはもっともな結果なんですけどね。ちなみにネタはBox2Dです。

ところで今回の勉強会が開催されるAOSSA、福井県民的には記録的がっかり建造物として有名ですが、勉強会ではぼちぼち使われるようになっているようです。18日にはFITEA 春の勉強会が開催されます。会場が割と安いって話は聞いてるので、勉強会が開催しやすいのかもしれないですね。しかし18日と言えばIGDAのスクリプト第2回。関東に住んでたら行ってただろうなぁ。Xtalもあるし、某クイズゲームの人とか参加しそうだ。

TTSSHをビルドしてみた

ウィンドウが半透明にできるというどうでもいい理由でTeraTermを使ってるんですが、前々から思うところがあったのでソースをいじってみることにしました。

ビルドする方法についてはこちらに詳しく書かれています。

うちの環境はここで注記が入っているVS2005なので、環境変数をいじる必要がありました。

まずTeraTermそのものをビルドします。これを実行すると、ttermpro.exeが生成されますが、普段使ってるバージョンとちょっと違います。メニューが英語なのはいいとして、起動直後の接続方法にSSHがありません。

SSHをサポートさせるためには、ttxssh.dllをttermpro.exeのあるディレクトリにおく必要があります。DLLの作り方は上のマニュアルの「■ TTSSHのビルド方法」に書かれています。ここで生成したDLLをコピーすれば普段使ってるTeraTermになります。

ビルド通ったのでさっそくソースをいじってSCPの受信ディレクトリを指定できるようにしました。

今後は送信先なんかも指定できたらいいなーと思ってます。

画像処理ライブラリのパフォーマンス比較

……っていうのがあればいいんですが、ちょっと探したところだと見当たりませんでした。品質比較なら結構あるんですけどね。というわけで、自分で少しずつ書き進めてます。

モチベーションは、ImageMagickでGIFへの変換速度が異常に遅いこと。twitterのロゴ画像をlibgdとMagick++(ImageMagickC++ API)で変換する速度を比べると、10倍ぐらい差が出るんですよ。以下ざっとRDTSC命令で測ったクロック数。

          gd png2gif:            138661147 [clock]
         gd png2jpeg:             81727812 [clock]
    Magick++ png2gif:           1246842821 [clock]
   Magick++ png2jpeg:            168686610 [clock]

厳密にPNGからGIFへの変換部分のみを抜き出して測ってるわけではないので、参考程度の数値なんですが、にしても遅いです。ちなみにgprofとかoprofileはVirtualBox上で動かしているからか機嫌が悪かったようなので、思ったような値を返してくれませんでした。

ちなみにすべてのPNGで遅いわけではなく、Magick++のサイトのロゴをやってみたらこのような結果にはなりませんでした。

          gd png2gif:            127497390 [clock]
         gd png2jpeg:            362692980 [clock]
    Magick++ png2gif:            273114407 [clock]
   Magick++ png2jpeg:            947084217 [clock]

逆にJPEGへの変換が遅くなっているので、おそらくカラーテーブル周りの処理だとは思うんですが、libgdはそこをうまくやってのけているんですよね。また時間があるときに詳しく掘り下げてみたいと思います。