暖かくて通学中の自転車の上で眠ってしまいそうな今日この頃ですが、昨日のエントリで凄いミスを犯していたので訂正します。
というのも、昨日のバイトコード、引数をスタックに逆順で積まなければならない所を、見事にそのまま積んでいたので、コンストラクタには引数が全てひっくり返って渡されてしまっていました。正しいスクリプトはこちらになります。
__bytecode__("8E2E00637265617465496E7374616E636546726F6D537472696E67000200062A0001706174680002706172616D7300570096020004011C8701000317960A000402006C656E677468004e870100041796020004048701000517960200040551870100059605000700000000489d02000d00960400040204054e990200dbff9605000404040303533e");
見やすくするとこう。
function2 createInstanceFromString (r:1='path', r:2='params') ()
push r:path
getVariable
setRegister r:3
pop
push r:params, 'length'
getMember
setRegister r:4
pop
push r:4
setRegister r:5
pop
label1:
push r:5
decrement
setRegister r:5
push 0
lessThan
branchIfTrue label2
push r:params, r:5
getMember
branch label1
label2:
push r:4, r:3, UNDEF
newMethod
return
end // of function createInstanceFromString
うっかりしてましたスイマセン。
何かアクセスが増えてると思ったら、fladdict.netさんからトラックバックが来てた。スクリプトエンジンで(一部の)ビルトインクラスのnewが上手くいかないので汎用ファクトリを作ろうという話です。
何気なしに考えてたら、(あまり美しくは無いですが)バイトコード直書き(Cのインラインアセンブラみたいなの)で実現できることに気付いたので書いておきます。
まず、次のコードを1フレーム目とかに書いておきます。長いですが、区切ったり改行入れたりしないで下さい。
__bytecode__("8E2E00637265617465496E7374616E636546726F6D537472696E67000200062A0001706174680002706172616D73005B0096020004011C8701000317960A000402006C656E677468004e8701000417960500070000000087010005179604000405040448129d02001800960400040204054e9602000405508701000517990200daff9605000404040303533e");
これを書くと、createInstanceFromString()というメソッドが定義されます。ので、次のようにして使います。
import flash.display.BitmapData;
var b:BitmapData = createInstanceFromString("flash.display.BitmapData", [10, 10]);
第1引数にクラスのコンストラクタへのパスを文字列で、第2引数にコンストラクタに渡す引数を配列にして呼び出します(要素は何個でも大丈夫です)。すると、オブジェクトが生成されて返ってきます。以上。
実際どんなことをやっているか、flasmで見やすくしたバイトコードを載せておきます。
function2 createInstanceFromString (r:1='path', r:2='params') ()
push r:path
getVariable
setRegister r:3
pop
push r:params, 'length'
getMember
setRegister r:4
pop
push 0
setRegister r:5
pop
label1:
push r:5, r:4
lessThan
not
branchIfTrue label2
push r:params, r:5
getMember
push r:5
increment
setRegister r:5
pop
branch label1
label2:
push r:4, r:3, UNDEF
newMethod
return
end // of function createInstanceFromString
要は配列の要素を push してスタックに積んで、 newMethod を呼んでるだけです。
追記
上のコードバグってました。ストリングからクラスのインスタンスを作る 訂正をご覧下さい。
4月1日ですが、ActionScript3で、Namespaceを使ってオーバーロードをスマートに実現する方法を思いついたので書きます。
今回、ちょっとコードが長いので、FlexBuilder2 Beta2用のプロジェクトをそのまま固めてアップしました。ダウンロードはこちら。これを見ながら読み進めてください。
このプロジェクトには、org.be_interactive.overloadパッケージと、サンプル用のOverload.asが含まれます。
org.be_interactive.overloadパッケージには、
- OverloadExtensionクラス
- 4つのネームスペース
が定義されています。
とりあえず使い方ですが、まず
import org.be_interactive.overload.OverloadExtension; import org.be_interactive.overload.overload_String; import org.be_interactive.overload.overload_Int;
クラスと必要なネームスペースを読み込んで、
class Hoge
{
import flash.util.trace;
overload_String function fuga (str:String) : void
{
trace('String', str);
}
overload_Int function fuga (n:int) : void
{
trace('Int', n);
}
}
引数の型に合わせてネームスペースを変え、同じ関数名でオーバーロード用のメソッドを定義します。そして、
public function fuga (...args) : void
{
OverloadExtension.execute(this, 'fuga', args);
}
実際に呼び出されるpublicメソッドを作り、 OverloadExtension.execute(ターゲット, 呼び出すメソッド名, 引数配列) で呼び出します。これで準備完了。なんとこれだけで、オーバーロードが可能になります。
var h:Hoge = new Hoge();
h.fuga('hello, world'); // 出力:String hello, world
h.fuga(1); // 出力:int 1
但し、デフォルトでは 引数なし/String/Int/String+Int のオーバーロードしかサポートされていないので、これを拡張する方法を紹介します。
まず、オーバーロード用の適切なネームスペースを用意します。ネームスペース名は引数の型を並べるのがいいかと思いますが、実際は何でも構いません。
public namespace overload_String_String = 'http://www.be-interactive.org/OverloadExtension/String/String';
次に、OverloadExtensionクラスにネームスペースを登録します。これは初期化段階で1度呼び出せばOKです(例えばDocumentRootのコンストラクタ)。
OverloadExtension.register(overload_String_String, String, String); // 引数は、(ネームスペースへの参照, Class, Class, ...)
最後に、先ほどと同じようにメソッドを定義すれば完了です。
overload_String_String function fuga (str:String, str2:String) : void
{
trace('StringString', str, str2);
}
デフォルトでは、初期化段階(OverloadExtension#initializer)で次のように4つ登録されています。ここは自由に変更してもらって構いません。
register(overload_default); register(overload_String, String); register(overload_Int, int); register(overload_String_Int, String, int);
OverloadExtensionの実装の説明は省きます。興味がある人はコードを読んでくださいな。ちなみに、このライブラリは自由に使用/改変してもらって構いませんが、できればパッケージは変えないでください。



