Operators and Bindings¶
The KL code that runs within the dependency graph and event graph is contained in objects called Operators. The same operator can be bound to multiple Nodes and EventHandlers by using different Bindings (see below) that share the same Operator.
Operator Creation¶
An Operator is created by calling fabricClient.RT.createOperator
, passing the name of the Operator as the first argument. Operator names must be unique and not shared with any Nodes, Events or EventHandlers.
>>> op = fabricClient.DG.createOperator("doSomething")
>>>
Setting Operator Source Code¶
The source code contained in the Operator is set using the Operator’s setSourceCode
method. It takes a string containing the KL source code.
注釈
Source code is first usually loaded from an external resource, using eg. the fabricClient.loadResourceURL
function, rather than being included as an inline string as is done in these examples.
After setting the source code, you can check if any warnings or errors were generated by the KL compiler by calling the getDiagnostics
method, which returns an array of objects describing the warnings/errors, including the level (warning/error), line number, column number and message. You can later retrieve the source code by calling the getSourceCode
method.
>>> op.setSourceCode("operator entry( io Scalar result, in Size index, in Container self ) { result = 3.14 }")
>>> op.getDiagnostics()
[{u'column': 82, u'line': 1, u'level': u'error', u'desc': u'syntax error, unexpected }, expecting ;', u'filename': u'(unknown)'}]
>>> op.setSourceCode("operator entry( io Scalar result, in Size index, in Container self ) { result = 3.14; }")
>>> op.getDiagnostics()
[]
>>>
Setting the Operator Entry Point¶
In addition to source code, an operator needs an entry point, which is the name of the KL operator (see the KL プログラミングガイド) in the source code that should be called when the operator is invoked. Note that this must be an KL operator and not a KL function. The entry point is specified by calling the setEntryPoint
method. By specifying the source code and entry point separately, it is possible to have multiple possible entry points into the same source code.
>>> op.setEntryPoint('entry')
>>> op.getEntryPoint()
'entry'
>>>
Bindings¶
To make an operator run on a Node or EventHandler, you must create a Binding object which describes what data the KL operator arguments are bound to when the operator is run.
注釈
It is possible to have multiple bindings that all share a single operator.
A binding object is created by calling fabricClient.DG.createBinding
, and you set the Operator called by the Binding by calling the Binding’s setOperator
method. This operator can later be retrieved by calling the Binding’s getOperator
method.
>>> binding = fabricClient.DG.createBinding()
>>> binding.setOperator(op)
>>> binding.getOperator()
<fabric._OPERATOR object at 0x1958e50>
>>>
Binding Parameter Layouts¶
The way in which the KL operator arguments are bound is specified by calling the Binding’s setParameterLayout
method. setParameterLayout
takes a single parameter that is an array of strings. The length of the array must be equal to the number of parameters taken by the KL operator in the Operator’s source code, and each string describes what data that parameter should bind to.
Such string is of the format object.member
, or object
for a special usage which we will detail later. The object
part refers to what Node, EventHandler or Event object contains the data to be bound, as follows:
- If
object
isself
, the data is contained on the object where the binding is attached - For Bindings that live on Nodes,
object
is the name of the direct dependency Node that contains the data - For Bindings that live on EventHandlers,
object
is the name of an ancestor EventHandler in the call chain as specified by a call to itssetScopeName
method, or a Node that is connected to the EventHandler through a call to the EventHandlers’ssetScope
method.
The member
part refers to the data member on the object specified by object
, with support for the following additional syntaxes:
- If
member
is simply the name of a member (eg. “position”), the parameter will be bound to that member once for each slice. The operator will be invoked once for each slice of the Node, potentially in parallel. The KL parameter in the operator must be anio
parameter whose type is the type of the member. - If
member
is the name of a member followed by[]
(eg.position[]
), the parameter will be bound to a variable-length array that contains the data for all the slices for that member. The length of the array will be equal to the slice count of the Node. The KL parameter in the operator must be anio
parameter whose type is a variable-length array of the type of the member. - If
member
isindex
, the parameter will be index of the current slice for which the operator is being executed. The parameter must be anin
parameter of typeIndex
(or, equivalently,Size
) - If
object
is specified (instead ofobject.member
), then the parameter must be of typeContainer
, which allows you to get or set the Node slice count in KL. CallingContainer
‘sresize(Size)
method will immediately change the slice count of the Node, and thesize()
method will return its current slice count. Theresize(Size)
method requires that the parameter is specified asio
.
>>> binding.setParameterLayout( ["self.result","self.index","self"] )
>>> node = fabricClient.DG.createNode("foo")
>>> node.bindings.append(binding)
>>> node.getErrors()
[u"binding 0: operator 'doSomething': node 'self': parameter 1: member 'result': 'result': no such member"]
>>> node.addMember("result","Scalar")
>>> node.getErrors()
[]
>>>
注釈
Even when a Binding binds a parameter to a member of a dependency of a Node, rather than a member of the Node itself, the parameter must still be declared as an io
parameter in KL. This is a limitation of the system which will be removed in the future; in fact, it will become required that members of non-self
objects be bound to in
parameters.