2011-06-27

JavaScript: new無しで基本型のコンストラクタを呼んだ時の挙動

Closure Compilernew無しのArray()を使うのを見て興味を持ったので調べた。適当にまとめるとこうなる。

型名挙動
Object
String
Boolean
Number
引数をその型に変換
Function
Array
RegExp
Error
newがあるときと同じ
Datenew Date().toString()と同様
(引数は無視される)

Dateだけ挙動が不思議。

以下ECMA Scriptの仕様書より抜粋。

15.2.1 The Object Constructor Called as a Function
When Object is called as a function rather than as a constructor, it performs a type conversion.

15.2.1.1 Object ( [ value ] )
When the Object function is called with no arguments or with one argument value, the following steps are
taken:
1. If value is null, undefined or not supplied, create and return a new Object object exactly as if the standard
built-in Object constructor had been called with the same arguments (15.2.2.1).
2. Return ToObject(value).

15.3.1 The Function Constructor Called as a Function
When Function is called as a function rather than as a constructor, it creates and initialises a new Function
object. Thus the function call Function(…) is equivalent to the object creation expression new
Function(…) with the same arguments.

15.4.1 The Array Constructor Called as a Function
When Array is called as a function rather than as a constructor, it creates and initialises a new Array object.
Thus the function call Array(…) is equivalent to the object creation expression new Array(…) with the
same arguments.

15.5.1 The String Constructor Called as a Function
When String is called as a function rather than as a constructor, it performs a type conversion.
15.5.1.1 String ( [ value ] )
Returns a String value (not a String object) computed by ToString(value). If value is not supplied, the empty
String "" is returned.

15.6.1 The Boolean Constructor Called as a Function
When Boolean is called as a function rather than as a constructor, it performs a type conversion.
15.6.1.1 Boolean (value)
Returns a Boolean value (not a Boolean object) computed by ToBoolean(value).

15.7.1 The Number Constructor Called as a Function
When Number is called as a function rather than as a constructor, it performs a type conversion.
15.7.1.1 Number ( [ value ] )
Returns a Number value (not a Number object) computed by ToNumber(value) if value was supplied, else
returns +0.

15.10.3.1 RegExp(pattern, flags)
If pattern is an object R whose [[Class]] internal property is "RegExp" and flags is undefined, then return R
unchanged. Otherwise call the standard built-in RegExp constructor (15.10.4.1) as if by the expression new
RegExp(pattern, flags) and return the object constructed by that constructor.

15.11.1 The Error Constructor Called as a Function
When Error is called as a function rather than as a constructor, it creates and initialises a new Error object.
Thus the function call Error(…) is equivalent to the object creation expression new Error(…) with the
same arguments.

2011-03-18

murmurをMac OS X 10.4 PowerPCでコンパイルする

Mumbleというオープンソースのボイスチャットのソフトを見つけた。サーバの実装はMurmurというらしい。自宅で動かしているサーバはPowerPCのOS X Tigerという古いもので、murmurの最新版ではサポートされていない。でもいじりまわした結果なんとか動いたのでメモする。

うまくいくまでの経緯

まず、DarwinPortsにmurmurがあったのでそれを単純に入れてみようとした(sudo port install murmur)。依存関係のビルドは、qt4-macというportの作業領域に6GBほどHDDを食われたものの、特に問題なくすんだ。しかし、murmur自体のビルドでエラーになった。ログを見るとコンパイラの引数に-arch i386という文字があったので最新版はとりあえず諦めた。

DarwinPortsを諦めてtarballを野良ビルドすることも考えたが、サーバとして動かすなら、ビルドするだけではすまない。LaunchDaemonsなどに登録する必要がある。plistなどを用意する作業と天秤にかけて、Portfile(DarwinPortsのビルド手順を記述したスクリプト)をいじってみることにした。

検索してみると、昔のバージョンではppcでも動いていたような報告がある。Portfile歴史をさかのぼってみると、1.2.2(この記事時点での最新 r75458)になる前のバージョンは1.1.8だった。とりあえずr69125のPortfileを落として野良ビルドしたところ、qmakeが見つからないというエラーが出た。

最新版のPortfileではqt関係の記述が変わっていたので、それを見ながら適当にPortfileをいじった。ビルドしなおすとエラーが変化して、リンク時にエラーが出た。/usr/bin/ld: Undefined symbols: _Gestaltという見覚えのあるエラーが出ていたので、リンカのオプションに-framework CoreServicesをつけるようなMakefileを生成するようにqmakeに引数を与えるようPortfileを修正した(こういうとき自動化は怖い)。

いじったPortfile

できあがったPortfileは以下。そのうちフィードバックしたい。

使い方としては、このPortfileを空のフォルダに置き、そこにcdしてsudo port installとすればよい。

追記

umurmurというQtに依存しないクローンがあった。ビルドがmake一発と簡単な上1.2のプロトコルにも対応しているので、こっちを入れるほうがよさそう。

野良ビルドのメモ:

  • libconfigというライブラリはDarwinPortsにあるものとは別物らしい
  • 定数_POSIX_PRIORITY_SCHEDULINGを#undefする必要がある

2011-02-14

Thumbs.dbのフォーマット

WindowsのThumbs.dbはOLE Compound DocumentとかStructured Storageという形式でできている。Wordのdocファイルにも使われていて、要はアーカイブの一種らしい。

この形式を解凍する方法自体はメモしない。ここでは内部にどういうファイルが入っているかだけ書く。

これを読み込むライブラリは、Rubyではruby-oleジェム、PerlならWin32::OLE、JavaならApache POIのPOIFS、CならCOMのIStorageとかPOLEというライブラリがある。

ディレクトリの内容

そのたぐいのライブラリで解凍すると、中身のディレクトリはこうなっている。フォルダはなく、いくつかバイナリファイルが入っているだけ。

  • Catalog
  • 01
  • 1
  • 11
  • 2
  • 21
  • 3
  • 4
  • 5
  • ...

内容としては、名前が数字のファイルがJPEGファイルにヘッダがくっついたもので、Catalogにその数字と実ファイル名の対応付けが記録されている。

各サムネイルにはIDがついている。

不思議なのはIDとファイル名が文字列として反転しているというところ。

idが5のサムネイルは5というファイルに入っているが、idが13のサムネイルは31というファイルに入る。

カタログ (Catalog ファイル)

Catalogのフォーマットは先頭からこう。数値は全てUint32LE。

  • ヘッダが16バイト
    • 4バイト~8バイト目にサムネイル数
  • サムネイル数だけ以下を繰り返し(各エントリのサイズは不定)
    • エントリ全体のサイズ
    • ID
    • 更新日時
    • 何かの数値
    • UTF16LEでファイル名
    • 0でパディング

サムネイル (0, 1, 11, 2, 21 ..)

サムネイル画像のファイルの内容は以下のとおり。

  • ヘッダ (12バイトか16バイト)
  • JPEGファイル

ヘッダの長さは可変というか、12バイトか16バイトかのどちらからしい。

本来ならヘッダにあるタイプを調べるべきなのだろうが、とりあえずJPEGファイルの頭にあるマジックナンバー(0xFF 0xD8)を目印にすればヘッダの内容を読む必要はない。

実装

画像を吐き出すだけのツールを作るなら、流れはこうなる。

  • Structured Storageのライブラリを使ってthumbs.dbを読み込む
  • Catalogファイルを取り出して上のフォーマットで解釈する
  • 各ファイル名についてサムネイルを取り出し、JPEGファイルをその辺に書き出す

これをRubyとC++で書いた。

手抜き

以上はlibforensicsというPythonのライブラリのlf.win.shell.thumbsdbのコードを読んだだけなので、このライブラリを使うのが一番早いかもしれない。

こういった解体処理をする必要があるのは元のファイル名が欲しいときだけで、サムネイル画像を取り出したいだけならまじめに読み込む必要はない。

無圧縮で暗号化もされていないので、thumbs.dbを普通に開き、JPEGのSOI(0xFF 0xD8)とEOI(0xFF 0xD9)を探して次々書き出せばすむ。

(copied from tumblr)

2011-02-06

android.util.Config.DEBUGが役に立たない

public static final boolean DEBUG

If this is a debug build, this field will be true.

android.util.ConfigというクラスにDEBUGというbooleanの定数がある。説明の通りデバッグビルドかどうかこの定数を通じて調べることができる。これは嘘ではないのだが、アプリケーション(apk)のビルドのことではなく、デバイスがデバッグビルドかどうかというのを見るものらしい。つまり、アプリケーションの開発者の欲しがるような「LOGのメソッドを呼ぶか切り替えるスイッチ」ではない。

代案としてあがっているものはどれも微妙に意味あいが違う。

  • ユニットテストを真面目に書く (・・・ようにすれば、デバッグのためのログは必要なくなるはず)

ウィザードモードなんかをつけたい場合には応用がきかない。

  • デバッガが接続されているか調べる

if (Debug.isDebuggerConnected()) { ... }

見た目に綺麗だが、「実機に入れて動かしていたら落ちたのでログを見たい」という場合とは使いどころが合わない。

  • マニフェスト(AndroidManifest.xml) の属性設定でDebuggableを切り替え、それを調べる

if ((applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { ... }

おそらくこれが最もまっとうな方法。設定ファイルに書き込む欄があり、それを調べる手段があるのだから、利用しない手はない。ApplicationInfoを取得するのに手間がかかるのが難点。

  • 自前で定数を定義する

static import com.example.android.app.StaticConfig.DEBUG;

if (DEBUG) { ... }

上に出た代案の問題はどれも問題がある。条件がコンパイル時定数でないので最適化されず、結局コンパイル後にも条件の判定やLOG文は残ってしまう。「#ifdefの代わり」としては結局これしかないように思う。

DEBUG = true; DEBUG = false; を手で切り替える手間を省けないかと考えている。antの-pre-compileタスクあたりにコード生成を挟めばよいのだろうか。

(copied from tumblr)

2011-01-07

異種enumの比較を防ぐ

Visual C++ 2010はenum classがない(SP1でもC++0xの機能を増やす予定はないとか)。

でも、enum classがなくても型チェックはある程度できる。

enum WeaponType {
    WEAPON_TYPE_MOCHI,
    WEAPON_TYPE_SALMIAKKI,
};
enum TreatsType {
    TREATS_TYPE_MOCHI,
    TREATS_TYPE_SALMIAKKI,
};
// the trick is here
void operator ==(WeaponType, int);
void operator !=(WeaponType, int);
void operator ==(int, WeaponType);
void operator !=(int, WeaponType);
void operator ==(TreatsType, int);
void operator !=(TreatsType, int);
void operator ==(int, TreatsType);
void operator !=(int, TreatsType);
void test() {
    if (WEAPON_TYPE_MOCHI != WEAPON_TYPE_SALMIAKKI) { // ok
        eat();
    }
    if (WEAPON_TYPE_MOCHI == TREATS_TYPE_SALMIAKKI) { // error!
        scream();
    }
}

enum#defineconstの羅列より短いから使っているだけなので、自分は使わないだろう」と思っていたが、昨日別種のenumを比較するコードを書いてバグを出し、30分悩んだ。なので少なくとも型チェックのところは欲しいと思うようになった。

type-safe enumのようなクラスでラップするという方法は確実だが、文字数がとても多いのでそらで書き下せる自信がない。「enumのまま型チェックをする」方法を考えていたら上記の方法を思いついた。

つまり、「比較のoperatorを定義して、実装しない」という方法だ。

最初は operator ==(WeaponType, TreatsType); のように定義していたが、 enum の数を増やすと爆発することに気づいて int に変えた。 int でないほうがエラーは多少読みやすい。

返り値を void でなく bool で定義すると、リンク時のエラーになる。 void で定義するとコンパイル時にエラーが出てくれるのでそうした。

2010-11-28

jom: 並列nmake

jomというQtの作っているビルドツールを見つけた。

Visual C++付属のmakeであるnmakeのクローンらしい。違いはマルチコア用に並列ビルドができること。バイナリが単独で公開されているので最近利用している。

gmakeは-j 5とすると5プロセスで並列でビルドを走らせてくれるが、nmakeは未だにそのサポートがない。cl /MP a.c b.cというオプションで並行でコンパイルはしてくれるが、makeを使う場合ソースファイルはひとつずつしか処理されないので、このオプションは使えない。

  • MSVCでプログラムを作っているのに
  • Visual Studioではなくnmakeを使っていて
  • nmake用のMakefileを捨てたくないが
  • コンパイル速度を上げたい

というかなりニッチな需要を満たしてくれる。(というか、本来はQtユーザ用なのだと思う)

ベンチマーク

手元の自作ゲームを一からビルドする時間を計ってみた。3GHzのCore 2 Duoで試した。

『体力制チェス』 (総計9461行、53ヘッダ+47ソース=100ファイル)

--nmakejom
プリコンパイルヘッダなし25.2 秒14.23 秒
プリコンパイルヘッダあり5.0 秒3.6 秒

『加速度センサー小品集』 (総計10067行、51ヘッダ+57ソース=108ファイル)

--nmakejom
プリコンパイルヘッダなし33.6 秒18.5 秒
プリコンパイルヘッダあり7.2 秒4.9 秒

それなりに効果はあるようだ。(ディスクキャッシュの話があるので細かい数字は信用できないが)

移行

クローンとはいえ、既存のMakefileを全くそのまま使えたわけではない。

依存関係を見て並列的に処理できるところを分けるので、依存関係をきちんと設定していないと順不同になるところが出てくる。最初に試したときはmkdirが偶然うまくいっていた所がつまづいた。

Makefile内でINCLUDE環境変数を書き換えてインクルードパスを無理やり設定していたが、その方法が通じなかった。CFLAGSに直接追加することで解決した。INCLUDEをMakefile内で設定してもコンパイラに伝わらないようだ。

下位互換性はあるので、とりあえず仲間内では自分だけ使おうと思っている。

Nov 28th, 2010 11:10pm
(copied from tumblr)

2010-09-01

マンデルブロ/ジュリア/バーニングシップ集合ビューア

Mandelbrot

よくあるマンデルブロー集合のビューアを作った。マウスだけでつかんで4次元で切片を動かせる。緑が発散までの回数、赤青は発散した向き。

ダウンロード

mandelbrot.zip

gist:525284

操作

操作効果
ドラッグ移動
ダブルクリック移動
マウスホイール拡大・縮小
右ドラッグ定数の調整
右クリックモード切り替え (ジュリア集合とか)
左クリックしながらマウスホイール繰り返し回数の調整
右クリックしながらマウスホイールモード切り替え (ジュリア集合とか)
マウスホイールをクリックリセット

(copied from tumblr)