FlexScriptインターフェイス
説明
それぞれのFlexScriptインターフェイスは、エンジン内の特別なオブジェクトにより定義されます。このオブジェクトは、インターフェイスのメソッド、演算子、プロパティのリストを格納します。新しいインターフェイスを追加するには、新しいインターフェイスオブジェクトを作成し、コンパイラーとコードエディタが読み取るリストに含まれることを確認します。
モジュールから新しいインターフェイスを作成するには、bindInterface()というメソッドをオーバーライドしてから、そのオブジェクトについてbindInterface()を呼び出す必要があることをエンジンに通知します。これら2つの手順は、ObjectDataTypeオブジェクトとSimpleDataTypeオブジェクトで多少異なるため、両方について説明します。また、ポインターをラップするインターフェイス(Tableインターフェイスと同様に)を作成する方法もあります。これについても説明します。この3つの場合について、bindInterface()メソッドを実装/オーバーロードする必要があります。その方法について最後に説明します。
ObjectDataType
C++クラスの定義に、次のメソッドを必ず追加します。
virtual void bindInterface() override
さらに、このメソッドを実装する必要があります。これについては後で説明します。エンジンが確実にオブジェクトのbindInterface()メソッドを呼び出すように、オブジェクトのツリー定義にノードを追加する必要があります。このノードは、behavior/eventfunctionsのサブノードです。名前はflexScriptInterfaceで、テキストデータを含む必要があります。このテキストデータには、クラスのFlexScript名が含まれます。エンジンは、このノードを持つクラスをライブラリから検索し、そのクラスのbindInterface()メソッドを呼び出します。
SimpleDataType
ObjectDataTypeと同様に、クラスのC++定義に次のメソッドを追加します。
virtual void bindInterface() override
実装については後で説明します。次に、このクラスのインターフェイスオブジェクトを作成するようエンジンに通知する必要があります。エンジンは、作成するインターフェイスをMAIN:/project/exec/globals/sdtinterfacesノードで調べます。モジュールでは、次の構造を持つノードを、このリストに追加する必要があります。
- ノード名が、クラスのgetClassFactory()名である。
- ノードのテキストがインターフェイス名である。
- 1つのサブノードが存在する。
- サブノード名がisCouplingである。
- サブノードの数値は、クラスがSimpleDataType (0)とCouplingDataType (1)のどちらを継承するかを反映している。
ポインターベースのヘルパークラス
SDTまたはODTをベースとしていない、Conveyor.SlugBuilderなどのクラスがいくつか存在します。このインターフェイスは、次のオブジェクトにより定義されます。
class SlugBuilderProperty
{
protected:
TreeNode* conveyorNode = nullptr
const SlugBuilder& getSlugBuilder()
public:
// methods and properties
// ...
static void bindInterface()
}
ここで重要なのは、このクラスにはbindInterface()という名前の、パラメータを持たないstatic voidメソッドが存在することです。クラスにこれが存在する場合、そのクラス用のFlexScriptインターフェイスを作成できます。
エンジンでFlexScriptインターフェイスオブジェクトを作成するには、別のオブジェクトのbindInterface()メソッド内でbindClass()メソッドを呼び出す必要があります。コンベヤのbindInterface()メソッドには、次のコードが含まれます。
bindClassByName<SlugBuilderProperty>("Conveyor.SlugBuilder")
このメソッドは、Conveyor.SlugBuilderという名前のFlexScriptクラスを作成し、C++ SlugBuilderPropertyクラスにより定義されたインターフェイスを与えるよう、コンパイラーに指示します。これについては、次のセクションでさらに説明します。
bindInterface()
ConveyorクラスのbindInterface()コードは次のとおりです。
void Conveyor::bindInterface()
{
bindParentClass("Object")
bindDocumentationXMLPath("modules\\Conveyor\\help\\FlexScriptAPI\\Conveyor.xml")
bindTypedPropertyByName<Motor>("motor", "Object", force_cast<void*>(&Conveyor::__getMotor), nullptr)
bindMethod(estimateConveyTime, Conveyor, "double estimateConveyTime(Object origin, double n_a, Object dest, double n_a, double itemLength, int flags = 0)", BIND_METHOD_STATIC)
bindMethod(sendItem, Conveyor, "void sendItem(Object item, Object dest)", BIND_METHOD_STATIC)
bindTypedProperty(targetSpeed, double, &Conveyor::__getTargetSpeed, &Conveyor::__setTargetSpeed)
bindTypedProperty(currentSpeed, double, &Conveyor::__getCurrentSpeed, nullptr)
bindTypedProperty(defaultSpeed, double, &Conveyor::__getDefaultSpeed, nullptr)
bindTypedProperty(acceleration, double, &Conveyor::__getAcceleration, nullptr)
bindTypedProperty(deceleration, double, &Conveyor::__getDeceleration, nullptr)
bindTypedProperty(width, double, &Conveyor::__getWidth, nullptr)
bindTypedProperty(length, double, &Conveyor::__getSimLength, nullptr)
bindNodeListArrayClassByName<ConveyorItems>("Conveyor.Items", "Conveyor.Item", true)
bindTypedPropertyByName<ConveyorItems>("itemData", "Conveyor.Items", force_cast<void*>(&Conveyor::__getItemData), nullptr)
bindClassByName<SlugBuilderProperty>("Conveyor.SlugBuilder")
bindTypedPropertyByName<SlugBuilderProperty>("slugBuilder", "Conveyor.SlugBuilder", force_cast<void*>(&Conveyor::__getSlugBuilder), nullptr)
}
このコードのbind呼び出しについて簡単に説明します。
- bindParentClass - FlexScriptインターフェイスが他のFlexScriptクラスを継承するため使用されます。これにより、Conveyorクラスはオブジェクトのメソッドとプロパティにアクセスできます。
- bindDocumentationXMLPath - FlexScriptインターフェイスを、特定のヘルプドキュメントに接続します。
- bindTypedPropertyByName - このメソッドは、インターフェイスにプロパティを追加します。プロパティ名、返すタイプのFlexScript名、getter(オプションとしてsetterも)メソッドのポインターを指定します。単純なプロパティについては、bindTypedPropertyマクロを使用できます。
- bindMethodByName - このメソッドは、インターフェイスにメソッドを追加します。残念ながら、これはほとんどの場合にintellisenseで構文エラーになりますが、構築はされます。staticフラグを渡すときは、渡すメソッドが実際に静的メソッドであることを確認してください。
- メソッドのシグネチャでは、FlexScriptで利用可能なタイプ名のみを使用します。エンジンがこの式を解釈できないと、FlexSimが起動時にクラッシュします。
- bindNodeListArrayClassByName - このメソッドは、ノードリストの配列に基づいて新しいクラスインターフェイスを作成します。カスタムのNodeListArrayクラスが存在する場合、このメソッドを使用して、このクラス用のインターフェイスを作成できます。
- bindClassByName - このメソッドは、任意のクラスに基づいて新しいクラスインターフェイスを作成します。このクラスには、static void bindInterface()メソッドが必要です。
- デフォルトでは、このメソッド(およびbindNodeListArrayByName)でバインドされるクラスは宣言できません。これらのクラスについては、var typeを使用してローカル変数のみを作成できます。ユーザーアクセス可能なタイプを作成するフラグを渡すことができ、そのタイプのローカル変数を直接宣言可能です。
Conveyor.SlugBuilderのbindInterfaceのコードは次のとおりです。
void Conveyor::SlugBuilderProperty::bindInterface()
{
XS
bindCopyConstructor(&SlugBuilderProperty::construct)
bindCopyAssigner(&SlugBuilderProperty::operator=)
bindTypedProperty(conveyor, Conveyor, &SlugBuilderProperty::__getConveyor, nullptr)
bindTypedProperty(isEnabled, int, &SlugBuilderProperty::__getEnabled, &SlugBuilderProperty::__setEnabled)
bindTypedProperty(isClear, int, &SlugBuilderProperty::__getIsClear, nullptr)
bindTypedProperty(state, int, &SlugBuilderProperty::__getState, nullptr)
bindMethodByName<decltype(&SlugBuilderProperty::makeReady)>("makeReady", &SlugBuilderProperty::makeReady, "void makeReady()")
bindMethodByName<decltype(&SlugBuilderProperty::release)>("release", &SlugBuilderProperty::release, "void release()")
// bindMethodByName<decltype(&SlugBuilderProperty::addItem)>("addItem", &SlugBuilderProperty::addItem, "void addItem(Object item)")
bindDocumentationXMLPath("modules\\Conveyor\\help\\FlexScriptAPI\\Conveyor.SlugBuilder.xml")
XE
}
このコードの新しいバインド呼び出しについて、以下に説明します。
- bindCopyConstructor - SDT/ODT以外のクラスにのみ必要です。これにより、必要なポインターをクラスのインスタンスから、別のインスタンスにコピーできます(conveyorNode = other.conveyorNode)。
- bindCopyAssigner - SDT/ODT以外のクラスにのみ必要です。これにより、この変数をFlexSimのvar typeに割り当てできます。
bindInterfaceでは、他のいくつかのbind___メソッドを使用できます。
- bindBracketOperator - FlexScriptインターフェイスで、[]構文を使用できます。
- bindCastOperator - このオブジェクトを他のタイプにキャストできます(通常はTreeNode*)。
最後に、エンジンは2パスでクラスをバインドすることに注意してください。最初のパスでは、すべての外部タイプ(SDT/ODT)について空のタイプシェルを作成します。これにより、タイプはメソッド呼び出しで利用可能になります。bindClassまたはbindNodeListArrayClassを使用する場合、これらのクラスは、そのクラスを扱うプロパティやメソッドよりも前にバインドします。
参照
bindMethod
メンバーメソッドをFlexScriptで利用可能にするために使用します。
bindMethod(mehod, className, ...)
template <class FunctionPtrType>
bindMethodByName(const char* name, FunctionPtrType memberPtr, const char* signature, int flags = 0)
flags
パラメータは、0またはBIND_METHOD_STATIC
です。C++関数がstatic関数の場合のみ、staticフラグを使用します。このフラグを使用すると、関数はインスタンスではなくクラス自体を使用してアクセスされます(例:Color.random()
)。
bindOperator
オブジェクトの演算子(+、-など)をFlexScriptで利用可能にするために使用します。
bindOperator(method, className, signature)
template <class FunctionPtrType>
bindOperatorByName(const char* opName, FunctionPtrType memberPtr, const char* signature)
有効な演算子名は次のとおりです。
Add: "+"
Subtract: "-"
Multiply: "*"
Divide: "/"
Mod: "%"
Equal: "=="
NotEqual: "!="
GreaterThan: ">"
GreaterThanOrEqual: ">="
LessThan: "<"
LessThanOrEqual: "<="
LeftBitShift: "<<"
RightBitShift: ">>"
BitwiseXOr: "^"
BitwiseOr: "|"
BitwiseAnd: "&"
LogicalOr: "||"
LogicalAnd: "&&"
Brackets: "[]"
Assignment: "="
PlusAssignment: "+="
MinusAssignment: "-="
MultiplyAssignment: "*="
DivideAssignment: "/="
BitwiseOrAssignment: "|="
BitwiseAndAssignment: "&="
Negate: "unary -"
LogicalNot: "!"
BitwiseNot: "~"
Cast: "cast"
Increment: "--"
Decrement: "++"
Construct: "construct"
Dereference: "dereference"
これらのメソッドのシグネチャでは、関数名を省略できます。単に、戻り値のタイプと引数のみを指定します。
bindTypedProperty
オブジェクトのプロパティをFlexScriptで利用可能にするために使用します。
bindTypedProperty(method, className, signature, ...)
template <class Type>
bindOperatorByName<Type>(const char* propName, const char* typeName, void* getter, void* setter, const char* version)
プロパティ名は有効な識別子名の必要があります(スペースを含まず、英字で始まるなど)。typeNameは、目的のタイプのFlexScript名にする必要があります。version文字列は、このプロパティを持つタイプが以前にFlexSimで利用可能で、動的なプロパティを保有していた場合のみ、使用する必要があります。上述のルールすべての例として、トークンのnameプロパティをバインドする方法を次に示します。
bindTypedPropertyByName<const char*>("name", "char*",
force_cast<void*>(&TokenHelper::getName), force_cast<void*>(&TokenHelper::setName), "17.1.0");
ただし、タイプのクラス名がFlexScriptクラス名と一致する場合、bindTypedProperty()マクロを使用できます。
bindTypedProperty(activity, treenode, &TokenHelper::getCurActivity, nullptr);
シグネチャ
const char* signature
引数を必要とするbind*()メソッドは、FlexScriptシグネチャを指定するようユーザーに求めます。FlexScriptシグネチャは次のルールに従います。
- シグネチャには、FlexScriptタイプ(組み込みタイプ、つまりbindInterface()を呼び出すタイプ)のみを含めることができます。
const
とマークされた引数を含めることはできません。- char*を除き、ポインターで引数を渡すことはできません。
- 参照として渡される引数はマークする必要があります。
// Good
void func1(const Variant& value) // => "void func1(Variant& value)"
Table getTable(TreeNode* source) // => "Table getTable(treenode source)"
const char* getText() // => "char* getText()"
// Bad
// void func1(const Variant& value) => "void func1(const Variant& value)"
// Table getTable(TreeNode* source) => "Table getTable(TreeNode* source)"
bindInterface()を実装すると、FlexSimが起動しない問題が発生することがあります。これは、不正なシグネチャにより引き起こされる場合があります。タイプ、const、参照をチェックしてください。bindInterface()が問題であることを確認するには、この関数をコメントアウトし、dllを構築して、FlexSimを再起動します。