bless($args, $class) is a bad practice

さいきん散見される以下のようなコードは悪いコードです。

# usage: Foo->new({ age => 42 })
sub new {
  my($class, $args) = @_;
  return bless $args, $class;
}

それは、ハッシュリファレンスと想定される$argsのそのままつかっているため、引数としてハッシュリテラル以外の何かを渡すと予期しない動作を引き起こすという点です。

たとえば、以下のような使い方は問題を引き起こします。

my %args = (age => 20);
my $o1 = Foo->new(\%args);
my $o2 = Foo->new(\%args);

# ここで $o1 と $o2 は同じオブジェクトになってしまう

このような場合、$argsはコピーすべきです*1。またついでにハッシュリファレンスではなくkey-value pairsを受け取れるようにしておくと無難です。

sub new {
  my $class = shift;
  my %args  = (@_ == 1 ? %{$_[0]} : @_);
  return bless \%args, $class;
}

ただし、newに関するベストプラクティスは、自分でnewを書かずにクラスビルダーを使うというものです。上記のコピーする版でも引数の仕様がまったくわかりません。MouseやClass::Accessor::Liteなどのクラスビルダーを使うと引数の仕様を(ある程度)コードで記述できるので特に理由がない限りはクラスビルダーを使うべきです。

*1:浅いコピーか深いコピーかは場合によるでしょう

JSXからemscriptenで生成したJSを呼ぶ #upcamp

#upcamp でハッカソンしてます。JSXからemscriptenで生成したJSを呼び出してみました。emscriptenはhomebrewでbrew install llvm --with-clangllvmを入れ、emccのshebangを修正するだけで動きました。

コンパイル は EXPORTED_FUNCTIONS を指定すること以外は特になにもしていません。
Cのコードは以下のとおりです。

double add(double a, double b) {
    return a + b;
}

JSXのコードは以下のようになります。

native class C {
  static function ccall.<T>(name : string, returnType : string, argsType : string[], args : variant[]) : T;
} = "require('./add.js')";

class _Main {
  static function main(args : string[]) : void {
    var r = C.ccall.<number>("add", "number", ["number", "number"], [10, 20] : variant[]);
    log r; # 30
  }
}

// vim: set tabstop=2 shiftwidth=2 expandtab:

ccall()はemscripten runtimeのAPIで、シグネチャを与えてCの関数を呼び出します。以外と簡単にできました。

全コードはgithubにあります。

https://github.com/gfx/jsx-call-emscripten-example

emcc-jsx の proof-of-concept #upcamp

emscriptenでJSにコンパイルできるものの、JSからのインターフェイスは使いやすいとは言いがたいし、ましてやJSXから呼び出すとなると本来型のあるCの関数を呼び出すのにccall()経由では静的型チェックの恩恵をうけられない。そこでJSXのラッパを自動生成することで、emccでコンパイルするだけでJSXのモジュールとして使えるようにできないだろうかと考えた。

簡単な emcc-jsx を書いてみたところ、以下のCコードからJSXのラッパを生成することができた。これならJSXからCコードを利用することも比較的簡単にできる*1

#include <stdio.h>

double add(double a, double b) {
    return a + b;
}

int addInt(int a, int b) {
    return a + b;
}

void myPuts(const char* s) {
    puts(s);
}

JSX:

// emscripten JS API
native class _C {
  static function ccall(name : string, returnType : string, argsType : string[], args : variant) : variant;
} = "require('./add.js')";

// from C file
class EMS {
  static function add (a : number, b : number) : number {
    return _C.ccall("add", "number", ["number","number"], [a,b]) as number;
  }
  static function addInt (a : number, b : number) : number {
    return _C.ccall("addInt", "number", ["number","number"], [a,b]) as number;
  }
  static function myPuts (s : string) : void {
    _C.ccall("myPuts", "void", ["string"], [s]);
  }
}

ただ、いまのところemscriptenのコード生成器はプラガブルではないので、emscripten/src/jsifier.js に直接JSXコード生成器を埋め込むことになり、利用しやすいとは言いがたい。emscriptenのコードジェネレータをプラガブルにすれば、emcc-jsxも現実的になるかもしれない。

*1:ただし構造体を利用するのは難しい