Target Language Compiler | ![]() ![]() |
This section discusses how the Target Language Compiler resolves references to variables (including records).
Scope, in this document, has two related meanings. First, scope is an attribute of a variable that defines its visibility and persistence. For example, a variable defined within the body of a function is visible only within that function, and it persists only as long as that function is executing. Such a variable has function (or local) scope. Each TLC variable has one (and only one) of the scopes described in Scopes below.
The term scope also refers to a collection, or pool, of variables that have the same scope. At any point in the execution of a TLC program, several scopes may exist. For example, during execution of a function, a function scope (the pool of variables local to the function) exists. In all cases, a global scope (the pool of global variables) would also exist.
To resolve variable references, TLC maintains a search list of current scopes and searches them in a well-defined sequence. The search sequence is described in How TLC Resolves Variable References.
Dynamic scoping refers to the process by which TLC creates and deallocates variables and the scopes in which they exist. For example, variables in a function scope exist only while the defining function executes.
Scopes
The following sections describe the possible scopes that a TLC variable can have.
Global Scope. By default, TLC variables have global scope. Global variables are visible to, and can be referenced by, code anywhere in a TLC program. Global variables persist throughout the execution of the TLC program. Global variables are said to belong to the global pool.
Note in particular that the CompiledModel
record of the model.rtw
file has global scope. Therefore, you can access this structure from any of your TLC functions or files.
You can use the scope resolution operator (::
) to explicitly reference or create global variables from within a function. See The Scope Resolution Operator for examples.
Note that you can use the %undef
directive to free up memory used by global variables.
File Scope. Variables with file scope are visible only within the file in which they are created. To limit the scope of variables in this way, use the %filescope
directive anywhere in the defining file.
In the following code fragment, the variables fs1
and fs2
have file scope. Note that the %filescope
directive does not have to be positioned before the statements that create the variables:
Variables whose scope is limited by %filescope
go out of scope when execution of the file containing them completes. This lets you free up memory allocated to such variables.
Function (Local) Scope. Variables defined within the body of a function have function scope. That is, they are visible within and local to the defining function. For example, in the following code fragment, the variable localv
is local to the function foo
. The variable x
is global:
A local variable can have the same name as a global variable. To refer, within a function, to identically-named local and global variables, you must use the scope resolution operator (::
) to disambiguate the variable references. See The Scope Resolution Operator for examples.
Note Functions themselves (as opposed to the variables defined within functions) have global scope. There is one exception: functions defined in generate scope are local to that scope. See Generate Scope. |
%with Scope. The %with
directive adds a new scope, referred to as a %with
scope, to the current list of scopes. This directive makes it easier to refer to block-scoped variables.
The structure of the %with
directive is
adds the CompiledModel.System[sysidx]
scope to the search list. This scope is searched before anything else. You can then refer to the system name simply by
Generate Scope. Generate scope is a special scope used by certain built-in functions that are designed to support code generation. These functions dispatch function calls that are mapped to a specific record type. This capability supports a type of polymorphism in which different record types are associated with functions (analogous to methods) of the same name. Typically, this feature is used to map Block
records to functions that implement the functionality of different block types.
Functions that employ generate scope include GENERATE
, GENERATE_TYPE
, GENERATE_FUNCTION_EXISTS
, and GENERATE_TYPE_FUNCTION_EXISTS
(See GENERATE and GENERATE_TYPE Functions). This section will discuss generate scope using the GENERATE
built-in function as an example.
The syntax of the GENERATE function is
The first argument (blk
) to GENERATE
is a valid record name. The second argument (fn
) is the name of a function to be dispatched. When a function is dispatched through a GENERATE
call, TLC automatically adds blk
to the list of scopes that is searched when resolving variable references. Thus the record (blk
) is visible to the dispatched function, as if there were an implicit %with <
blk
>... %endwith
directive in the dispatched function.
In this context, the record (blk
) is said to be in generate scope.
Three TLC files, demonstrating the use of generate scope, are listed below. The file polymorph.tlc
creates two records representing two hypothetical block types, MyBlock
and YourBlock
. Each record type has an associated function named aFunc
. The block-specific implementations of aFunc
are contained in the files MyBlock.tlc
and YourBlock.tlc
.
Using GENERATE
calls, polymorph.tlc
dispatches to the appropriate function for each block type. Notice that the aFunc
implementations can refer to the fields of MyBlock
and YourBlock
, because these records are in generate scope.
polymorph.tlc
.
%% polymorph.tlc %language "C" %%create records used as scopes within the dispatched functions %createrecord MyRecord { Type "MyBlock"; data 123 } %createrecord YourRecord { Type "YourBlock"; theStuff 666 } %% dispatch the functions thru the GENERATE call. %% dispatch to MyBlock implementation %<GENERATE(MyRecord, "aFunc")> %% dispatch to YourBlock implementation %<GENERATE(YourRecord, "aFunc")> %% end of polymorph.tlc
MyBlock.tlc
.
%%MyBlock.tlc %implements "MyBlock" "C" %% aFunc is invoked thru a GENERATE call in polymorph.tlc. %% MyRecord is in generate scope in this function. %% Therefore, fields of MyRecord can be referenced without %% qualification %function aFunc(r) Output %selectfile STDOUT The value of MyRecord.data is: %<data> %closefile STDOUT %endfunction %%end of MyBlock.tlc
YourBlock.tlc
.
%%YourBlock.tlc %implements "YourBlock" "C" %% aFunc is invoked thru a GENERATE call in polymorph.tlc. %% YourRecord is in generate scope in this function. %% Therefore, fields of YourRecord can be referenced without %% qualification %function aFunc(r) Output %selectfile STDOUT The value of YourRecord.theStuff is: %<theStuff> %closefile STDOUT %endfunction %%end of YourBlock.tlc
The invocation and output of polymorph.tlc
, as displayed on the MATLAB console, are shown below:
The Scope Resolution Operator
The scope resolution operator (::
) is used to indicate that the global scope should be searched when looking up a variable reference. The scope resolution operator is often used to change the value of global variables (or even create global variables) from within functions.
By using the scope resolution operator, you can resolve ambiguities that arise when a function references identically-named local and global variables. In the following example, a global variable foo
is created. In addition, the function myfunc
creates and initializes a local variable named foo
. The function myfunc
explicitly references the global variable foo
by using the scope resolution operator.
%assign foo = 3 %% this variable has global scope . . %function myfunc(arg) %assign foo = 3 %% this variable has local scope %assign ::foo = arg %% this changes the global variable foo %endfunction
You can also use the scope resolution operator, within a function, to create global variables. The following function creates and initializes a global variable:
How TLC Resolves Variable References
This section discusses how the Target Language Compiler searches the existing scopes to resolve variable references.
Global Scope. In the simplest case, the Target Language Compiler resolves a variable reference by searching the global pool (including the CompiledModel
structure).
%with Scope. You can modify the search list and search sequence by using the %with
directive. For example, when you add the following construct
the System[sysidx]
scope is added to the search list. This scope is searched first, as shown by this picture.
Figure 6-1: %with Scope Added to Search Sequence
This technique makes it simpler to access embedded definitions. Using the %with
construct (as in the previous example), you can refer to the system name simply by
Function Scope. A function has its own scope. That scope is added to the previously described search list, as shown in this picture.
Figure 6-2: Scoping Rules Within Functions
For example, in the following code fragment:
% with CompiledModel.System[sysidx] . . . %assign a=foo(x,y) . . . %endwith . . . %function foo (a,b) . . . assign myvar=Name . . . %endfunction . . . %<foo(1,2)>
If Name
is not defined in foo
, the assignment will use the value of Name
from the previous scope, CompiledModel.System[SysIdx].Name
.
In the case of nested functions, only the innermost nested function scope is searched. In the picture below, foo
is called by callfoo
. When resolving variable references in foo
, only the scope of foo
is searched (together with enclosing %with
and global scopes.)
Figure 6-3: Nested File Scopes
File Scope. File scopes are searched before the global scope, as shown in the following picture.
Figure 6-4: File Scopes Searched Before Global Scope
The rule for nested file scopes is similar to that for nested function scopes. In the case of nested file scopes, only the innermost nested file scope is searched.
![]() | Identifier Definition | Target Language Functions | ![]() |