2013年5月10日

js-ctypes で Objective-C を使う (2)

そういえば Proxy なんてものもあったねー、という事で前回のサンプルを Proxy を使ってもうちょっとイイカンジに書き直す。

"use strict"

let Cu = Components.utils;

Cu.import ("resource://gre/modules/ctypes.jsm");

/* なんかイイカンジにアレしてくれるアレ */
let objc = {
  /* はじめる */
  init : function () {
    this.lib = ctypes.open (ctypes.libraryName ("objc"));

    this.objc_getClass = this.lib.declare ("objc_getClass",
                                           ctypes.default_abi,
                                           ctypes.voidptr_t,
                                           ctypes.char.ptr);
    this.sel_registerName = this.lib.declare ("sel_registerName",
                                              ctypes.default_abi,
                                              ctypes.voidptr_t,
                                              ctypes.char.ptr);
    this.objc_msgSend = this.lib.declare ("objc_msgSend",
                                          ctypes.default_abi,
                                          ctypes.voidptr_t,
                                          ctypes.voidptr_t,
                                          ctypes.voidptr_t,
                                          "...");
  },
  
  /* おわる */
  release : function () {
    this.lib.close ();
  },
  
  /* メッセージを送る */
  send : function (listener, sel, args) {
    return this.objc_msgSend.apply (undefined,
                                    [listener, this.sel_registerName (sel)]
                                    .concat (args));
  },
  
  /* NSString を作る */
  str : function (str) {
    return this.classes.NSString.alloc ()
    .initWithUTF8String (ctypes.char.array ()(str))
    .autorelease ().VALUE;
  },
  
  /* 名前に対応するクラスを返すオプジェクト的なアレ */
  classes : new Proxy ({}, {
      get : function (_, name) {
        return new Proxy (objc.objc_getClass (name), objc.proxy);
      }
    }),
  
  /* メソッド呼び出しみたいにするプロキシ */
  proxy : {
    get : function (obj, sel) {
      /* 中身を取り出すための特殊プロパティ */
      if (sel == "VALUE") {
        return obj;
      }
      if (sel == "VALUE_AS") {
        return function (type) {
          return ctypes.cast (obj, type).value;
        };
      }
    
      /* 引数に応じてメッセージを送ってくれる関数 */
      return function () {
        let args = Array.prototype.slice.call (arguments, 0);
        if (args.length > 0) {
          sel += ":";
        }
        let ret = objc.send (obj, sel, args);
        return new Proxy (ret, objc.proxy);
      };
    }
  }
};

/* ---- ここから本体 ---- */

/* はじめる */
objc.init ();

/* プールを作る */
let pool = objc.classes.NSAutoreleasePool.alloc ().init ();

/* スピーチシンセサイザを作る */
let voice = "com.apple.speech.synthesis.voice.kyoko.premium";
let synth = objc.classes.NSSpeechSynthesizer.alloc ()
  .initWithVoice (objc.str (voice)).autorelease ();

/* 喋っていただく */
synth.startSpeakingString (objc.str ("a"));

/* 喋り終わるのを待つ */
while (synth.isSpeaking ().VALUE_AS (ctypes.int32_t)) {
}

/* プールを開放 */
pool.release ();

/* おわる */
objc.release ();

メソッドの呼び出しっぽく書けるし、クラス取得もプロパティっぽく取れるし、文字列で書く部分が無くなってかなり自然になった気がする。

ただ Proxy から中身を取り出す VALUE と VALUE_AS がイマイチ。
メッセージと衝突する可能性もあるし。



あとから気付いたけど、この方法だと 2 引数以上のセレクタ書けないジャーン

0 件のコメント:

コメントを投稿