Objective-C で AUTOLOAD (あるいは method_missing )
forwardInvocation
とかを使えば出来るみたい。
#import <Foundation/Foundation.h>
@interface Foo : NSObject;
-(void)call:(NSString*)sel;
@end
@implementation Foo
-(void)call:(NSString*)sel {
NSLog(@"call: %@", sel);
}
-(void)forwardInvocation:(NSInvocation *)invocation {
[self call:NSStringFromSelector([invocation selector])];
}
-(NSMethodSignature*)methodSignatureForSelector:(SEL)sel {
NSMethodSignature* sig = [super methodSignatureForSelector:sel];
if (sig) return sig;
return [[self class] instanceMethodSignatureForSelector:@selector(call:)];
}
@end
int main() {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
Foo* foo = [[Foo alloc] init];
[foo bar];
[foo buzz];
[foo release];
[pool drain];
return 0;
}
// gcc -framework Foundation foo.m
詳細は Objective-C Runtime Programming Guide にある。
libuv の Perl バインディング
気分転換プロジェクトとして、libuvのPerlバインディングを書いている。
現状、timerとtcp周りを一通り実装したところ。
実装は愚直にlibuvの関数とperlの関数を1:1に(uv_tcp_init(...)
をUV::tcp_init(...)
というような形で)マッピングしている。 この実装はlow level APIと位置づけ、その上に使いやすい高レベルなインタフェースをPerl上で用意したいつもり。
このlow level APIをつかったTCP echo serverはこんな感じになる:
use strict;
use warnings;
use UV;
my $server = UV::tcp_init();
UV::tcp_bind($server, '0.0.0.0', 3000)
&& die 'bind error: ', UV::strerror(UV::last_error());
UV::listen($server, 10, sub {
my $client = UV::tcp_init();
UV::accept($server, $client) && die 'accept failed: ', UV::strerror(UV::last_error());
UV::read_start($client, sub {
my ($nread, $buf) = @_;
if ($nread < 0) {
my $err = UV::last_error();
if ($err != UV::EOF) {
warn 'client read error: ', UV::strerror($err);
}
UV::close($client);
}
elsif ($nread == 0) {
# nothing to read
}
else {
UV::write($client, $buf, sub {
my ($status) = @_;
if ($status) {
warn 'client write error: ', UV::strerror(UV::last_error());
UV::close($client);
}
});
}
});
}) && die 'listen error: ', UV::strerror(UV::last_error());
UV::run();
見てわかるようにperlモジュールとしての使いかってはわるいが、libuvを使い慣れた人なら迷いなく使うことが出来るようになっている。 これは僕にとっては発見だった。
また、このサンプルをみてもわかるようにlisten
とかaccept
とかまでラップしている関係上、libuvをAnyEventのバックエンドとしてつかうのは現状は無理そう。
libuv自体はWindowsでも動くはずだが、メインマシンをAirにして以来Windows環境を仮想環境でも持ち歩いていないため確認できていない。 Windows対応してくれる人募集!
開発を始めるためのステップ:
$ cpanm Module::Install
$ cpanm Module::Install::XSUtil
$ git clone git://github.com/typester/p5-UV.git
$ cd p5-UV
$ git submodule update --init
$ perl Makefile.PL
$ make
$ make test
Growl 1.3.1 と Skype
ひさしぶりにMacを再起動したらSkypeがGrowlを認識しなくなった。
そういえばGrowlを1.3.1にしてから再起動していなかった。原因はそれだろうとSupport Forumをのぞいてみると同様の問題を持った人がいっぱいいて、そこに解決法も書かれていた。
Skypeに内蔵されてるGrowl.frameworkが古いからそれをアップデートしてやればOKということらしい。
フレームワークのアップデートは自分でファイルを置き換えても良いが、Growl公式サイトでそれ用のツール、Growl Version Detectiveが配布されているのでそれを使用すると良い。
アプリはこんな感じで、
Skypeを選択してUpdate FWを押すだけで入れ替わる。
オレオレバッファ
C でなんか書くときに、 lighttpd の buffer.c をコピペ(&若干改変)したのをずっと使いまわしてきたけど、コピペして使い回すのがめんどくなってきたので submodule として使えるように github にアップした。
上げるついでにテスト書いたけど、全部テスト書くのが面倒だったので、使ってなかった関数郡はごっそり削除。 必要になったら追加する方針に。
こういうのってどっかに定番でみんな使うようなのあるんかなー。
ngx-queue.h
libuv のソースを見ていたら、ngx_queue_*
という API が出てきてびっくり。どうやら nginx から ngx-queue.h
っていうリンクドリストの実装を持ってきているようだ。
include/uv-private/ngx-queue.h at master from joyent/libuv - GitHub
なかなかおもしろい。これ、いろんなところで使えそうなので手元でも試してみた。
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include "ngx-queue.h"
typedef struct {
ngx_queue_t queue;
char* data;
} chunk_t;
chunk_t* chunk_init(const char* data, size_t len) {
chunk_t* c = malloc(sizeof(chunk_t));
assert(c);
ngx_queue_init(&c->queue);
c->data = malloc(len + 1);
assert(c->data);
memcpy(c->data, data, len);
c->data[len] = '\0';
return c;
}
void chunk_delete(chunk_t* c) {
ngx_queue_remove(&c->queue);
free(c->data);
free(c);
}
int main(int argc, char** argv) {
ngx_queue_t queue;
chunk_t* chunk;
ngx_queue_init(&queue);
chunk = chunk_init("foo", 3);
ngx_queue_insert_tail(&queue, &chunk->queue);
chunk = chunk_init("bar", 3);
ngx_queue_insert_tail(&queue, &chunk->queue);
chunk = chunk_init("buz", 3);
ngx_queue_insert_tail(&queue, &chunk->queue);
while (!ngx_queue_empty(&queue)) {
ngx_queue_t* q = ngx_queue_head(&queue);
chunk_t* c = ngx_queue_data(q, chunk_t, queue);
printf("data: %s\n", c->data);
chunk_delete(c);
}
chunk = chunk_init("foo", 3);
ngx_queue_insert_tail(&queue, &chunk->queue);
chunk = chunk_init("bar", 3);
ngx_queue_insert_tail(&queue, &chunk->queue);
chunk = chunk_init("buz", 3);
ngx_queue_insert_tail(&queue, &chunk->queue);
while (!ngx_queue_empty(&queue)) {
ngx_queue_t* q = ngx_queue_last(&queue);
chunk_t* c = ngx_queue_data(q, chunk_t, queue);
printf("data: %s\n", c->data);
chunk_delete(c);
}
return 0;
}
出力は
$ ./a.out
data: foo
data: bar
data: buz
data: buz
data: bar
data: foo
これは使えるなぁ。
テラスモール湘南に行ってきた
11月11日に辻堂駅前にオープンしたばかりの湘南地区最大級のショッピングモール、テラスモール湘南に行ってみた。
我らが鎌倉bowlsも出店しているからその様子見もかねて。
テラスモール湘南のデリカテッセンのお店。いつもの家庭の食卓にサプライズと笑顔をお届け!
個人的には、109シネマズ(もちろんIMAXシアター付き)が入っているというのが最大のポイント。いままでは川崎まで見に行っていたからね。
ただ、懸念点は交通事情。あのあたりってそんなに道も大きくないような気がしたし、テラスモール湘南自体は2500台収容の駐車場があるらしいけど周りの交通が麻痺してしまっていたら意味がない。
実際に行ってみるとそんなに心配することもなかった。到着したのはたぶん1番混んでると思われる14時台だったけど、テラスモール湘南の少し手前から駐車場に入る列が出来ている程度の渋滞しかなかった。 ただ駐車場は屋上以外はほとんど満車状態だったから、雨の日など屋根付きのところへ駐車したいという場合には早めか遅めの時間帯に行くようにした方がいいだろう。
鎌倉から向かうには海沿いをずっと行って浜見山交番前を右折、あとはずっとまっすぐ行くとテラスモールの真ん前に出るから1番楽だと思う。帰りはそのルートは混むので模索中…。
今回は映画を見るわけでもなく施設内をぶらぶらしただけだったけど、なかなか好印象。湘南地区の人たちは横浜や都内に出る機会がだいぶ減るんじゃないかな。ここで事足りるっていう意味で。 次回は映画を見に行こうと思う。
libuv を iOS 対応した件
だいぶ前から iPhone アプリ作成でネットワーク系の機能を作るときには libev を愛用してるのだが、今日 node をいじっていたら libuv がなかなかよさそうに感じた。
libuv は libev が Windows で動かないからっていう理由で始まったプロジェクトだとおもうけど、Linux などの環境においても libev をラップしつつ、より便利な機能が追加されていていわば C でネットワークプログラミングするためのフレームワークといえるくらいになってる、みたい。(まだ詳しく見たわけじゃないけど)
libev は基本的に io 監視と timer のみで、socket の生成などは基本的に自分で syscall 呼んでつくる必要があるけど、libuv は uv_tcp_*
とか uv_udp_*
といったAPI郡をもってて便利そう。 あと、非同期 DNS ルックアップがサポートされてるのはうれしい。これ、 libev になくて不便だった。
そういうとこまでラップしないと同じコードを Windows でも動かすってのは無理だからそうなったんだとおもうけど、結果として大変便利なライブラリになっているのではないか。
あと、テストケースが充実しているので、(たぶん)すべての機能のサンプルコードがテストを見ればOKっていうのもうれしい。
っていうわけで iOS 対応のパッチを書きました。
#243: Added experimental iOS support by typester for joyent/libuv - Pull Request - GitHub
iOS で動かない理由はハードウェア時間をnano秒で取得してる関数が iOS にはない CoreServices.framework に依存しちゃってるからってことだけなので、それを使わないように修正をした。
現状のパッチは iOS 用にビルドするときのみ CoreServices 非依存のコードを使うようにしているけど、これに依存してることで uv.a を組み込むだけじゃなくて、 CoreServices.framework に別途リンクする必要があってめんどいからパフォーマンスや精度が問題なければ非依存のコードの方に統一してしまった方がいいと思う。っていうようなことは pull req に返信が来たら伝えようとは思っている。
とりあえず、これで使えるようになったから。さっそく今作ってるアプリに組み込んでにようと思う。
node.js を iPhone アプリから動かす
結論から言うと、 jailbreak してないと動きません。詳細はこの辺:
Issue 1312 - v8 - It's time to get iOS supported! - V8 JavaScript Engine - Google Project Hosting
というわけでターゲットがデベロッパー用に絞られてしまいますがやってみましょう!
Node ダウンロード
今回はリリースされたばかりの 0.6.0 を使いました。
からダウンロードして展開しましょう
libuv にパッチを当てる
libev はなにもしないでもそのまま iOS で動きますが、 libuv はそうではないようです。
$ cd deps/uv
$ wget --no-check-certificate https://raw.github.com/gist/1354552/cfb4e9a544185bdbda1a8374aaf1cd5cc812c070/libuv-ios.patch
$ patch -p1 < libuv-ios.patch
なお、このパッチは本家に pull req 送っておきました。取り込まれると良いですね。
V8 にパッチを当てる
V8 自体は ARM に対応しているようですが、iOS SDK でクロスコンパイルしようとすると
ARM EABI support is required.
とか言われてしまいます。iPhone の ARM が EABI をサポートしてないためでしょうか。
ただ、最初にリンクした Goole Code の issue 内で iOS 対応のパッチを投稿している人がいて、そのパッチを当てることでビルド可能になる模様。
$ cd deps/v8/src
$ patch -p0 < ~/Downloads/v8-ios.patch
Node にパッチを当てる
プロセスリストにでるプロセス名を設定する機能が Carbon をつかっているので関数殺しちゃいます。iOS でプロセス名かえられても誰得ですよね。具体的には
process.title = "hoge";
が動かなくなります。
$ wget --no-check-certificate https://raw.github.com/gist/1354570/0c28584c07f0410c5e5608d4e2e9ea68e6d5dbc4/node-ios.patch
$ patch -p1 < node-ios.patch
ビルド
まずもろもろ環境変数をセットし:
export CC=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/gcc
export CFLAGS="-arch armv7 -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk -I$HOME/dev/iphone/lib/openssl/include"
export CXX=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/g++
export CXXFLAGS=$CFLAGS
export CPP=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/cpp
export AR=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/ar
export LINKFLAGS="-arch armv7 -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk -L$HOME/dev/iphone/lib/openssl/lib"
ビルド:
./tools/waf-light configure build --product-type=cstaticlib --dest-cpu=arm --without-snapshot
ひっそり、前もってビルド済みの openssl を参照していますが、ない場合は --without-ssl
を追加すればいけるはずです。
成功すると
./out/Release/deps/uv/uv.a
./out/Release/libnode.a
./out/Release/libv8.a
./out/Release/libv8preparser.a
といった静的ライブラリが出来てると思うんで、これらをアプリに埋め込んであげればいいわけですね。
armv6, armv7, i386 用それぞれビルドし、lipo
でユニバーサルバイナリに仕上げるのが iOS 用静的ライブラリ作成のセオリーですが、ここではめんどいので省略します。
アプリ
だいぶ適当ですが、サンプル作ってみました。
lib ディレクトリにさっきビルドした .a ファイルを突っ込んであげる必要があります。 また node.h
の参照元を nave でインストールした node のパスを設定してあるので、違う環境の人は Header Search Path を環境に合わせて書き換える必要もあります。
こんな感じの画面が出るんで、適当にコード書いて Run おせば動かすことが出来ます。
注意
コードみてもらえばわかりますがだいぶ適当につくっておりまして、node の標準出力などをうけとっておりません。また、二回目実行しようとするとアプリが落ちますw
直していただける方お待ちしております!
nginx で lighttpd のようにユーザーをトラッキングする方法
lighttpd では mod_fastcgi や mod_proxy 経由でアプリケーションが、
X-Lighttpd-Hogehoge: foobar
のような X-Lighttpd-
ではじまるヘッダーを返してもそれをクライアントに送り返さないという仕組みがあり、 たとえばそれを利用してアプリからユーザーIDを返してあげたりすると、それをクライアントに送ることなく lighttpd のアクセスログにだけ記録する、といったようなことが出来て便利なのですが、 同じようなことを nginx でやりたかったのでしらべてみた。
アプリから
X-MyApp-User: foobar
みたいなのを返してそれをクライアントに送ることなくアクセスログに記録したい場合、まずクライアントに送らないように、
proxy_hide_header X-MyApp-User;
とし、さらに accesslog のフォーマット設定 log_format
に
$upstream_http_x_myapp_user
を追加することで記録が行えました。まる。
追記@2011-11-08T16:22:26+09:00
だったので修正。
iPhone4S とか iOS5 とか Xcode 4.2 とか
iPhone4S は予約しようと思ってたけど、予約開始日がちょうど F1 日本 GP とかぶっていたので初日予約はあきらめ、暇なときに予約しに行こうと思っていて今に至る…。
iOS5 は開発者用のものを前から使っていたけど、通知で邪魔されないようになったのがすごい大きい。 いままではカーナビとして使っている最中に Push 通知が来てしまうと通知を消すまでカーナビとしての役割をはたさないようになってしまっていたからね。
で、こういうApple製品リリースの中で一番ライフチェインジングだったのが Xcode 4.2! ARC (Automatic Reference Counting) と Storyboard が便利すぎて iOS アプリの開発速度いままでの何倍にもなります。ただ Storyboard つかっちゃうと iOS5 以降限定になっちゃうってのが…。
ARC も zeroing weak 変数(破棄されたらnilになってるって保証されてるweak変数)使おうとすると iOS5 限定になっちゃうみたいだし。 zeroing ってなんて訳せば良いんだろ。
そんなこんなで、次のアプリから iOS5 のみで良いかなとか考えてる最中。