相変わらず脱線しまくって、予定外のことばかりやってます。
プラグインにArray.map()とArray.grep()を追加しました。それぞれPerlのmap関数、grep関数そのまんま(+α)です。Array.map()の引数には関数オブジェクトを渡します。場合によっては、for文による配列イテレーションの代わりになります。Array.grep()の引数にはPerl同様、正規表現オブジェクトか関数オブジェクトを渡せるようにしたほか、判定条件を逆転させるフラグも追加しました。
相変わらず脱線(以下略)
XHTML1.1への変換時に、A要素やMAP要素のNAME属性をid属性に変換する処理を追加しました。重複して指定された属性を削除する処理も追加しました。同一属性の重複指定はSGML的にダメです。
プラグイン。配列から重複する要素を取り除く関数Array.uniqueを実装しました。同一判定には"==="演算子と"=="演算子から選択することができます。
配列関係の仕様はこの辺でフィックスします。
プラグイン。SQLiteインターフェースを、本体2.25beta9用に書き直しました。似非ネイティブクラス実装だったのが、まっとうなネイティブクラスになりました。これで後ろ暗いところはなくなりました(←後ろ暗かったのか)。
さて、ドキュメントを整備しないと…
INFERNO「灼熱」でアイテム稼ぎをして、イズナーFFゲット。これでペイルウィングの武器は全て揃いました。残るは陸戦兵の武器ですが、ちっとも出ません。超級が殺虫スプレーのみというのは悲しすぎます。
プラグイン。SQLiteインターフェースの書き直しに伴って、仕様が一部変更になりました。なるべく仕様を変えないつもりだったのですが、アレな部分が気に入らなかったので。もし使ってる人がいたらごめんなさい。
と書いてる矢先に2.25beta10がリリースされていることに気づきました。
プラグインでバグ発見。引数で受け取ったコールバック関数を実行する場合は、クロージャで実行してやらないとダメですね。iTJSDispatch2::FuncCallで実行する場合は、objthisをきちんと指定しないと、明示的にコンテキスト指定された関数が期待通りに実行されません。以後気をつけよう。
ほか、global.getClassInstanceInfo()を追加しました。使い道は…ない?
プラグイン。ネイティブクラスだけでなく、通常の関数も半自動生成させるようにしました。これで関数やクラスメンバの記述方法をほぼ統一することができました。
ついでに、TJS2オブジェクトのコンテナ(?)となる、C++のクラス名も自動生成させるようにしました。これでプログラマ(私)はC++のクラス名を意識せずに済みます。
ついでについでに、マクロ、メッセージ、例外処理周りを強化しました。作業中に、Layer.colorEllipse()でメモリリークするバグが見つかったり…orz
ついでについでについでに、ドキュメント自動生成ツールを、関数だけでなくプロパティにも対応できるようにしました。その過程で、Layer.equalize()とLayer.equalizeColor()のドキュメントを書き忘れていたことに気づいたり。なんだかなあ、もう。
よって、このケースでは
3.4×1038÷(1.0×1010×1.0×1010×3.2×107) ≒ 1.1×1011
ですから、わずか一千億年 *1 程度で全てのUUIDを使い尽くしてしまうことになります。
マグマ火炎砲を所持していたことに気づきました。いつの間に入手していたんでしょう?
今日もINFERNO「灼熱」で武器稼ぎをしましたが、何も出ませんでした。俺の「THE 地球防衛軍2」には、超級武器のデータが入ってないんじゃないかと。(←単に運が悪いだけです)
TeXを参考にしているせいか、文字の配置、ルビの振り方等、かなり美しく組版されています。これ、そのまま頂こうかな。
SQLitePlus 3.0.7に付属していたisqliteを、一部日本語化しました。ついでに、インタラクティブモードのコマンドにエイリアスindexesを追加しました。index という単語の複数形はindices とindexes がありますが、isqliteにはデフォルトでindicesしかなかったので。
isqliteはもともとシンプルに作られているので、折を見ていろいろ改造してみましょうか。
プラグイン。util_sqliteのmakeにはsqlite3.libとsqlite3.dllが必要なのに、makeコマンドだけではこの二つが生成されません。予め別に作成しておく必要があります。前から思っていたんですが、これ、面倒です。だったら対処しろよ、との心の叫びに従って、Makefileを改良しました。これでようやく真の「make一発」になりましたよ、ああ楽ちん。
ちなみに、make release一発で、全ライブラリ再作成、全ドキュメント再作成、全リリース用アーカイブファイル作成までやってくれるので、私は楽ちんです。作者(私)にしか恩恵がないですけど。
プラグイン。使い道が不明な関数、global.getClassInstanceInfo(obj)のドキュメントを書き忘れた気がします。はい、忘れてました。
DXライブラリLunaから、ソケットライブラリであるLunaSocket、LunaSocketAsyncIO、LunaSocketServerAsyncを引っこ抜いて、これらだけでコンパイルが通るようにしました。
…した矢先、Luna本家ではすでに、LunaSocket系がLunaNetworkに移行されていることに気づいてがっかり。
INFERNO「灼熱」。相変わらず超級武器が出ません。「殺虫スプレー」はもういいから、ハーキュリーとかUMXA-V2を出してくれ。
放置気味だったティンクルスタースプライツをプレイ。キャラクタモードの最後の空欄、ドラゴンスター リアリー・ティルがなかなか出てこず、ストーリーモードを何周もしました。ほとんど「作業」でした。運が悪かったのでしょうか。
ギャラリーにサターン版OPが追加されたので、鑑賞。この下手くそな歌はほとんど音波攻撃ですね。この歌い方はとても独創的ですね。PS2ということもあって、画質はかなり向上しています。まあ、サターン版ではハードの制約上、画質を落とさざるを得なかったわけで、PS2版で本来の画質になった、というところでしょう。ただ、画質は向上しても歌唱力は向上しなか…うわ、何をす…
で、NEOGEO版。ラスボスが凶悪ですね。特にエキストラアタック。PS2版ではずいぶん弱体化しているように感じますが、やっぱり歳のせ…うわ、何(略)
怪しげな関数を思いつきました。Javaでは表現しにくかった、Smalltalk風の表記法も、TJS2でなら実現できる!
Array.timesRepeat = function (lambda) { if (this.count > 0) { var cnt = this[0]; if (cnt > 0) { while (cnt--) { lambda(); } } } }; [ 3 ].timesRepeat(function () {println("hoge");});
Array.ifTrue = function (lambda) { if (this.count > 0) { lambda() if (this[0]); } }; Array.ifFalse = function (lambda) { if (this.count > 0) { lambda() if (!this[0]); } }; var foo = 5; var bar = 3; [ foo > bar ].ifTrue(function () {println("fuga");}); [ foo > bar ].ifFalse(function () {println("xyzzy");});
アホです。配列をこんなことに使うなよ…
冗談はさておき。TJS2では、オブジェクトはプロパティを持つことができます。関数もオブジェクトですから、関数もまたプロパティを持つことができます。では、その関数内部から、その関数オブジェクトが持つプロパティにアクセスする方法は?
var func = function() { println(prop1); }; func.prop1 = "hoge"; func();
現状のTJS2では、このコードは実行時エラーになります。
メンバ "prop1" が見つかりません at test/f2.tjs(3)[(function expression) (anonymous)]
func.prop1
と明示すればアクセスできますが、これは美しくありません。また、this
はglobalを指すので、this.prop1
でもダメです。まあ、これができたからといって、どうということもないのですが。
より高度なクロージャとは?にあったrange関数の改悪版。
Array.range = function(lambda) { if (this.count > 1) { var begin = this[0]; var end = this[1]; var step = (this.count > 2) ? this[2] : 1; if (begin <= end) { step = -step if (step < 0); for (var i = begin; i <= end; i += step) { lambda(i); } } else { step = -step if (step > 0); for (var i = begin; i >= end; i += step) { lambda(i); } } } }; [ 1, 3 ].range(function (n) {println(n);}); [ 5, 1, 2 ].range(function (n) {println(n);});
だから、配列をこういうことに(略)
全然関係ないですが、ラムダ関数で検索するとラムダ関数たん…ハァハァ(内容は真面目です)がトップにくるGoogleは素敵だと思いました。
ネットワーク関係。構想中。こんなイメージで進めてみましょうか。
秋葉原のエロゲ取り扱いをしているソフマップ14号店の店内で、お客さん同士の口論から、ナイフで背後から斬りつけるという傷害事件が発生した。
真偽のほどは不明だが、2chへの書き込みを紹介されているブログ(エントリー下部の【関連リンク】)には、『「キモイ」発言が発端で口論。刺した方は高校2年生らしい』となっているのも見られる。
未成年がエロゲショップ?入店だけなら問題ないか。
斬ったの?刺したの?【秋葉原】 ソフマップ14号店でヲタ同士による傷害事件発生によると、刺傷事件のようですが。
「キモい」「キモくない」で刺されちゃ堪らんな。
昨日、LunaNetworkのバイナリ作成をしていたときから嫌な予感はしていたのですが、今日ソースを追ってみて、予感が当たっていたことがわかりました。これ、複数のソケットを制御できません。クライアントソケット、サーバソケットがそれぞれ一つずつ、ソケットハンドルをクラス変数として持っているためです。LunaNetwork::Client::ConnectServerには接続中の場合は切断
とはっきり書かれていますし。
こいつは手術が必要ですね。それとも、LunaSocket系に戻すか?
面白いことが書かれています。視聴者だけでなく、制作サイドもまた規格に振り回されているようですね。
今のテレビ放送事情って、HDTVだのデジタル放送だの、画質がどうの双方向性がどうの、なんだかもう、視聴者置いてけぼりです。まあ、正直言ってどうでもいいんですけどね。そこそこ綺麗に映ればいいんですよ、テレビなんて。本気で画質云々言ってるのは、一部の映像マニアだけじゃないんですか?
バジリスクと、十月から放映開始予定のリリカルなのはA'sがそこそこ綺麗に映ればそれで良し。画質に疎い俺にとっては、テレビなんてそんなもの。
あ、いや、待て。ゲームの画面は綺麗に映って欲しいな。
クライアントソケットとサーバソケット、それぞれインスタンスを作成できるように、staticの呪縛から解き放ちました。LunaNetwork::Clientクラス中のWSA Overlapped Completion Routine *1 中で、どのソケットなのかを知る方法がわからず(ソケットハンドルを受け取れないっぽい?)、どうしてくれようかと悩みましたが、結局この機能は切り捨てました。代わりに、関連する部分はWSAGetOverlappedResult()関数を使って何とかすることにしました。
とりあえずバイナリは作成できた *2 ので、あとは吉里吉里でこいつをどう扱うか。まあ、ゆっくり考えるとしましょう。
つまり、このコールバック関数は、どのインスタンスが登録したものなのかを、自分で知ることができません。それどころか、どのソケットなのかすらもわかりません。
…これで昨日は悩んだわけですが、今日、HTTPサーバーからファイルを取り寄せるを見て、やっと解法がわかりました。LunaのOVERLAPEDDATA構造体の先頭にWSAOVERLAPPED構造体を配置し、その後ろに自分自身へのポインタを配置します。
struct OVERLAPEDDATA { WSAOVERLAPPED Overlap; LunaClientSocket * Client; // …略 };
WSASend/WSARecvをコールするときは、OVERLAPEDDATA構造体へのポインタを、WSAOVERLAPPEDへのポインタにキャストして渡せばいいだけです。コールバック関数は、受け取ったWSAOVERLAPPEDへのポインタを、OVERLAPEDDATA構造体へのポインタにキャストし直せば、OVERLAPEDDATA構造体経由でインスタンスへのポインタを取得することができます。もちろん、渡したポインタと受け取ったポインタが同じ場所を指している、というのが大前提ですけどね。
ところで、まだ一度も動作確認してないんですが…
Winsock2そのものと、LunaClient(or Server)Socketを管理・制御するための専用クラスを作成しました。管理下にあるソケット全てを一斉にクローズしたり、Winsock2の開始とシャットダウンを管理したり、まあそんな感じのことをします。吉里吉里プラグインとして使用中のソケットを監視制御するのに必要となるでしょう。特に、プラグインのアンリンク時にはしっかり解放してやらないと。
あれ?中華キャノンがないよ。ああそうか。剣を装備して近接格闘戦用になったから要らないのか(違)
ソースコード自動生成スクリプトmakekrnc.plを改良して、引数と戻り値についても記述できるようにしました。今までアレなマクロで書いていた部分が、さらにアレな記述になりました。
TVP_PLUGIN_FUNCCALL_DECL(tHogeFunction) { TP_REQ_NUM_PARAMS(2) TP_GET_REQ_PARAM_INT(x) TP_CHK_PARAM_MUST_OBJ TP_GET_REQ_PARAM_CLO(cond) // 中略 TP_SET_RETURN_VALUE_INT(rv) return TJS_S_OK; }
@function hoge @params 2 @par int x @must obj @par vclo cond // 中略 @return int rv return TJS_S_OK; @;
もはやスクリプトなしではコンパイルすらできませんが、もともとスクリプトに頼りきりだったので、今更依存部分が増えたところで大して変わりません。
libiconvに関する良い資料が見つからず、手こずりました。以下、Borland C++ Compilerで使うときのメモ。
プログラムする上での注意点。
cd == (iconv_t)(-1)
で比較して必ずエラーチェックすること。なぜiconvなんかを弄っているかというと、文字の符号化変換モジュールを作成するためです。このモジュールは、吉里吉里が内部で持っているワイド文字列(16ビット体系かな?)と、符号化されたオクテット列との相互変換を行います。
前々から欲しいと思っていた *1 、というのもありますが、ソケットを使う上でいずれ必要になる、というのが大きいです。例えば、文字列の送受信等で、通信相手が、自分とは異なる特定の符号化方法しかサポートしていない場合、符号化方法を変換してあげる必要がありますから。
…いや、そもそもソケットプラグインではオクテット列の送受信しか対応しない予定 *2 なので、文字列送受信には必須になるかもしれません。
libiconvって、LGPLなんですよね。吉里吉里のプラグインは、できれば吉里吉里独自のライセンスか、BSDライセンスか、あるいは「好きにしてくれ状態」で公開したいと考えているので、気をつけて扱わないとGPL汚染されてしまいます。まあ、動的リンクだから問題ないか。
文字の符号化方法を変換する機構を作成しました。perlみたいに、Encoder.encode(encname, text)
、Encoder.decode(encname, octet)
で変換できます。EUC-JPでもMacArabicでも何でも来やがれ、て感じですかね。
バイナリファイルを読み書きするためのクラスを作成しました。普通にdata = stream.read(SIZE)
とかwritten = stream.write(octet, SIZE)
みたいに扱えます。
こういうのを作っておけば、ソケットのテストが多少は楽になるでしょう。
serial experiments lain TV-BOXが届きました。暇を見つけて鑑賞しましょう。
なんか、バイナリファイルストリームにかかりっきりで、ソケットの作業が全然進んでません。連休中にクライアントくらいは、と思っていたのですが。うーむ。
送信完了時コールバック関数自体は実行されているのですが、内部でキックしているはずのTJS関数が期待通りに動いてくれていない様子。FuncCall()自体は例外も出さず、戻り値も0なのですが、実行結果が反映されていません。うーむ。
3x3メジアンフィルタ。符号なし8ビット、要素数9の配列から、メジアンを取得するロジックが浮かんだので、実装してみました。今まではクイックソートで並べてから真ん中の要素を抽出していましたが、これを基数ソート+バブルソート+クイックソートの変態複合型(?)に変えました。真面目にソートしないのがポイントです。
他の方法と比較したところ、当社(独自実装のクイックソート)比で約二倍、C標準のqsort関数の約四倍の速度になりました。ただ、これは値が平均的にばらついていた場合の話で、配列中の要素の値が極端に偏っている(エントロピーが小さい)場合は却って遅くなります。メジアンフィルタに適用した場合、ベタ塗りのアニメ調の画像では重くなりやすい、ということです。
あまりにアレなカラーイコライゼーションの実装を改善しました。
あと、イメージバッファ比較関数を書きました。これを使えば、リグレッションテストが多少は楽になるかな。