にゃははー

はへらー

bjam AdC jp 2011 9日目

疲れてます。

ちなみに 所謂オブジェクト指向言語はオブジェクト指向してないんじゃないかというなにか - Flast?なにそれ、おいしいの? の内容は忘れて読んでください。忘れろ。

クラス

bjamはクラスをもってます。OOPです!お姉ちゃんにパンツの色聞きましょう!

bjamのクラス構造は大まかに以下のとおりです。

class class-name : base-class
{
	# constructor
	rule __init__ ( args )
	{
		base-class.__init__ $(args) ; # call base class ctor
		# data member
		self.member = $(args) ;
	}

	# member rule
	rule member ( args )
	{
		...
	}
}

まぁコメント書いてあるんで大体分かってもらえると思います。

コンストラクタは__init__という名前で定義されたruleです。また、基底クラスのコンストラクタは自動で呼ばれないので手動で呼ぶ必要があります。

データメンバはpythonみたいに好き勝手追加できます。確か self.なんたら じゃないといけなかったと思います。ちょっとあやふやです。メンバruleは通常のruleの定義のように書けます。

あとはよくわかりません。というのも、このエントリを書くためにさっき初めてまともに使ったからです!!!

Instantiated from, Instantiated from, ...

クラスだけあっても仕方ないのでインスタンスを作りましょう。インスタンス化はclassモジュールのnew ruleで行ないます。
が、ここで落とし穴があります。多分最初は何言われてるのかわからないと思います。

classモジュールをインポートするために以下のように書いたとします。

import class ;

そうするとbjamは

$ bjam -f hoge.jam
hoge.jam:1: syntax error at keyword class
don't know how to make all
...found 1 target...
...can't find 1 target...

と怒ってきます。

これはclassがキーワードとしてパースされてしまうからです。というのもimportはキーワードではなくruleだからです。
まったくbjamは意味わからん理解不能な言語ですね。なんなんだこれは。

とりあえずどうしたらいいかですがimport部分を

import "class" ;

とすればよいです。

classモジュールのnew ruleの第1引数にクラス名、引数を続けて列挙すればインスタンスが戻り値として返されます。
new ruleを呼ぶときのclassは"class"ではなくclassです。また落とし穴ですね。ややこしいことこの上ないこの言語いったいなんなんだ。

import "class" ;
class hoge
{
	rule __init__ ( arg1 : arg2 : arg3 ) ...
	rule member( arg1 : arg2 : arg3 ) ...
	...
}

inst = [ class.new hoge arg1 : arg2 : arg3 ] ;

メンバruleを呼び出す必要がある場合は、先の例だと

$(inst).member arg1 : arg2 : arg3 ;

でおkです。直接

[ class.new hoge arg1 : arg2 : arg3 ].member arg1 : arg2 : arg3 ;

とはできないです。一回変数に代入する必要があります。
多分やり方はあると思うんですけど、、、なにしろさっき言ったとおり今日初めてまともに使ったのでよくわからないです。

一昨日のtestingで作ったnamed-なんたらを抽象化する

昨日のnamed-なんちゃらを再掲しときます。決して稼いでるわけではないです。

rule named-compile ( name : sources + : requirements * )
{
	compile $(sources) : $(requirements) : $(name) ;
	explicit $(name) ;
}
rule named-compile-fail ( name : sources + : requirements * )
{
	compile-fail $(sources) : $(requirements) : $(name) ;
	explicit $(name) ;
}
rule named-link ( name : sources + : requirements * )
{
	link $(sources) : $(requirements) : $(name) ;
	explicit $(name) ;
}
rule named-link-fail ( name : sources + : requirements * )
{
	link-fail $(sources) : $(requirements) : $(name) ;
	explicit $(name) ;
}
rule named-run ( name : sources + : requirements * )
{
	run $(sources) : : : $(requirements) : $(name) ;
	explicit $(name) ;
}
rule named-run-fail ( name : sources + : requirements * )
{
	run-fail $(sources) : : : $(requirements) : $(name) ;
	explicit $(name) ;
}

rule test ( rule : successes * : fails * )
{
	for local s in $(successes) { named-$(rule)      $(s) : $(s).cpp ; }
	for local f in $(fails)     { named-$(rule)-fail $(s) : $(s).cpp ; }
}

長さはそれほどですが冗長ですね...

classを使って書き直してみよう。

class explicit-test
{
    rule __init__ ( rule )
    {
        self.rule = $(rule) ;
    }

    rule extest-impl ( module rule : name : * )
    {
        switch $(self.rule)
        {
          case run
            : $(rule) $(3) : $(4) : $(5) : $(6) : $(name)
            ;
          case *
            : $(rule) $(3) : $(4) : $(name)
            ;
        }
        module $(module)
        {
            explicit $(name) ;
        }
    }

    rule extest ( * )
    {
        local m = [ CALLER_MODULE ] ;
        extest-impl $(m) $(self.rule) : $(1) : $(2) : $(3) : $(4) : $(5) ;
    }
    rule extest-fail ( * )
    {
        local m = [ CALLER_MODULE ] ;
        extest-impl $(m) $(self.rule)-fail : $(1) : $(2) : $(3) : $(4) : $(5) ;
    }
}

rule test ( rule : successes * : fails * )
{    
    local nrule = [ new explicit-test $(rule) ] ;
    for local s in $(successes) { $(nrule).extest      $(s) : $(s).cpp ; }
    for local f in $(fails)     { $(nrule).extest-fail $(f) : $(f).cpp ; }
}

explicit ruleはモジュールの関係上呼び出し側モジュールに依存してしまいす。そのためbuilt-in関数のCALLER_MODULEを使ってcaller側のモジュールを取得しています。
また、run/run-fail ruleとcompile/compile-fail/link/link-fail ruleとでは引数の数が異なるのでswitch文で引数の個数を調整しています。bjamはC++と違い実行時に引数の個数等を検査するのでこういったことができます。

まとめ

おねーちゃんにパンツの色聞き忘れました...