【Cocos2d-x】Script Bindingの作り方（その2）

その1の続きです
[【Cocos2d-x】Script Bindingの作り方（その1）](http://qiita.com/tkyaji/items/da858fd859b807dd922b)

それではbindings-generatorの実装方法を説明します。

#Lang言語用のファイルを作る
まずはCococ2d-xのtoolsディレクトリ配下で、Lang言語用のディレクトリを作成します。
一から作るのは大変なので、Luaのディレクトリをコピーすることにします。

```
cp -r tools/tolua tools/lang
cp -r tools/bindings-generator/targets/lua tools/bindings-generator/targets/lang
```

まずは*tools/lang*ディレクトリのファイルを見てみましょう。
*genbindings.py*というPythonスクリプトと、"cocos2dx"で始まるiniファイルがいくつかあるかと思います。

*genbindings.py*を開き、try〜except句の以下の部分を書き換えます。

* tolua / luaとなっているのをlangに変更
* cmd_argsの中身を一旦"cocos2d-x.ini"のみにする


```py
try:

    lang_root = '%s/tools/lang' % project_root
    output_dir = '%s/cocos/scripting/lang-bindings/auto' % project_root

    cmd_args = {'cocos2dx.ini' : ('cocos2d-x', 'lang_cocos2dx_auto'), \
                }
    target = 'lang'
    generator_py = '%s/generator.py' % cxx_generator_root
    for key in cmd_args.keys():
        args = cmd_args[key]
        cfg = '%s/%s' % (lang_root, key)
        print 'Generating bindings for %s...' % (key[:-4])
        command = '%s %s %s -s %s -t %s -o %s -n %s' % (python_bin, generator_py, cfg, args[0], target, output_dir, args[1])
        _run_cmd(command)

    if platform == 'win32':
        with _pushd(output_dir):
            _run_cmd('dos2unix *')

    print '---------------------------------'
    print 'Generating lang bindings succeeds.'
    print '---------------------------------'

except Exception as e:
    if e.__class__.__name__ == 'CmdError':
        print '---------------------------------'
        print 'Generating lang bindings fails.'
        print '---------------------------------'
        sys.exit(1)
    else:
        raise
```


こんな感じになります。

cmd_argsには生成する対象のソースとその設定ファイルを記述します。
最初から全てに対応するのは難しいので、まずは一番基本となる"cocos2d-x.ini"だけに限定しています。
"cocos2d-x.ini"が完璧になったところで、対応ファイルを1つずつ増やしていくと良いと思います。

次に、"cocos2d-x.ini"の中身を見てみましょう。
色々と設定がありますが、書き換えることがありそうな項目は以下のあたりだと思います。

* headers
>解析する対象のC++のヘッダ。指定したヘッダファイルを起点にincludeを辿って解析していく。

* classes
>生成対象のクラス。

* skip
>classesで指定したクラスの中で、除外するメソッドを記載。
例えば *Sprite::[getQuad]* は、SpriteクラスのgetQuadメソッドは除外するということ。
[*]は、全てのメソッドということ。

* rename_functions
>Lang言語上に作るメソッド名を、C++のメソッド名と異なるものにする場合に指定。
やたら長いメソッド名を短くしたりする。

* rename_classes
>Lang言語上に作るクラス名を、C++のメソッド名と異なるものにする場合に指定。

* abstract_classes
>抽象クラスを指定。（コンストクタを作らない）

iniファイルの中身を確認したら、一旦何も変更せずに閉じてください。

それではこの状態で*bindings-generator*を実行し、グルーコードを生成してみましょう。
先ほど書き換えた*genbindings.py*を実行してください。
いくつかログが出た後に、"Generating lang bindings succeeds." と表示されれば成功です。
成功した場合、以下のファイルが生成されているはずです。

`cocos2d-x/cocos/scripting/lang-bindings/auto/lang_cocos2dx_auto.hpp`
`cocos2d-x/cocos/scripting/lang-bindings/auto/lang_cocos2dx_auto.cpp`

生成コードはまだ何も変更していないので、中身はLua用の記述のままになってます。

#グルーコード生成用のテンプレートファイルを編集
生成されるコードを変更するには以下のディレクトリ配下にある、テンプレートファイルを変更します。
なお、bindings-generatorは[Cheetah](http://www.cheetahtemplate.org/)というPythonのテンプレートエンジンを使用しているため、
Cheetahの文法に従って書いていくことになります。

`cocos2d-x/tools/bindings-generator/targets/lang/templates/`

templatesディレクトリの下には以下のテンプレートファイルがあります。

* layout_head.h
>生成される.hppファイルの先頭に出力される。#include等を書く。
register_all_cocos2dx関数のプロトタイプをここで書いておく。

* layout_head.c
>生成される.cppファイルの先頭に出力される。#include等を書く。

* layout_foot.h
>生成される.hppファイルの末尾に出力される。

* layout_foot.c
>生成される.cppファイルの末尾に出力される。register_all_cocos2dx関数を実装し、
各クラス毎に定義したregisterメソッドを呼び出す処理を書く。

* prelude.h
>各クラス毎のまとまりの最初に出力される。

* prelude.c
>各クラス毎のまとまりの最初に出力される。クラス毎に生成したいコードがあれば書く。

* register.c
>lang_register_cocos2dx_*関数を実装する。クラス毎にbinding登録関数をまとめたもの。

* ifunction.c
>メンバ関数毎に出力される。

* ifunction_overloaded.c
>メンバ関数毎に出力される。オーバーロードしている場合はこちらが出力される。

* sfunction.c
>静的メンバ関数毎に出力される。

* sfunction_overloaded.c
>静的メンバ関数毎に出力される。オーバーロードしている場合はこちらが出力される。

* lambda.c
>std::functionをbindingする際に出力される。

* function.h
>メンバ関数毎に.hppファイルで出力される。(*function*.c共通のファイル)


関数をオーバーロードしている場合は別ファイルになっていますが、これはオーバーロードしている場合は
Lang言語から渡された引数の数や型で、どのオーバーロード関数を実行するかを判定する必要があり、
処理が複雑になるためです。
また、apidoc_*というテンプレートがあるかと思いますが、これはapidoc用に
Lang言語上に生成される関数のシグネチャをコメントで出力するためのものです。

テンプレート関連で、もう一つ以下のYamlファイルを編集する必要があります。
`cocos2d-x/tools/bindings-generator/targets/lang/conversions.yaml`

中身を見るとなんとなくわかるかと思いますが、型の変換方法を定義しているファイルです。
C++⇔Lang言語間の型変換関数を使って、型ごとに呼び出す変換関数を定義します。
"to_native"は、Lang言語 -> C++、from_nativeはその逆です。
なお、キーの文字列で、"@"で始まるものがいくつかあると思いますが、"@"で始まる文字列は正規表現として扱います。

#generator.pyを編集
C++のコードを解析しながら、テンプレートを使ってコードを出力していくのが*generator.py*です。
つまり、*bindings-generator*の本体のスクリプトとなります。
テンプレートの修正だけでは足りない場合等は、*generator.py*を直接いじることになります。

なお、C++コードの解析には以下のスクリプトを使っています。
`cocos2d-x/tools/bindings-generator/clang/cindex.py`
これはClangのPython bindingで、Clangのソースコードに付属しているものです。

#最後に
説明が足りないところも多いかとは思いますが、とりあえずここまでです。
cocosコマンドの対応については私自身現在実装しているところだったりするので、
ある程度まとまったら（その3）として書くかもしれません。
私は現在Squirrelという言語のbindingを少しずつ作っているところです。

明日のAdvent Calendarは、giginetさんです。
giginetさんは12/24に[Cocos2d-x本を出版される](http://giginet.hateblo.jp/entry/2014/11/26/185855)そうです。おめでとうございます！
私は既にAmazonで予約しました。

以上です。
(書いている間に12月4日が終わっていました。ごめんなさい...)