サンプルエクステンションの解説¶
この節では前節でビルドしたサンプルエクステンションに含まれるそれぞれのファイルを一通り見ていきましょう。一連のファイルがお互いどのように機能するかを通し、 Fabric Engine におけるエクステンションがどのように機能するかを理解していきます。
HelloWorld.fpm.json
¶
エクステンションに唯一必須であるファイルは、エクステンションマニフェストファイルです;サンプルでは HelloWorld.fpm.json
が該当、具体的内容は以下
{
"libs": "HelloWorld",
"code": "HelloWorld.kl"
}
エクステンションマニフェストは、 .fpm.json
で終わらせます。 .fpm.json
の前にくっつくファイル名はすなわち <エクステンション名> を表します。このエクステンション名が、KLにおける require ExtensionName;
文によるエクステンションインクルードのエクステンション、あるいは Fabric Engine coreクライアント作成時の exts
パラメータになります。
マニフェストファイルの内容は JSON形式で単一JSONオブジェクトとして記述します (see http://json.org/) サンプルにおけるJSONオブジェクトは libs
and code
; これら2つはオプショナルです。
libs
の値は、文字列, 文字列の配列 どちらも許容します。どの文字列も、エクステンションをなす共有ライブラリのベース名です。実際の共有ライブラリのファイル名はプラットフォームに依存します。サンプルの SConstruct
ファイルが、共有ライブラリのビルドをビルドを実行するプラットフォームに沿う正しいファイル名で扱います。つまりエクステンションがサポートする全プラットフォームに対し、同じ SConstruct ファイルによってビルドを行うことができます。(可能というだけでなく勿論そうすべきです)
code
の値は KL ソースファイルのファイル名(文字列単体でも配列でも)です( .kl
なエクステンションも含みます)。これらはコンパイルされ KLコードに含まれることになり require ExtensionName;
文によりエクステンションインクルードに使用します。
Fabric Engine ではエクステンションの検出にエクステンションパス中に存在する .fpm.json
ファイルを用います。エクステンションパスとは、環境変数 FABRIC_EXTS_PATH
に列挙された全てのディレクトリを含みます。このディレクトリには Fabric Engine をインストールした際のプラットフォームに依存した基本エクステンションが存在する、事前定義されたディレクトリを含みます。 Fabric Engine では、 エクステンションマニフェスト中の libs
と code
メンバは同じエクステンションマニフェストと同じディレクトリに存在しなければなりません。
.fpm.json
についてのより詳細な情報は エクステンション・マニフェストファイル を参照してください
HelloWorld.kl
¶
HelloWorld.kl
ソースファイルは、 require ExtensionName;
により、他の KLソースからこのエクステンションを名前で読み込んでくる際にインクルードすることになる、 KL ソースコードです。換言すると HelloWorld
エクステンションの提供する機能を、とある KLオペレータで使用したいのであれば、オペレータのコードの一番上に require HelloWorld;
とするとインクルードできます。 HelloWorld.kl
ファイルの内容は単純です:
function String GetHelloWorldString() = "GetHelloWorldString";
これは KL文であり、ネイティブコードの共有ライブラリで定義された関数を参照しています。一番重要な点は、 = "GetHelloWorldString"
です:これは KLに、「この関数はネイティブコード共有ライブラリの中で GetHelloWorldString
として呼ばれるものだ」ということを示しています。これを欠いてしまうと、ライブラリをロードした途端、リンク問題をひきおこします。なぜならディフォルトでは KL関数は複雑な内部名を持つためです。 = "GetHelloWorldString"
という記法は KLへと共有ライブラリでのシンボル名を単純に伝達しているわけです。
注意点:KLソースファイルは 型定義( struct キーワードを使用)や、メソッド定義、オペレータ定義、その他 KL で可能なことは全てを含めることができます。先ほど同じ = "..."
記法によってメソッドとネイティブコード共有ライブラリでのコードをバインドすることができます。
HelloWorld.cpp
¶
HelloWorld.cpp
ソースファイルには ネイティブコード共有ライブラリの C++ コードが含まれ明日。内容は以下のとおりです:
#include "HelloWorld.h" // Automatically generated by kl2edk
using namespace Fabric::EDK;
IMPLEMENT_FABRIC_EDK_ENTRIES( HelloWorld )
FABRIC_EXT_EXPORT void GetHelloWorldString(
KL::String::Result result
)
{
Fabric::EDK::log("Extension: Enter GetHelloWorldString");
result = "Hello, world!";
Fabric::EDK::log("Extension: Leave GetHelloWorldString");
}
#include "HelloWorld.h"
の行では C++ヘッダファイル ― Fabric Engine EDK (Extension Development Kit) のための全定義の詰まったヘッダファイルのインクルードを行います。
このヘッダファイル HelloWorld.h
は、HelloWorld.kl
を元に kl2edk ユーティリティにより生成されます; この振舞は SConstruct
ビルドスクリプトからも自動で行われます。 kl2edk コマンドは、KLソールファイルで使用されている全 <カスタム構造体, オブジェクト, インタフェース型>を C++での表現へと生成します。関数プロトタイプについてもどうように C++関数へと変換されます。 kl2edk についての詳細は kl2edk ユーティリティ を参照してください。
using namespace Fabric::EDK;
の行では、EDK関数と型を EDKネームスペース無しに参照できるようにします。これは単に記法を楽にするためです。
IMPLEMENT_FABRIC_EDK_ENTRIES
の行は、エクステンションの中で1度だけ記述する必要があるものです。h基数を一つとり、エクステンション名をわたします。エクステンションのロードに必要となる内部関数の実装に使用します。
注釈
IMPLEMENT_FABRIC_EDK_ENTRIES
呼び出しにはいくつか種類があり、 IMPLEMENT_FABRIC_EDK_ENTRIES_WITH_SETUP_CALLBACK
とすると引数を二つとるようになります。初期化処理の最後に呼ばれる関数のアドレスをわたします。エクステンション中、1度限りのセットアップ処理を導入できます。コードの残りでは、KLで利用可能となる関数定義です。重要な点を幾つか:
- KLでも使用可能な関数にするには、
FABRIC_EXT_EXPORT
キーワードを頭につけます。キーワードを使用する目的は、この関数がアンマネージドなシンボル名を含み、正しく呼び出すための規則を持つということを明示するためです。KL::String
型を第一パラメータとして使用する際には注意が必要です。KLの宣言function String GetHelloWorldString()
関数による戻り値がこのKL::String
型の値を解すことにも注意してください。呼び出し規則についてのセクションでより詳しく記述いたします。
test.py
¶
test.py
ファイルは Pythonソースファルです。単純な Fabric Engine dependency graph を作成し、その dependency graphから HelloWorld
エクステンションの GetHelloWorldString
関数を呼び出します。
ファイル最初のほうで明示的に、カレントディレクトリを環境変数 FABRIC_EXTS_PATH
へ設定します:
import os
if 'FABRIC_EXTS_PATH' in os.environ:
os.environ['FABRIC_EXTS_PATH'] = os.pathsep.join(['.', os.environ['FABRIC_EXTS_PATH']])
else:
os.environ['FABRIC_EXTS_PATH'] = '.'
通常カスタムエクステンションは所定の決められた場所へとインストールします。この場所は環境変数 FABRIC_EXTS_PATH
により事前に設定した場所です。ただし、上記のように、Coreクライアント作成前であれば、 FABRIC_EXTS_PATH
を設定し使用することが可能です。
次の2行で、 Fabric Engine クライアントを作成します。 Fabric Engine の Coreと直接やりとりをします。エクステンションの機能デモンストレーションするのに最適ですね。
import FabricEngine.Core as fabric
fabricClient = fabric.createClient();
次の数行では、 KLオペレータを作成します。KLオペレータはエクステンションの GetHelloWorldString
を呼び、結果を io
パラメータに保存、後の工程でノードメンバにバインドします:
opSource = [
"require HelloWorld;",
"",
"operator entry(io String string) {",
" report("KL: Enter entry");",
" string = GetHelloWorldString();",
" report("KL: GetHelloWorldString returned: " + string);",
" report("KL: Leave entry");",
"}"
]
op = fabricClient.DG.createOperator("op")
op.setSourceCode("\n".join(opSource))
op.setEntryPoint("entry")
code:require HelloWorld;: この行では注意が必要です。この文は GetHelloWorldString
関数の宣言をするために必須です。require文 はエクステンションによって提供されるKLソースコードをまとめてインクルードします。このサンプルの場合、 HelloWorld.kl
中のコードです。
次の数行では、 String
型のメンバを持つノードを作成、そのノードにオペレータをバインド、ノードを評価、しています。つまり entry
オペレータを実行し、エクステンションの GetHelloWorldString
関数を呼び出させます。
b = fabricClient.DG.createBinding()
b.setOperator(op)
b.setParameterLayout(['self.string'])
node = fabricClient.DG.createNode("node")
node.addMember("string", "String")
node.bindings.append(b)
node.evaluate()
次の行で、オペレータがメンバを正しい値に設定したかどうか検証します。
print "Python got: " + node.getData("string", 0)
最後にクライアントを閉じ、スクリプトを終了させます。
fabricClient.close()
SConstruct
¶
SConstruct
ファイルはエクステンションのビルドに使用します。最重要な箇所は、最後:
fabricBuildEnv.Extension(
'HelloWorld',
['HelloWorld.cpp', 'HelloWorld.kl']
)
この行では、 scons に「エクステンション名 HelloWorld
をネイティブコードソースファイル HelloWorld.cpp
と HelloWorld.kl
KLソースコードを使用しビルドせよ」と指示しています。このビルドコマンドにより、入力となるKLソースファイルから kl2edk を使用しヘッダファイル HelloWorld.h
が生成されます。結果、 HelloWorld.cpp
から HelloWorld.h
がインクルードされます。