Errors arising from two different areas have to be considered in the validation process. In the language domain, errors due to illegal or illogical usage of the language concepts might be introduced into the specification; while in the problem domain, logical errors might be introduced.
With traditional computer program development, most illegal uses of language concepts are found by compilers or by run-time systems. Examples are syntax errors, missing declarations, division by zero, or indexing an array out of its bounds.
In the problem domain, however, the only feasible ways of detecting logical errors in non-trivial programs are testing and proofreading. When it comes to specifications in SDL, language domain errors can be detected by using the SDT Analyzer, which can be seen as a compiler without a code generation facility (see chapter 31, The Analyzer). To detect problem domain errors, testing by simulating the specification is the main procedure available. Please see also chapter 33, The Validator.
The specification of a protocol in SDL, for instance, specifies a signal interface by giving a hypothetical implementation of the components in the protocol. This strategy immediately brings up two different purposes for simulating the behavior of a system specification: to understand the external view and to understand the internal view.
In the external view, the signal interface is of concern, while the internal behavior of the system specification (the behavior of the processes in the system) is of little or no interest. In the internal view, the internal behavior of the system specification is of concern, while the external signal interface is simply seen as part of the internal behavior.
A simulation of the internal behavior of a system specification constitutes an important part of the validation of the specification, both as a debugging tool and as a means to increase the understanding of the dynamic behavior of the specification. A designer of a system might use this kind of simulation to understand the specification better.
The ability to simulate and debug applications generated by the C Code Generator at an SDL level is a very important feature towards achieving the correct overall behavior of the application. The debugging facilities provided by SDT have much in common with interactive debuggers for ordinary programming languages. The debugging is performed on a host computer.
Another application of the C Code Generator as a simulator generator is of course in SDL education, where simulation of especially the internal behavior of a system specification can serve as a powerful way of clarifying the semantics of SDL concepts.
To simplify this kind of simulation, a number of SDL abstract data types and their implementations have been developed, where, for example, random number generation and handling of queues are supported. Please see chapter 35, The ADT Library.
As a generated C program does not know what it communicates with, it can of course communicate with any type of application, as long as the application is connected to the PostMaster (the communication medium) and sends signals according to the defined format. How to achieve this is described in the chapter chapter 10, How to Use the SDT Public Interface.
A very interesting group of such applications are user interfaces. By connecting a user interface and an SDL simulation you can achieve several things: You can, for example, build well-designed application oriented user interfaces that present what is going on in a simulation, or you can in a simple way define the logic behavior behind a user interface during its prototyping phase.
As indicated above, the C Code Generator contains two components:
Figure 552 : The Production of an Executable Program ----- (fig) -----
You interact, using SDL terminology, with the simulator through a monitor system, which contains a number of commands to:
The SDT Analyzer, which contains the C Code Generator can also be started as a stand-alone tool. For more information about this possibility please see "The Analyzer Command-Line UI" on page 1541 in chapter 31, The Analyzer.
A C program can only be generated for a complete SDL system. The C code that constitutes the program can, however, be generated on multiple files, which means that a local change in, for example, a block diagram only requires a regeneration and recompilation of the code for that unit. The object files, (the compiled versions of the C files) for the other unchanged units can then be used in the link operation to form a new executable program. This feature in automatically used by the make facility and the Quick Buttons, to minimize the amount of work and thus the turn-around time, for the process from a change in the SDL system to a new simulator (for example).
The separation of the C code for an SDL system can be decided by the user. The Edit Separation command from the Generate menu is used for that purpose, see chapter 22, The SDT Organizer, on page 1123. The effect on the generated file structure and some guidelines of how to use separation can be found in the section "Selecting File Structure for Generated Code - Directive #SEPARATE" on page 2020.
A generated simulator or validator can however be started in two different ways:
The compilation and linking of a generated C program may also be started directly from UNIX by typing the command:
make -f name_of_makefilewhere name_of_makefile should be replaced by the name of the generated make file. This file is named system_name.m if it is generated by the C Code Generator, and SDTmake.m if it is generated by the Organizer.
The execution of a generated C program can also be started from the operating system by typing the name of the executable program.
To compile, link and execute from the operating system, the environment variable sdtdir should be given an appropriate value. This variable is given the correct value in the initialization file sdt.sou, which is used to make SDT available. sdtdir is used to find include files and object files during compilation and linking, and to find the help file during execution. The make file also requires that the environment variable sctdir refers to the directory containing the makeoptions file and the object files for the runtime library (the directory selected by the popup menu Standard kernel in the Organizer's Make dialog). sctdir is usually a subdirectory to sdtdir.
An error message starts with the line:
** Internal Code Generation Error **and is followed by a description of the error. The error:
Feature of SDL not implemented: #1will also contain a trace showing where in the SDL source the feature was found.
All the error messages are listed below, together with a short explanation. Some messages contain a"#" followed by a number which is used to indicate where information, specific of the error situation, can be found.
Feature of SDL not implemented: #1The translator cannot yet handle this SDL feature. See the list of restrictions in the section "Restrictions" on page 2052.
Cannot open outfileA .c, .h, or .m file cannot be opened. Probably the user have no write permission in the target directory.
WARNING! Unique name generation failed.The scope structure is too deep.
#1 must be a unique name.#1 is replaced by the name of an SDL unit. During code generation the C code generator prefixes SDL names to make them unique. The prefixing algorithm and the limit for the length of the prefix might make it impossible to generate a prefix, in which case this warning is issued. It is still possible to execute the generated program if the name, without prefix, is unique.
Node = nil in #1 Incorrect node type in #1. Nodetype: #2 Internal error in the PathStructure Line exceeded MaxLineLengthThese four last errors are internal errors in the C Code Generator, which we would be pleased if you reported to us. The telephone and fax numbers to Telelogic Customer Support can be found on page 1001 of this manual.
------------------------------------------------------------------- Note: If it is the first time you read this manual we recommend that you temporarily skip the remaining part of this chapter and instead pro ceed with chapter 32, The Simulator. When you have acquainted yourself with the monitor system, please continue here again. -------------------------------------------------------------------In this section some implementation details are presented, that can be useful for understanding how a generated simulation or application behaves. Abstract data types are treated in the next section.
At program start up the system time, SDL Now, is zero. The system clock is stopped during the time the program spends in the monitor system.
------------------------------------------------------------------- Note: The C standard function time used as real time clock returns the time in seconds. It does not handle parts of seconds. The implemen tation of the clock can be changed by re-implementing the function SDL_Clock in sctos.c. -------------------------------------------------------------------
A transition is always executed without any interrupts, if the user does not manually rearrange the ready queue using an appropriate command provided by the monitor system (RearrangeReadyQueue). It is possible to execute a few SDL symbols in one transition and then to rearranged the ready queue and execute another transition. The interrupted transition can afterwards be executed to its end.
A quasi-parallel strategy for selecting transitions to be executed is thus the basic scheduling mechanism. SDL does not in itself define an execution strategy and does not have concepts such as priorities for processes, which require concurrent execution of transitions (or interrupts within transitions). The selected strategy is therefore an allowed, but not the only possible strategy for execution.
As a consequence of the execution strategy, the C Code Generator is not directly suited for simulation of "timing effects", that is, situations where the time or order of actions in different process instances is of vital importance.
Example 57 : SchedulingAn example of such a situation is: Suppose a process instance A outputs two signal instances during the same transition, one to process instance B and one to process instance C. During the corresponding transitions of B and C, a signal instance is sent to process instance D. If the behavior of the system is dependent on the order in which the signal instances are received in the input port of D, then this a hazard situation where the execution speed of process instances and the delay of signals in channels will determine the behavior. The way to handle such a situation would be to manually decide the order in which transitions should be executed.
As the C Code Generator is also intended to generate applications, process priority has been introduced as an additional feature. For more information about how to assign priorities to processes see subsection "Assigning Priorities Directive #PRIO" on page 2042.
Static enabling conditions or continuous signals do not provide any problems or any execution overhead, except that the corresponding expressions have to be calculated at nextstate operations. Dynamic enabling conditions or continuous signals, however, have to repeatedly be recalculated. The strategy selected for these expressions is to recalculate them after each transition or timer output performed by any process (and additionally also before the monitor is entered within a transition). In other words, each process waiting in a state containing a dynamic enabling condition or continuous signal executes an implicit nextstate operation between each transition or timer output performed by other processes.
The reason for bringing this question up is has to do with the implementation of arrays and powersets. There are two different implementations for each of these concepts, see "Array" on page 1983 and "Powerset" on page 1985. An array in SDL can either be translated to an array in C or a linked list in C. A powerset can either be translated to a bit array in C or a linked link. The translation method is selected by looking at the index type. If the index type is a syntype with one limited range, the array and bit array scheme is used, otherwise the linked list is used.
If a synonym translated to a variable is used in a range condition of a syntype and the syntype is used as an index sort in an array or powerset instantiation, the linked list scheme is used to implement the array or powerset. The reason for this is that the length of the array cannot depend on a variable in C.
Example 58 : Macro Definition #define synonym1 value1 #define synonym2 value2The synonym names are the SDL names (without any prefixes) and with any character not in letters, digits or underscore removed.
Example 59 : #CODE Directive /*#CODE #TYPE #include "filename" */If this structure is used, the value of an external synonym can be changed merely by changing the corresponding macro definition and recompiling the system.
--------------------------------------------------------------------- Note: When an application is created, macro definitions should be used for all external synonyms, as the function for reading synonym values stored on file is not available. (See below.) ---------------------------------------------------------------------
At the prompt
External synonym file :
Example 60 : Values at Program Startup synonym1 value1 synonym2 value2The synonyms may be defined in any order.
In the C Code Generator we use a model where the imported value is directly obtained from the exporter, which of course make the import operation much faster. However, the scheduling effect of the signal interchange is lost, as well as the change of SENDER in the involved processes. If these effects are important for an application, remote procedure calls can be used instead, see below.
output of pCALL signal with all parameters.
nextstate in pWAIT, i.e. a implicit wait state.
input of pREPLY signal with all IN/OUT parameters.
In the exporting process there will be implicit transitions where the pCALL signal can be handled.
input pCALL.
call remote procedure with parameters from pCALL.
output pREPLY with the IN/OUT parameters.
nextstate -
For more details about this model, please see the SDL recommendation.
Example 61 : Procedure Call TASK i := (call p(1)) + (call Q(i,k));is translated to:
CALL p(1, Temp1); CALL q(i, k , Temp2); TASK i := Temp1 + Temp2;Operators which are defined using operator diagrams, are according the models in the SDL recommendation, treated exactly as value returning procedure.------------------------------------------------------------------ Note: The value returning procedure calls are transformed to ordinary calls, by adding a new IN/OUT parameter for the procedure result, last in the call. ------------------------------------------------------------------
any(SortName)within an expression, or to write just
anyin a decision.The second case, with any in a decision, is implemented in the following way:
--------------------------------------------------------------------- Note: any(Sort) where Sort is a syntype is only implemented if the syntype contains at most one range condition which is of the form a:b, that is one limited range. ---------------------------------------------------------------------If any(SortName) is used for a sort violating the note above, there will be a C compilation error on the symbol ANY_SortNameWithPrefix. This means that a user can implement any for such sorts himself by defining a C macro with this name, that implements any for the given sort.
---------------------------------------------------------------------- SDL name/operator C name/expression/operator ---------------------------------------------------------------------- Boolean SDL_Boolean False, True SDL_False, SDL_True not xNot_SDL_Boolean =, /= yEqF_SDL_Boolean, yNEqF_SDL_Boolean and xAnd_SDL_Boolean or xOr_SDL_Boolean xor xXor_SDL_Boolean => xImpl_SDL_Boolean Character SDL_Character NUL SDL_NUL SOH SDL_SOH ... ... (for all unprintable characters) 'a' 'a' 'b' 'b' ... ... (for all printable characters except ' ) '''' '\'' =, /= xEqF_SDL_Character, xNEqF_SDL_Character <, <=, >, >= xLT_SDL_Character, xLE_SDL_Character, xGT_SDL_Character, xGE_SDL_Character, chr xChr_SDL_Character, num xNum_SDL_Character, Charstring SDL_Charstring 'aa' SDL_CHARSTRING_LIT("Laa","aa") =, /= yEqF_SDL_Charstring, yNEqF_SDL_Charstring MkString xMkString_SDL_Charstring Length xLength_SDL_Charstring First xFirst_SDL_Charstring Last xLast_SDL_Charstring // xConcat_SDL_Charstring SubString xSubString_SDL_Charstring Integer SDL_Integer 0, 1 etc. SDL_INTEGER_LIT(0), SDL_INTEGER_LIT(1) etc. + xPlus_SDL_Integer - (monadic, dyadic) xMonMinus_SDL_Integer, xMinus_SDL_Integer * xMult_SDL_Integer / xDiv_SDL_Integer mod xMod_SDL_Integer rem xRem_SDL_Integer =, /= yEqF_SDL_Integer, yNEqF_SDL_Integer <, <=, >, >= xLT_SDL_Integer, xLE_SDL_Integer, xGT_SDL_Integer, xGE_SDL_Integer, Float xFloat_SDL_Integer Fix xFix_SDL_Integer Natural SDL_Natural Real SDL_Real 12.45,... SDL_REAL_LIT(12.45, 12, 450000000) - (monadic, dyadic) xMonMinus_SDL_Real, xMinus_SDL_Real + xPlus_SDL_Real * xMult_SDL_Real / xDiv_SDL_Real =, /= yEqF_SDL_Real, yNEqF_SDL_Real <, <=, >, >= xLT_SDL_Real, xLE_SDL_Real, xGT_SDL_Real, xGE_SDL_Real PId SDL_PId NULL SDL_NULL =, /= yEqF_SDL_PId, yNEqF_SDL_PId Duration SDL_Duration 23.45 SDL_DURATION_LIT(23.45, 23, 450000000) + xPlus_SDL_Duration - (monadic) xMonMinus_SDL_Duration - (dyadic) xMinus_SDL_Duration * (Duration * Real) xMult_SDL_Duration, * (Real * Duration) xMultRD_SDL_Duration / xDiv_SDL_Duration =, /= yEqF_SDL_Duration, yNEqF_SDL_Duration <, <=, >, >= xLT_SDL_Duration, xLE_SDL_Duration, xGT_SDL_Duration, xGE_SDL_Duration Time SDL_Time 23.45 SDL_TIME_LIT(23.45, 23, 450000000) + (Time + Duration) xPlus_SDL_Time, + (Duration + Time) xPlusDT_SDL_Time - (result: Time) xMinusT_SDL_Time - (result: Duration) xMinusD_SDL_Time =, /= yEqF_SDL_Time, yNEqF_SDL_Time <, <=, >, >= xLT_SDL_Time, xLE_SDL_Time, xGT_SDL_Time, xGE_SDL_Time ----------------------------------------------------------------------
---------------------------------------------------------------- Note: For more information about the Charstring sort, see the section "Handling of the Charstring Sort" on page 1996. ----------------------------------------------------------------
Example 62 : Enumeration Type NEWTYPE EnumType LITERALS Lit1, Lit2, Lit3; ENDNEWTYPE;is translated to:
typedef unsigned char EnumType; #define Lit1 0 #define Lit2 1 #define Lit3 2
Example 63 : Struct NEWTYPE Str STRUCT a integer; b Boolean; c real; ENDNEWTYPE;is translated to:
typedef struct { SDL_Integer a; SDL_Boolean b; SDL_Real c; } Str;All the properties of a struct in SDL are preserved in the C code.
The predefined operators Extract! and Modify! are implemented as component selections in the struct in the same way as in SDL, that is, if S is a variable of type Str, then S!a in SDL is translated to S.a in C.
The predefined operator Make!, which is a constructor of a struct value, is implemented by generating a Make function in C. This means that the expression "(. 12, true, 0.22 .)" in SDL is translated to the C function call Make(12, true, 0.22). The Make function will, of course, be given a name that makes it unique.
The components of a struct may be of any sort that the C Code Generator can handle. A component may, however, not directly or indirectly refer to the struct sort itself. As an example the sort Str above may not have a component of sort Str. In such a case the translation to a C struct would not any longer be valid.
Example 64 : Union NEWTYPE Str /*#UNION*/ STRUCT tag integer; a integer; b Boolean; c real; ENDNEWTYPE;is translated to:
typedef struct { SDL_Integer tag; union { SDL_Integer a; SDL_Boolean b; SDL_Real c; } U; } Str;The first component in the struct is assumed to be a tag value indicating which of the union components that are active. The tag should either be integer or an enumeration type. Tag value 0 or first enumeration literal is used to indicate that the first of the remaining components are active, and so on. On the SDL level a #UNION struct should be handled just like any other struct. It is up to the C Code Generator to generate the correct code for operations on the struct, like assignment, test for equality, component selection, and so on.
------------------------------------------------------------------- Note: It is completely up to the user to make certain that only valid com ponents in a #UNION struct are accessed. The code generator does not handle charstring components in an union correctly (or structured types containing charstrings). -------------------------------------------------------------------
If the index sort is a discrete sort, with one closed interval of values, that is of the following sorts:
If the index sort is not one of the sort in the enumeration above, the SDL array is translated to a linked list. The list head contains the default value for all possible indexes, while the list elements contain value pairs, (index_value, component_value), for each index having a component value not equal to the default value.
Example 65 : Array SYNTYPE Syn = integer CONSTANTS 0:10 ENDSYNTYPE; NEWTYPE Arr ARRAY(Syn, real) ENDNEWTYPE;is translated to:
typedef SDL_Integer Syn; typedef struct { SDL_Real A[11]; } Arr;All the properties of an array in SDL are preserved in the C code.
The predefined operators Extract! and Modify! are implemented as component selection of the array in C in the same way as in SDL, so if AVar is a variable of type Arr, and Index is a valid index expression, then AVar(Index) in SDL is translated to AVar.A[Index] in C. In the case of a link list implementation of the array, component selection is made through function calls.
The predefined operator Make!, which is a constructor of an array value, is implemented by generating a Make function in C. This means that the expression "(. 0.22 .)" in SDL is translated to the C function call Make(0.22). The Make function will of course be given a name that makes it unique.
There are two translation schemes for powersets. If the component sort fulfills the conditions for index sorts mentioned an the subsection about arrays above (page 1983) an array of 32-bit integers are used. Each bit will be used to represent if a certain element is member of the powerset or not. If this is not the case, a linked list of all element that are member of the set is used to represent the powerset. All the available operations defined for Powersets in SDL are supported.
The syntype is translated to a type equal to the parent type using typedef. The check that a variable of a syntype is only assigned legal values is implemented in a test function that is generated together with the type definition. An attempt to assign an illegal value to such a variable will be reported as an SDL dynamic error. If the syntype is can be used as index sort in an array and the generated type in C would become an array, there will also be a test function that can be used to check that an index value is within its range in an array component selection.
Example 66 : Syntypes SYNTYPE Syn = integer CONSTANTS 0:10 ENDSYNTYPE; SYNTYPE Syn2 = integer CONSTANTS <0, =2, >=10 ENDSYNTYPE; SYNTYPE Arr1 = Arr DEFAULT (. 2.0 .); ENDSYNTYPE; /* Arr defined above */is translated to:
typedef SDL_Integer Syn; typedef SDL_Integer Syn2; typedef Arr Arr1;
Default values will also be assigned to all variables and components which do not have a default value specified in SDL. The reason for this is to avoid handling undefined variables in C, which might give serious problems and unexpected behavior of an executing program. The values selected by the C Code Generator in such a case can be found below.
--------------------------------------------------------------- Note: This is a deviation from SDL92. It means that the generated pro gram does not handle the value undefined for any type. ---------------------------------------------------------------The following start values are used if no default value is given in the sort and no start value is given in the data definition (DCL):
---------------------------------------------------------------- Sort Start Value ---------------------------------------------------------------- Integer 0 Natural 0 Real 0.0 Time 0.0 Duration 0.0 Character NUL Charstring '' (empty string) Boolean false PId null Enumeration First value in enumeration Struct and 1. Components initialized according to their de Array faults 2. Components initialized according to this table. String and Empty string, empty powerset Powerset Syntypes Default value of father type (newtype) According to this table for father type. ----------------------------------------------------------------
A user defined operator in an SDL sort definition, that is not defined by an operator diagram, is translated to a C function that asks the user for the result of the operation. At a call of an operator the user is supplied with information on what operator in what sort that is called, and with information about the parameter values. You are then requested to answer with the result value. If you press <Return> at the prompt for the result the default value of the actual result type is returned.
Example 67 : Operator Operator Op in sort S is called. Parameter 1: true Parameter 2: 10 Enter value (integer) : 12assuming that newtype S contains an operator
Op: Boolean, Integer -> Integer;
-------------------------------------------------------------- Note: The C Code Generator does not permit naming of literals using name class literals or character strings. --------------------------------------------------------------
It is possible to choose between two alternatives to implement operators and literal functions:
---------------------------------------------------------------------- Note: The C functions are divided into a function heading (extern or static declaration) and a function body. ----------------------------------------------------------------------
Example 68 : Implementing an Operator extern SDL_Integer Max (SDL_Integer Para1, SDL_Integer Para2);while the corresponding function body is:
SDL_Integer Max (SDL_Integer Para1, SDL_Integer Para2) { if (Para1 > Para2) return Para1; return Para2; }The main reason for this division of functions into heading and body is the separate compilation scheme used in C. If, for example, an abstract data type is defined in a system and used in a process in the system, and the process is generated on a separate file, then there has to be a module interface file (a .h file) for the system containing the external interface (types, extern declarations of functions and so on). The interface file should then be included in the file generated for the process.--------------------------------------------------------------------- Note: In all examples of this manual, prototypes as defined in ANSI-C, are used. If the compiler does not support prototypes, the examples should be transformed to the old C-style for functions. ---------------------------------------------------------------------
Even if separate compilation is not used, the division of functions into heading and body is useful. By having static declarations of the functions, the order in which functions must be defined is relaxed. If static declarations were not used, a function could only call the functions that are defined textually before the actual function.
To select the way the C Code Generator should generate code for operators and literals, C Code Generator directives are used. A C Code Generator directive is an SDL comment with the first characters equal to #, followed by a sequence of letters identifying the directive. In this case the letters are ADT (for Abstract Data Type) and OP (for operator). An ADT directive and a OP directive should thus look like:
/*#ADT */ /*#OP */The text is not case sensitive.
OP directives are recognized at two different positions in an abstract data type:
Example 69 : Implementing an Operator (#ADT) NEWTYPE Str STRUCT a integer; b Boolean; c real; ADDING LITERALS Lit1 /*#OP */, Lit2 /*#OP */; OPERATORS Op1 :Str,integer -> Str; /*#OP */ Op2 :Str,Boolean -> Str; /*#OP */ /*#ADT */ ENDNEWTYPE;At each of the positions after a literal name or operator definition, there is a possibility to specify how this literal or operator should be implemented. In the directive immediately before ENDNEWTYPE the default implementation technique can be given. When the C Code Generator determines how to generate code for a literal or an operator, it first looks for an OP directive after the literal name or operator definition. If no such directive is found it looks for a directive immediately before ENDNEWTYPE. If no ADT directive is found here, the generation technique Q (question) is assumed.
An OP or ADT directive specifying a generation technique should have the following structure:
/*#OP (B) */ /*#ADT (B) */The letter between the parentheses should be either Q (question) or B (body). The interpretation of Q and B was explained on page 1988. If B has been specified for any operators or literals, then the C code for these functions must be supplied by the user. This code should be placed in the #BODY section in the ADT directive, according to the following example:
Example 70 : Implementing an Operator (#ADT) /*#ADT (B) #BODY C code, representing bodies of functions */The section name, i.e #BODY, must be given on a line of its own and must have the # character in the first position of the line. Uppercase and lower case letters are as usual considered to be equal. If the section is empty, the section name can also be removed.
--------------------------------------------------------------------- Note: that the C Code Generator will not check the consistency between the specification of implementation techniques and the actual code included in the body section. This check is, together with checking the C code for syntactic and semantic errors, left to the C compiler. ---------------------------------------------------------------------Unfortunately it is not possible to have C comments within the code that is included in a #ADT directive, as SDL and C use the same symbols for start and end of comments. If a C comment is included, the SDT Analyzer will consider the end of the C comment as the end of the SDL comment. Instead a C macro called COMMENT can be used according to the examples below.
Example 71 : Comment in ADT COMMENT(This is a comment) COMMENT(These comments may not contain commas \ and should have a backslash at each \ line break) COMMENT((By having double parenthesis, any text can be entered into the comments. Some compilers might not allow everything.))The function headings representing literals and operators are determined by their corresponding definition in SDL. The number of parameters, their types, the result type of the function and function name are all defined in SDL. In the example above, where the struct Str is defined, there are two literals (Lit1 and Lit2) and two operators
Example 72 : Implementing an Operator extern Str Lit1 (void); extern Str Lit2 (void); extern Str Op1 (Str, SDL_Integer); extern Str Op2 (Str, SDL_Boolean);The function bodies, which should be supplied by the user if B is specified in the OP or ADT directive, are ordinary C functions.
Example 73 : Implementing an Operator Str Lit1 (void) { Str Temp; Temp.a = 2; Temp.b = false; Temp.c = 10.0; return Temp; } Str Op1 (Str yParam1, SDL_Integer yParam2), { yParam1.a = yParam1.a + yParam2; return yParam1; }Before it is possible to give a complete example of an abstract data type with implementation of its operators supplied as C functions, it is necessary to look at the problem of names. When a name of some object in SDL is translated to C, a suitable sequence of characters, a prefix, is added to the SDL name, to make the name unique in the C program, see also "Names and Prefixes in Generated Code" on page 2036. This strategy is selected in the C Code Generator to avoid name conflicts in the generated code, but it makes it also impossible to predict the actual name of, for example, a type or a function, in the generated program. To handle this problem the user can tell the C Code Generator to translate a name in the C code in the same way as SDL names are otherwise translated. This is specified by enclosing the SDL name between #( and ) in the C code. The two functions in the previous example and their headings would then become:
Example 74 : Including SDL name in C Code extern #(Str) #(Lit1) (void); extern #(Str) #(Op1) (#(Str), SDL_Integer); #(Str) #(Lit1) (void) { #(Str) Temp; Temp.a = 2; Temp.b = false; Temp.c = 10.0; return Temp; } #(Str) #(Op1) (#(Str) yParam1, SDL_Integer yParam2) { yParam1.a = yParam1.a + yParam2; return yParam1; }This facility to access an SDL name in C code is described in more detail in the section "Accessing SDL Names in C Code - Directive #SDL" on page 2029. A few observations concerning the example above might be appropriate:
Example 75 : ADT Example NEWTYPE Str STRUCT a integer; b Boolean; c real; ADDING LITERALS Lit1; OPERATORS Op1 : Str, integer -> Str; Op2 : Str, Boolean -> Str; /*#ADT (B) #BODY #(Str) #(Lit1) (void) { return yMake_#(Str) (2,SDL_false,10.0); } #(Str) #(Op1) (#(Str) yParam1, SDL_Integer yParam2) { yParam1.a = yParam1.a + yParam2; return yParam1; } #(Str) #(Op2) (#(Str) yParam1, SDL_Boolean yParam2) { if (yParam2) return yParam1; return #(Lit1)(); } */ ENDNEWTYPE;The only thing in this example which has not been discussed earlier is the use of the make function in the function for literal Lit1. Each struct in SDL has an implicit operator Make!, which is a constructor for struct values from the struct components. The C Code Generator generates a function that corresponds to this operator with the name yMake_ followed by the sort name with prefix. The function has one parameter for each struct component and returns a value of the struct sort. The returned value is the struct value that is obtained when each component is assigned the value of the corresponding parameter. This make function is often very useful when literal functions in struct sorts are to be implemented. In C code the name of the make function in easiest to obtain by writing yMake_#(SortName), substituting the name of the actual struct sort for SortName.
Example 76 : ADT Example SYNTYPE Index = Integer CONSTANTS 1:10 ENDSYNTYPE, NEWTYPE A Array(Index, integer) ADDING LITERALS Zero /*#OP (B) */; OPERATORS Add : A, A -> A; /*#OP (B) */ Sum : A -> integer; /*#ADT() #BODY #(A) #(Zero) (void) { return yMake_#(A) (0); } #(A) #(Add) (#(A) yParam1, #(A) yParam2) { #(Index) I; #(A) Result; for (I = 1; I<=10; I++) Result.A[I] = yParam1.A[I] + yParam2.A[I]; return Result; } */ ENDNEWTYPE;Note that no body is supplied for the operator Sum as the default implementation strategy for operators, which should be used for Sum, is Q (question).
For more information about the functions and types (supplied by the runtime library in the C Code Generator and contained in generated code) that can be useful when implementing operators in C, see "SDL Predefined Types" on page 1976, and last in "More about Abstract Data Types" on page 2000.
extern void xSDLOpError( char *OpName, char *ErrText )can be used for this purpose.
Example 77 : Error Handler in OperatorExample of use:
if ( strlen(C) <= 1 ) { #ifdef XECSOP xSDLOpError("First in sort Charstring", "Charstring length is zero." ); #endif return SDL_NUL; } else return C[1];This is a simplified version of the test in the function for the operator First in the sort Charstring. Here the error situation is when we try to access the first character in a charstring of length 0. In this case the xSDLOpError is called and a default value is returned (NULL). By including the xSDLOpError call between #ifdef XECSOP - #endif the function is only called to report the error, if error checks are turned on. The first parameter to xSDLOpError should identify the operator and the sort, while the second parameter should describe the error.
-------------------------------------------------------------------------- Note: This means that the value NUL (ASCII character 0) cannot be part of a Charstring, as this value is used as string terminator in C (this is checked by the library functions for Charstring). --------------------------------------------------------------------------The C Code Generator and the library functions for the Charstring operators use the first character (index 0) in the C string to indicate the status of the string. If the first character is:
As pointers and dynamic memory are used to implement Charstrings, it is necessary to be careful when Charstrings are handled in C code, which we show in two examples.
Example 78 : Equal Test on Charstring SortIf the C operator == is used to check if two charstrings are equal, then the actual test that is performed is to see if the two pointer values to the data areas representing the characters in the string are equal.
To check if the characters in the charstrings are equal the equal function should be used:
yEqF_SDL_Charstring Example 79 : Assignment on Charstring SortIf the C assignment operator, =, is used to assign the value of one charstring variable (C1) to another charstring variable (C2), then two things will go wrong:
yAssF_SDL_CharstringThe problems mentioned above can of course also occur if a struct or array containing charstring components (or subcomponents) is handled carelessly. It is, for example, necessary to use the generated equal and assign routines to perform equal test and assignment (see subsection "Help Functions" on page 2009).
To avoid problems one should be aware that Charstring is implemented as char * in C and take the consequences thereof. There are a number of help functions (that implement the operators for the Charstring sort) supplied in the runtime library that might be helpful when handling Charstrings. See "SDL Predefined Types" on page 1976).
It is usually necessary to allocate dynamic memory when an operator returning a charstring value is implemented. There are two help functions that should be used in connection with allocation and de-allocation of dynamic memory:
Example 80 : External Properties in a Newtype NEWTYPE Str STRUCT a integer; b Boolean; c real; ADDING LITERALS Lit; OPERATORS Op1 : Str, integer -> Str; Op2 : Str, Boolean -> Str; ALTERNATIVE C; #ADT (B) #BODY some appropriate C code ENDALTERNATIVE; ENDNEWTYPE;The #ADT directive, without the /* */ can be placed between ALTERNATIVE C; and ENDALTERNATIVE.
-------------------------------------------------------------------- Note: According to the syntax of SDL, if you have an external properties clause (i.e. alternative - endalternative), you cannot, in the same newtype, have operator diagrams, axioms, or literal mappings. --------------------------------------------------------------------
For an operator in an abstract data type not only B (body) or Q (question) may be specified. The following choices are available:
---------------------------------------------------------------------- Note: As C does not include the possibility to have user defined operators, I (infix) is only adequate together with S (standard). ----------------------------------------------------------------------
The purpose of S is straight forward and easy to understand, but H might require some explanation. H means that the C Code Generator will not generate any code for the operator, which leaves the user with a number of possibilities:
Example 81 : Using S (Standard Function or Operator)Example of usage of S (standard)
"+" : integer, real -> real; /*#OP (SI) */ sin : real -> real; /*#OP (SP) */An SDL expression using these operators:
sin(a + 7.0) will be translated to: sin(zh723a + 7.0)
These examples show how standard functions in the target language can be directly utilized in abstract data types. In C it is often easiest to use #OP(HP) for such special cases, and implement the operator in the #HEADING section as a C macro transforming the call to the appropriate syntax.
/*#ADT (T(x) A(x) E(x) D(x) F(x) K(x) X(x) M(x) W(x) R(x) xy 'file name') #TYPE C code #HEADING C code #BODY C code */where each x on the first line should be replaced by one of the characters B, H, Q, S, or G. Replace y by P or I. The interpretation of these characters is similar to the their interpretation for operators.
----------- B Body H Heading Q Question S Standard G Generate P Prefix I Infix -----------The reason why G (generate) is not allowed for operators or literals is of course that it would mean to generate the implementation of the operators from the axioms, which is, at least in the general case, an impossible task. For an operator defined in an operator diagram, G is assumed independently of what the user specifies.
The specifications, given in ADT directives, of how to generate code for type definition, assignment, test for equal, default values, and free function should be interpreted according to the table below. More details about the functions can be found in the subsection "Help Functions" on page 2009.
--------------------------------------------------------- Type Interpretation --------------------------------------------------------- T(G) Generate type definition from SDL sort T(B) Do not generate type definition T(H) Generate type from SDL, but place it in a C comment T Same as T(B) - Same As T(G) ---------------------------------------------------------
yAssF_#(SortName)(....);The yAssF_#(SortName) is a macro either implemented as assignment or as a call to the yAss_#(SortName) function (if such function is to be used), i.e as:
#define yAssF_#(S)(V,E,A) V = E #define yAssF_#(S)(V,E,A) yAss_#(S)(&(V),E,A).---------------------------------------------------------- Type Interpretation ---------------------------------------------------------- A(B) Use and generate heading, but not body, of yAss_#(S) A(H) Use, but generate no code for yAss_#(S) A(G) · If the type definition is generated:
yEqF_#(SortName)(....);The yEqF_#(SortName) is a macro either implemented as C equal or as a call to the yEq_#(SortName) function (if such function is to be used), i.e as:
#define yEqF_#(S)(E1,E2) E1 == E2 #define yEqF_#(S)(E1,E2) yEq_#(S)(E1,E2)The /= operator is represented by the macro
#define yNEqF_#(S)(E1,E2) (! yEqF_#(S)(E1,E2) ).------------------------------------------------------------------ Type Method ------------------------------------------------------------------ E(B) Use and generate heading, but not body, of yEq_#(S) E(H) Use but generate no code for yEq_#(S) E(G) · If the type definition is generated:
------------------------------------------------------------- Type Interpretation ------------------------------------------------------------- D(B) Generate heading, but no body of default function
yDef_#(SortName). D(H) Generate neither heading nor body of default function. D(G) Generate both heading and body of default function, or use macro if adequate. D(S) Same as D(G) D Same as D(B) - Same as D(G) -------------------------------------------------------------
------------------------------------------------------------- Type Interpretation ------------------------------------------------------------- F(B) Generate heading, but no body of the free function yFree_#(SortName). F(H) Generate neither heading nor body of the free function. F(S) Use the function xFree F Same as F(B) - Do not use free function. -------------------------------------------------------------
------------------------------------------------------------- Type Interpretation ------------------------------------------------------------- K(B) Generate heading, but no body of the make function yMake_#(SortName). K(H) Generate neither heading nor body of the make function. K(G) Generate the make function K Same as K(B) - Same as K(G) -------------------------------------------------------------
Variable ! Component Variable (Index)An Extract operation can be generated in four ways:
Variable . Component used for struct Variable . U . Component used for #UNION Variable . A (Index) used for array yExtr_SortName(Variable, Expr)The last version, the Extract function, is used for all other cases.
----------------------------------------------------------- Type Interpretation ----------------------------------------------------------- X(B) Use Extract function X(G) Use component selection according to table above. X Same as X(B) - Same as X(G) -----------------------------------------------------------A Modify operation can in the same way be generated in four ways:
Variable . Component used for struct Variable . U . Component used for #UNION Variable . A (Index) used for array (* yAddr_SortName((&Variable), Expr))The last version, the Addr function, is used for all other cases.
----------------------------------------------------------- Type Interpretation ----------------------------------------------------------- M(B) Use Addr function M(G) Use component selection according to table above. M Same as M(B) - Same as M(G) -----------------------------------------------------------
----------------------------------------------------------------- Type Interpretation ----------------------------------------------------------------- W(B) Generate heading but not the body of a write function. W(H) Generate neither heading nor body of a write function, but assume that the user has provided such a function. W(S) Values of this type are to be printed as a HEX string. No write function is assumed to be present. W Same as W(B) - Same as W(S) -----------------------------------------------------------------
------------------------------------------------------------- Type Interpretation ------------------------------------------------------------- R(B) Generate heading but not the body of a read function. R(H) Generate neither heading nor body of a read function, but assume that the user has provided such a function. R(S) Values of this type are to be read as a HEX string. No read function is assumed to be present. R Same as R(B) - Same as R(S) -------------------------------------------------------------When generate is specified for a function, the C Code Generator might decide not to generate the heading of the function, as in some cases it is not needed.
All code that is not generated is, of course, assumed to be included by the user in the #TYPE, #HEADING and #BODY sections in the #ADT directive.
Another name for an assign function, equal function and so on may be used, by including the desired name within quotes together with the generation options in the #ADT directive.
If for example the name of a certain assign function should be AssX, this can be obtained by specifying: A(B 'AssX') for the assign function. This name will then be used throughout the generated code, both in generated declaration and at the places where the function is called. Note that the name should be last in the specification for the actual function.
An include statement may be generated together with or replacing the type definition by giving a file name within quotes last in the specification part of the #ADT directive, immediately before the first section with code.
Example 82 : Including a File in ADTIf the directive
/*#ADT (T(B) A(S) E(S) 'file name') */is used, the following include statement will be generated:
#include "file name"------------------------------------------------------------------- Note: Turning off the generation of the objects contained in the include file must be done by the user. -------------------------------------------------------------------
The #REF directive is used as shown in the example below.
Example 83 : Including a File in ADT newtype s struct a, b, c, d integer; operators eq1 : s, s -> Boolean; eq2 : s /*#REF*/, s /*#REF*/ -> Boolean; endnewtype;The headings for these two operator will become in ANSI-C syntax (ignoring prefixes)
extern SDL_Boolean eq1 (s Param1, s Param2); extern SDL_Boolean eq2 (s *Param1, s *Param2);This feature can be used to optimize parameter passing to operators. The directive, however, also imposes the restriction that the actual parameters must be variable (see example Example 84 below). This is checked by the C Code Generator. A #REF directive does not in any way effect the way a operator call should be implemented in SDL. It is the responsibility of the code generator to generate the proper actual parameters in C.
Example 84 : Including a File in ADTWith the ADT in the previous example the following operator call is valid:
eq1(sVar, (. 1, 2, 3, 4 .) )The same call of eq2 would not be valid as the second parameter is not a variable.
void yAss_#(SortName) (#(SortName) *yVar, #(SortName) yExpr, int AssType)It should assign the value passed as second parameter to the variable passed as first parameter. The SDL predefined sort Charstring, has an assign functions xAss_SDL_Charstring, while all the other predefined types can use =. Sorts translated as enum types can always use =, while arrays (translated to arrays), powersets (translated to bit arrays), and struct sorts can use = if all components can use =. The remaining generators array (translated to linked list), powerset (translated to linked list), and string need assignment functions. A syntype in SDL uses = if its father type can use =, otherwise it uses the assign function of the father type.
The extra third parameter in Assign functions should be given a value according to the ideas given below, preferably using the macros indicated.
SDL_Boolean yEq_#(SortName) (#(SortName) yExpr1, #(SortName) yExpr2);It should return true or false depending on if the two values passed as parameters are equal or not. The SDL predefined sorts Charstring, Time, Duration and PId have equal functions:
xEq_SDL_Charstring, xEq_SDL_Time, xEq_SDL_Duration, xEq_SDL_PId,while all the other predefined types can use ==. Sorts translated as enum types can always use ==, while arrays, stings, powersets, and struct sorts always have equal functions. A syntype in SDL uses == if its father type can use ==, otherwise it uses the equal function of the father type.
void yDef_#(SortName) (#(SortName) *yVar)It should assign the default value to the variable passes as parameter. The default function will in most cases be implemented using a macro (all predefined sorts, all sort with explicit default value, all sorts translated to enum type, all Syntypes). Only array and struct sorts without a default value will have a function as default function.
void yFree_#(SortName) (#(SortName) **yVar)The function should take the address to a pointer, return the allocated memory to the pool of available memory and assign 0 to the pointer. The SDL predefined sort Charstring has a free function:
xFree_SDL_CharstringEach structured type (struct or array) which has a component that has a free function, will have a free function. The usage of the free functions will be discussed in detail in the section "Allocating Dynamic Memory" on page 2260, in chapter 36, The Master Library.
#(ArraySortName) yMake_#(ArraySortName) ( #(ComponentSortName) yExpr ) #(StructSortName) yMake_#(StructSortName) ( #(ComponentSortName1) yComponenName1; #(ComponentSortName2) yComponentName2 )The number of parameters in a struct make function is the same as the number of components in the struct.
extern char * yWri_SortName (void * Value)Given the address of a value of the type SortName, this function should return a char *, i.e. a character string, containing the value represented in a printable form. This character string is the string that will be printed by the monitor, when it needs to print a value of this type. To implement the write function it is not uncommon that a static char array is needed.
----------------------------------------------------------------- Note: The following two considerations when it comes to write and read functions: -----------------------------------------------------------------
#ifdef XREADANDWRITEF #endifto be removed if read and write functions are not needed.
extern char * xWriteSort XPP(( void *In_Addr, xSortIdNode SortNode));The xWriteSort function takes the address of a value to be printed, and a pointer to a xSortIdNode and returns given value as a string. This function is typically useful if the sort we are implementing a write function for, contains one or several components of sorts defined in SDL.
extern int yRead_SortName (void * Result)A read function is passed an address where to store the value that is read. It should return 1 if the read operation was successful. Otherwise, 0 should be returned and Result should be unchanged.
To your help when implementing a read function there are some suitable functions in the run-time kernel. Basically the function xScanToken described below is a tokenizer, that transforms sequences of characters to tokens. This function returns tokens according to the following enum type:
typedef enum { xxId, /* identifiers, numbers */ xxString, /* SDL Charstring literal */ xxSlash, /* / */ xxColon, /* : */ xxMinus, /* - */ xxPlus, /* + */ xxStar, /* * */ xxComma, /* , */ xxSemicolon, /* ; */ xxArrowHead, /* ^ */ xxLPar, /* ( */ xxRPar, /* ) */ xxLParDot, /* (. */ xxRParDot, /* .) */ xxLParColon, /* (: */ xxRParColon, /* :) */ xxDot, /* . */ xxLBracket, /* [ */ xxRBracket, /* ] */ xxLCurlyBracket, /* { */ xxRCurlyBracket, /* } */ xxAt, /* @ */ xxQuaStart, /* << */ xxQuaEnd, /* >> */ xxLT, /* < */ xxLE, /* <= */ xxGT, /* > */ xxGE, /* >= */ xxEQ, /* = */ xxNE, /* /= */ xxQuestionMark, /* ? */ xx2QuestionMark, /* ?? */ xxExclMark, /* ! */ xxSystem, /* system */ xxPackage, /* package */ xxBlock, /* block */ xxProcess, /* process */ xxProcedure, /* procedure */ xxOperator, /* operator */ xxSubstructure, /* substructure */ xxChannel, /* channel */ xxSignalroute, /* signalroute */ xxType, /* type */ xxNull, /* null */ xxEnv, /* env */ xxSelf, /* self */ xxParent, /* parent */ xxOffspring, /* offspring */ xxSender, /* sender */ xxEoln, /* end of line */ xxEOF, /* end of file */ xxErrorToken /* used to indicate error */ } xxToken;
extern xxToken xScanToken XPP(( char * strVar));reads the next token from input (stdin or Simulator UI) and returns the type of the next token as function result. If the token is xxId or xxString the strVar parameter will contain the identifier, number, or string. The size of the char[] parameter passed as actual parameter should be large enough to store the possible values. If some other token was found, no information is stored in strVar.
extern void xUngetToken XPP(( xxToken Token, char * strVar));The functions below can also be useful while implementing Read function. xPromptQuestionMark is suitable to obtain prompt in a similar way as for SDL defined sorts, while xReadOneParameter can be used to read element for element in a list, separated by commas and terminated either by ".)" or "]". The function xReadSort is similar to xWriteSort and can be used to read a component in the treated sort.
extern xxToken xPromptQuestionMark XPP(( char * Prompt, char * QuestionPrompt, char * strVar));The function result and the parameter strVar behave in the same way as for the function xScanToken (see above). The parameter Prompt should be the prompt that should be used. This string have to start with a ` `, i.e. a space. To conform with other built-in read function, the Prompt parameter should be: " (SortName)". The QuestionPrompt parameter should either be identical to the Prompt parameter, or be null, i.e.
extern int xReadOneParameter XPP(( xSortIdNode SortNode, char * Prompt, void * Result_Addr));The xReadOneParameter can be used to read one component in a structured data type. It is required that components are separated by commas or just white spaces, and that the component list is ended with either ".)" or "]". For an example of how to use this function, please see the second example below. xReadOneParameter returns 0 if an error was found, 1 if the read operation was successful and there are more components to read, and 2 if the read operation was successful and the next token is the ending ".)" or "]".
extern int xReadSort XPP(( void * Result_Addr, xSortIdNode SortNode));This function reads a value of the sort referenced by the SortNode parameter. If the read operation was successful, 1 is returned and the read value is stored at the address given as parameter. If an error was detected 0 is returned.
Example 85 : ADT Example, Byte TypeThis example is taken from the ADT byte (see "Abstract Data Type for Byte" on page 2091 in chapter 35, The ADT Library). The byte type should be read and printed using HEX format.
#ifdef XREADANDWRITEF static char yCTmp[20]; #ifndef XNOPROTO extern int yRead_byte( void *Result ) #else extern int yRead_byte( Result ) void *Result; #endif { unsigned temp; xxToken Token; Token = xPromptQuestionMark(" (byte) : ", " (byte) : ", yCTmp); if (Token == xxId && sscanf(yCTmp, "%X", &temp) >= 1) { *(byte *)Result = (byte)temp; return 1; } xPrintString("Illegal byte value\n"); return 0; } #ifndef XNOPROTO extern char *yWri_byte( void * C) #else extern char *yWri_byte( C ) void * C; #endif { sprintf(yCTmp, "%0.2X", *(byte *)C); return yCTmp; } #endif Example 86 : ADT Example, Struct Read and Write FunctionsThis is an example of how read and write functions for a struct with two components can look like. The monitor system can handle reading of writing of struct values automatically, so please see this just as an example.
newtype struct1 /*#NAME `struct1' */ struct a,b Integer; /*#ADT(RW) #BODY #ifdef XREADANDWRITEF static char CTemp[500]; #ifndef XNOPROTO extern char * yWri_struct1 (void *In_Addr) #else extern char * yWri_struct1 (In_Addr) void *In_Addr; #endif { strcpy(CTemp, "(. "); strcat(CTemp, xWriteSort((void *) (&((struct1 *)In_Addr)->a), xSrtN_SDL_Integer) ); strcat(CTemp, ", "); strcat(CTemp, xWriteSort((void *) (&((struct1 *)In_Addr)->b), xSrtN_SDL_Integer) ); strcat(CTemp, " .)"); return CTemp; } #ifndef XNOPROTO extern int yRead_struct1 (void *Result_Addr) #else extern int yRead_struct1 (Result_Addr) void *Result_Addr; #endif { xxToken Token; Token = xPromptQuestionMark(" (struct1) : ", (char *)0, CTemp); if (Token != xxLParDot && Token != xxQuestionMark && Token != xxEoln) { xPrintString("Value should start with (.\n"); return 0; } if (Token != xxLParDot) xUngetToken(Token, CTemp); if (! xReadOneParameter(xSrtN_SDL_Integer, " a (integer) : ", (void *)( &((struct1 *)Result_Addr)->a)) ) { xPrintString("Error in component a\n"); return 0; } if (! xReadOneParameter(xSrtN_SDL_Integer, " b (integer) : ", (void *)( &((struct1 *)Result_Addr)->b)) ) { xPrintString("Error in component b\n"); return 0; } Token = xScanToken(CTemp); if (Token == xxRParDot || Token == xxQuestionMark || Token == xxEoln) { if (Token == xxEoln) xUngetToken(Token, CTemp); return 1; } xPrintString("Value should end with .)\n"); return 0; } #endif */ endnewtype;
A directive has the general structure:
Example 87 : #OP DirectiveTake as an example the directive:
/*#OP (B) */.This comment will be recognized as a directive only if no other character is inserted in the sequence /*#OP. After this part, spaces and carriage returns may be inserted freely.
---------------------------------------------------------------------- Note: Instantiations cannot be separated. If #SEPARATE directives are used, they should be placed directly after the first semicolon in the system, block, process, or procedure heading; see the following ex ample. ----------------------------------------------------------------------
Example 88 : #SEPARATE Directive system S; /*#SEPARATE 'filename' */ block B; /*#SEPARATE */ process type P1 inherits PType; /*#SEPARATE */ process P2 (1, ); /*#SEPARATE */ procedure Q; /*#SEPARATE */In the example above the two versions of separate directive, with or without file name, are shown. As can be seen a file name should be enclosed between quotes. The C Code Generator will append appropriate extensions to this name when it generates code.
If no file name is given in the directive, the name of the system, block, process, or procedure will be used to obtain a file name. In such case the file name becomes the name of the unit with the appropriate extension (.c .h) depending on contents. The file name is stripped from characters that are not letters, digits or underscores.
The possibility to set up full, user-defined, or no separation in the Organizer's Make dialog (see page 1112) and in the user-interface of a stand-alone Analyzer (see page 1541), can be used to in simple manner select certain default separation schemes. This setting will be interpreted in the following way:
Example 89 : #SEPARATE Directive system S; /*#SEPARATE 'Sfile' */ block B1; /*#SEPARATE */ process P11; /*#SEPARATE 'P11file' */ process P12; block B2; process P21; process P22; /*#SEPARATE */
-------------------- Sfile.c Sfile.h B1.c B1.h P11file.c P12.c B2.c B2.h P21.c P22.c --------------------The .c files contain the C code for the corresponding SDL unit and the .h files contain the module interfaces.
------------------------------------------------------ Sfile.c Sfile.h Contains code for units S, B2, P21 B1.c B1.h Contains code for units B1, P12 P11file.c Contains code for unit P11 P22.c Contains code for unit P22 ------------------------------------------------------The user defined separate generation option thus makes it possible for a user to completely decide the file structure for the generated code. The comments on files and extensions given above are, of course, also valid in this case.
------------------------------------- Sfile.c Contains code for all units -------------------------------------The comments on files and extensions earlier are valid even here.
------------------------------------------------------------------ Note: A file name has to be specified, in the Organizer Edit Separation command, see page 1123 in chapter 22, The SDT Organizer or in the #SEPARATE directive, if two units in the system have the same name in SDL and should both be generated on separate files, other wise the same file name will be used for both units. ------------------------------------------------------------------
The first step is to enable partitioning. This is performed in the Make dialog in the Organizer (see page 1112 in chapter 22, The SDT Organizer) or in SDL-PR by a #PART directive introduce according to the following example:
SYSTEM Example; /*#PART*/Next, should be decided what possibility to have, when it comes to forming partitions. Use separate generation to obtain the appropriate C files. Note that processes generated on the same file must be part of the same partition.
When the partitioning is enabled, each separate unit (except the system unit) will be represented by two c files:
unitname.c and e_unitname.c
where the file unitname.c is the ordinary file for the SDL object or objects. The file e_unitname.c is a file that should be used to indicate that this SDL object and all objects below it should not be part of this partition. The e_unitname.c file, which is an almost empty file, contains the appropriate code to redirect channels and signal route for this SDL object to the environment.
The partitioning and the separate generation have now made it possible to obtain several partitions. Now it is up to the user to decide which partitions or programs that should be created. This information is not needed until the linking of the programs, so in should be entered by changing the link script in the generated make file.
Example 90 : Partitioning a SystemBelow the demon game system will be used as example of how to handle partitions. The structure of this example is:
SYSTEM demongame BLOCK DemonBlock PROCESS Demon BLOCK GameBlock PROCESS Main PROCESS GameThe beginning of the generated make file for this example is given below, assuming that full modularity has been selected.
# makefile for System: demongame include $(sctdir)/makeoptions default: demongame$(sctEXTENSION) demongame$(sctEXTENSION):demongame$(sctOEXTENSION) \ DemonBlock$(sctOEXTENSION) \ Demon$(sctOEXTENSION) \ GameBlock$(sctOEXTENSION) \ Main$(sctOEXTENSION) \ Game$(sctOEXTENSION) $(sctLD) $(sctLDFLAGS) demongame$(sctOEXTENSION) \ DemonBlock$(sctOEXTENSION) \ Demon$(sctOEXTENSION) \ GameBlock$(sctOEXTENSION) \ Main$(sctOEXTENSION) \ Game$(sctOEXTENSION) \ $(sctLINKKERNEL) \ -o demongame$(sctEXTENSION)The part shown above contains the include of the makeoptions file, the default action, and the link script. Following this, there will be the compilation scripts for all the c files. The generated link script will link all units into the same program, that is, the default behavior of the make file is the same as before, making one program from an SDL system.
To create a partition only containing the Main process (and not the Game and Demon processes), the link script becomes:
Example 91 : Partitioning a System default: mainproc$(sctEXTENSION) mainproc$(sctEXTENSION): demongame$(sctOEXTENSION) \ e_DemonBlock$(sctOEXTENSION) \ GameBlock$(sctOEXTENSION) \ Main$(sctOEXTENSION) \ e_Game$(sctOEXTENSION) $(sctLD) $(sctLDFLAGS) demongame$(sctOEXTENSION) \ e_DemonBlock$(sctOEXTENSION) \ GameBlock$(sctOEXTENSION) \ Main$(sctOEXTENSION) \ e_Game$(sctOEXTENSION) \ $(sctLINKKERNEL) \ -o mainproc$(sctEXTENSION)First the name of the executable program ought to be changed to indicate that it does not contain the complete system any longer. Here mainproc has been selected as a new name. Then, as nothing in the DemonBlock is to be part of the partition, DemonBlock.o is changed to e_DemonBlock.o in two places and all references to units defined in the DemonBlock (process Demon) are removed. The GameBlock.o file should still be used as some object in the block (process Main) is part of the partition. Main.o should, of course, also be used, but Game.o should be replaced by e_Game.o. Now, we have a complete make file that will generate a program called mainproc.sct, which is a simulation or application only containing the process Main. The same way of defining a partition can be used also when working with the SDT Validator, making it possible to validate the behavior of only a part of a system.
Another example of a link script would be to generate two partitions, one containing the processes Main and Demon and the other partition containing the process Game:
Example 92 : Link Script for Partition default: maindemon$(sctEXTENSION) \ game$(sctEXTENSION) maindemon$(sctEXTENSION):demongame$(sctOEXTENSION) \ DemonBlock$(sctOEXTENSION) \ Demon$(sctOEXTENSION) \ GameBlock$(sctOEXTENSION) \ Main$(sctOEXTENSION) \ e_Game$(sctOEXTENSION) $(sctLD) $(sctLDFLAGS) demongame$(sctOEXTENSION) \ DemonBlock$(sctOEXTENSION) \ Demon$(sctOEXTENSION) \ GameBlock$(sctOEXTENSION) \ Main$(sctOEXTENSION) \ e_Game$(sctOEXTENSION) \ $(sctLINKKERNEL) \ -o maindemon$(sctEXTENSION) game$(sctEXTENSION): demongame$(sctOEXTENSION) \ e_DemonBlock$(sctOEXTENSION) \ GameBlock$(sctOEXTENSION) \ e_Main$(sctOEXTENSION) \ Game$(sctOEXTENSION) $(sctLD) $(sctLDFLAGS) demongame$(sctOEXTENSION) \ e_DemonBlock$(sctOEXTENSION) \ GameBlock$(sctOEXTENSION) \ e_Main$(sctOEXTENSION) \ Game$(sctOEXTENSION) \ $(sctLINKKERNEL) \ -o game$(sctEXTENSION)During the start up of an executable program formed from a partition, the channels and signal routes will be redirected in such a way that any path going to a unit that is not part of the partition will be redirected to the environment. This means also recomputing the signalset of all channels and signal routes. The consequence in the monitor interface of the redirection of paths, is that when executing a simulation of a partition, all signals that usually come from other parts of the system (not in this partition), can now be sent from the environment, using the commands OutputVia and OutputTo.
---------------------------------------------------------------------- Note: Signals sent using OUTPUT without to and which should go to a process not in the partition, will be sent to the environment, as the channels and signal routes are redirected in that way. ----------------------------------------------------------------------
To handle the system internal interfaces, .ifc files are generated not only for the system, but for also all units that have separate subunits. In this way, the relevant information is available for any partitioning that the user selects. A partition will have the same properties as the program for an SDL system described in chapter 9, Building an Application, except that the partition may have to include several .ifc files to obtain all the information necessary to implement the interface.
#define SDL_Name Name_Used_In_CSuch macros are generated to simplify writing the interface code and they can be found for signals, channels, signal routes, synonyms, sorts, and literals for sorts translated to enum types in C. This means that if the same name is used for two of these objects in SDL, C macros are subject to cause problems (redefinition of macro).
To be able to write C code and use the name of SDL objects in that code, the C Code Generator provides the directive #SDL which is used in C code to translate an SDL name to the corresponding C name.
The syntax of the #SDL directive is as follows:
#SDL (SDL name)or
#SDL (SDL name, entity class name)There is also a short form for the directive. No characters are allowed between the # character and the left parentheses in this form:
#(SDL name)or
#(SDL name, entity class name)Replace SDL name with the name of an object in the SDL definition and entity class name by any of the following identifiers (upper and lower case letters are considered to be equal):
------------------------------------------- block newtype signal blockinst operator signallist blocksubst package signalroute blocktype predef sort (= newtype) channel procedure state channelsubst process synonym connect processinst syntype formalpar processtype system gate remoteprd systemtype generator remotevar timer imported service variable label serviceinst view literal servicetype -------------------------------------------This list contains all entity classes, which means that not all of the entries are relevant for practical use. When a #SDL directive is found in included C code, the C Code Generator first identifies what SDL object that is referred and then replaces the directive by the C name for that object. The search for the SDL object starts in the current scope (the scope where the C code is included), and follows the scope hierarchy outward to the system definition, until an appropriate SDL object is found. An appropriate SDL name is considered to be found if it has the specified name and is in the specified entity class. If no entity class name is given the search is performed for all entity classes.
--------------------------------------------------------------------- Note: In types, especially in block types, #SDL should be used with care. The reason is that some of the objects in a block type are generated for each instantiation of the block. A #SDL directive on such an ob ject might lead to overloading of names in C. Sensitive objects are processes, process instantiations, signal routes, channels, remote definitions. ---------------------------------------------------------------------The table in the subsection "SDL Predefined Types" gives the direct translation between an SDL name and the corresponding C name or expression (see page 1976). For these names the #SDL directive should not be used.
/*#CODE C code that should be included in generated code */Type the directive name on the first line and the C code on the following lines up to the end of comment symbol. Note that text on the same line as the #CODE directive are not handled.
A #CODE directive can be placed:
------------------------------------------------------------------ Note: The C Code Generator handles the C code in directives as text and performs no check that the code is valid C code. ------------------------------------------------------------------The code directive is included as a facility in the C Code Generator to provide experienced users an escape possibility to the target language C. This increases the application range of the C Code Generator.
An example of a possible use of the code directive is: An algorithm for some computation, which in the SDL description is only indicated as a task with an informal text, could be implemented in C. In this case the directive #SDL described in the previous subsection will probably become useful to access variables and formal parameters defined in SDL.
Some general hints on how to write C code that can be included into a simulation program, especially when charstrings or sorts containing charstrings as components are used, can be found in the last part of the section "Implementation of User Defined Operators" on page 1988.
Unfortunately it is not possible to have C comments within the code that is included in any directive, as SDL and C use the same symbols for start and end of comments. See also Example 71 on page 1991 which illustrates the possibility to use the C macro COMMENT.
/*#CODE #TYPE C code containing: Types and variables #HEADING C code containing: Extern or static declarations of functions #BODY C code containing: Bodies of functions */The separation of functions into HEADING and BODY sections serves the same purpose as in the #ADT directive, see "Implementation of User Defined Operators" on page 1988.
Code directives to include C declarations may, generally speaking, be placed immediately after a semicolon that ends a declaration in SDL. More precisely it is allowed to place a #CODE directive after the semicolon that ends:
Example 93 : #CODE Directive system s; *1 signal s1, s2(integer); *2 channel c1 from env to b1 with s1, s2; *3 newtype n ... endnewtype n; *4 block b1; *5 signalroute sr1 from env to p1 with s1, s2; *6 connect c1 with sr1; *7 process p1 (1,1); *8 signalset s1, s2; *9 dcl a n; *10 start; ... state ...; ... endprocess p1; *11 endblock b1; *12 endsystem s1;A code directive is considered to belong to the unit where it is defined and the declarations within the directive are thus placed among the other C declaration for that unit. In the example above, directives at positions 1, 2, 3, 4, 12 belong to system s, directives at positions 5,6,7,11 belong to block b1, while directives at positions 8, 9, 10 belong to process p1. Only one code directive may be placed at each available position.
------------------------------------------------------------------------ Note: A variable declared in a #CODE directive that belongs to a process will be shared between the process instances of the process type. Such a variable should only be used to represent some common property of all the process instances. To have a variable that is local to a process instance, the variable should be defined in SDL using DCL. ------------------------------------------------------------------------In the generated code the type sections are included in the order of appearance in SDL. However, the type sections are also sensitive for their relative position comparing with SDL sort definitions. This means that the order of the type definitions in the system in the example above will be as follows:
The general hints on how to write C code that fits into a generated C program given in the section "Implementation of User Defined Operators" on page 1988 and in the section "Accessing SDL Names in C Code - Directive #SDL" on page 2029 are also applicable here.
#CODE : Charstring -> S;where S is replaced by the sort name. This operator or rather these operators make it possible to access variables and functions defined in C using the #CODE directive in SDL expressions and still have syntactically and semantically correct SDL expressions.
During code generation, the C Code Generator will just copy the Charstring parameter at the place of the #CODE operator.
Example 94 : #CODE DirectiveSuppose that x and y are SDL variables, which are translated to z72_x and z73_y, that a and b are C variables, and f is a C function defined in #CODE directives.
----------------------------------------- SDL expression C expression ----------------------------------------- x + #CODE('a') z72_x + a x + #CODE('a*b') z72_x + a*b x*#CODE('(a+b)')*y z72_x*(a+b)* z73_y #CODE('f(a,#SDL(x))') f(a, z72_x) -----------------------------------------
Within the Charstring parameter of a #CODE operator the #SDL directive is available in the same way as in other included C code. This is also shown in the last of the examples above.
As there is one #CODE operator for each sort in the system, it is sometimes necessary to qualify the operator with a sort name to make it possible for the SDL Analyzer to resolve which operator that has been used. If, for example, the question and all answers in a decision are given as applications of #CODE operators, then it is not possible to determine the type for the decision. One of the #CODE operators should then be qualified with a sort name to resolve the conflict.
Example 95 : Code Directive DECISION #CODE('a'); (#CODE('1')) : TASK ...; (#CODE('2')) : TASK ...; ENDDECISION;In this case the sort of the decision cannot be resolved. To overcome this problem the question could be written as
DECISION TYPE integer #CODE('a');
A generated name for an SDL object contains four parts in the following order:
The sequence of characters that make the name unique is determined by the position of the declaration in structure of declarations in the system:
If, for example, a sort is defined as the 5th declaration in a block that in turn is the 12th declaration in the system, then the total sequence will be b4 (if not more than 36 declarations are present on any of the two levels).
Example 96 : Generated Names in CodeExamples of generated names:
----------------------------------------------------------- SDL Position of the Declaration Generated Name Name ----------------------------------------------------------- S1 10th declaration in the system z9_S1 Var2 3rd declaration in the process, which is ze42_Var2 the 5th declaration in the block, which is the 15th declaration is system -----------------------------------------------------------
There will also be other generated names using the prefixes. If, for example, a sort MySort is translated to za2c_MySort, then the equal function connected to this type (if it exists) will be called yEq_za2c_MySort.
-------------------------------------------------------------------- Note: If the OO diagram types in SDL-92 are used (system type, block type, process type), full prefix should always be used, as the OO concepts in itself most likely mean the name conflicts will be intro duced in C. --------------------------------------------------------------------This strategy for naming objects in the generated code should be used in all normal situations, as it guarantees that no name conflicts occur. The C Code Generator offers, however, possibilities to change this strategy. In the Make dialog in the Organizer (see chapter 22, The SDT Organizer, on page 1112) and in the user interface an Analyzer running stand-alone (see "Analyzer Command-Line UI" on page 1541 in chapter 31, The Analyzer), it is possible to select one of the following strategies: full prefix, entity class prefix, no prefix, or special prefix. Full prefix is default and is the strategy described above.
------------------------------------------------------------- Entity class Prefix Entity class Prefix ------------------------------------------------------------- Block, block type, blo Process, Process type, prs block instance Process instance Block substructure bls Remote procedure rpc Channel cha Remote variable imp Channel substructure chs Service, Service type, ser Service instance Connection con Signal sig Formal parameter for Signal list sil Gate gat Signal route sir Generator gen Sort sor Import imp State sta Label lab Syntype syt Literal lit Synonym syo Operator ope System, System type sys Package pac Timer tim Predef pre Variable var Procedure prd View vie -------------------------------------------------------------Using entity class prefix means that the user must guarantee that no name conflict occurs. It also means, however, that the generated names are predictable and thus simplifies writing C code where the SDL names are used. It is only needed to look for name conflicts within entity classes, for example not having two sorts with the same name. The entity class prefixes handle the case when two objects of different entity class have the same name. Note that the table above contains all entity classes. Not all of the items are actually used by the C Code Generator.
This strategy normally works very well, as each occurrence of a C name is generated in the same way and as uniqueness of the C identifiers are obtained using the prefixes. If you, however, are about to use the SDL name in included C code, in for example a #CODE directive, then you need to know how upper and lower case letters are used in the C name. This might be a problem if there are multiple occurrences of the name using different combinations of upper and lower case letters. Using the #SDL directive solves this problem in most cases.
As a solution for other potential problems concerning upper and lower case letters in C names, the C Code Generator has an option to translate all SDL names to lower case. Such a translation scheme can be selected in the Make dialog in the Organizer (see "Make" on page 1112 in chapter 22, The SDT Organizer) or in the user interface of the Analyzer, when it is executed stand-alone (chapter 31, The Analyzer, on page 1541).
Example 97 : #NAME Directive NEWTYPE S /*#NAME 'S' */ STRUCT a integer; b Boolean; ADDING OPERATORS Op /*#NAME 'OtherName' */ : S, S -> Boolean; ENDNEWTYPE;The name defined in a #NAME directive will be used everywhere that the SDL name is used in the generated code, with two exceptions:
Example 98 : #PRIO Directive in process headings . Process P1; /*#PRIO 3 */ Process P2(1,1); /*#PRIO 5 */ Process P3 : P3Type; /*#PRIO 3 */ Process P4(1,1) : P4Type; /*#PRIO 5 */Processes that do not contain any priority directive will have the default priority 100.
Example 99 : #MAIN Directive System S; /*#MAIN C code for initialization */The #MAIN directive has exactly the same structure as the #CODE directive for including code in tasks. The included code will, however, be placed in the C function main, after the initialization of the internal structure, but before any transitions are executed.
The directive #EXTSIG is used to replace the code for an SDL output with any appropriate in-line C code. This is an optimization and an alternative to the OutEnv function (see chapter 9, Building an Application). The #EXTSIG directive can be specified:
Example 100 : #EXTSIG Directive signal Signal1 /*#EXTSIG */, Signal2(integer) /*#EXTSIG */; output Signal3 To Sender /*#EXTSIG */;For each output of a signal with a #EXTSIG directive (in either way described above) the following code is generated:
#ifndef EXT_SignalName "the normal implementation of an output" #else EXT_SignalName( SignalName, ySigN_SignalNameWithPrefix, ToExpression, SignalParameters) #endifwhere SignalName is the name of the signal in SDL. The parameter ToExpression is a translated version of the SDL expression after TO in the output. If no TO expression is given in the output, this parameter will be xNotDefPId. The entry SignalParameters will be replaced by the list of signal parameter values given in the output.
The intension of this code is to give the user a possibility to introduce a macro with the same name as the signal, where the implementation of the output is expanded to in-line code. By just having a compilation switch which governs if this macro is visible or not, the same generated code can be used both for simulation and for highly optimized applications. An appropriate switch is probably XENV, which governs the normal way of connecting an SDL system to the environment.
Example 101 : #CODE DirectiveThe following #CODE directive can be included in a text symbol in the system diagram (assuming a signal called SigName with one parameter).
/*#CODE #TYPE #ifdef XENV #define EXT_SigName(Name, IdNode, ToExpr, Param1) \ suitable macro code #endif */The other two directives, #ALT and #TRANSFER, can be used together with appropriate real-time operating systems, to have two different interpretations of an output (internal or external output for example) and to specify that a received signal should be immediately retransmitted (#TRANSFER). These kinds of features are not uncommon in real-time operating systems, and can be modeled and simulated by the C Code Generator using these directives. Both these directives should be placed last in the output symbol.
The presence of an #ALT directive will be reflected in the generated code in the way described below.
The presence of a #TRANSFER directive indicates that a signal should be directly retransmitted to some other receiver. This can of course be performed in SDL by an input-output, but then it is necessary to create a new signal and copy the contents of the received signal to the new signal. Using the #TRANSFER directive this copying can be avoided.
In generated code the macros
------------------------------------------------------------------ Note: In the master library #TRANSFER will still be implemented as a signal copy. It may be possible in an OS integration to avoid the copying if the OS supports such actions. ------------------------------------------------------------------
INPUT signal1(,,,); OUTPUT signal1(,,,) /*#TRANSFER*/;That is, receive none of the signal parameters in the input and retransmit the signal unchanged. If you want to receive, for example, the second parameter (in variable Var1) and retransmit the signal unchanged except for parameter 3, that should have a new value (73), the following code can be used:
INPUT signal1(,Var1,,); OUTPUT signal1(,,73,) /*#TRANSFER*/;
------------------------------------------------------------------- Note: This feature is only valid for SDL-PR as input, when the Analyzer is executed stand-alone. Similar features are available in the Orga nizer's Make dialog among the Generate makefile options, see page 1115 in chapter 22, The SDT Organizer. -------------------------------------------------------------------You can tell the C Code Generator that a number of user defined and precompiled units should be linked together with a generated simulation program. You do this in a #WITH directive that should be placed in the system definition directly after the system heading.
Example 102 : #WITH Directive System S; /*#WITH 'file1.o' 'file2.o' */Within the #WITH directive the object files that are to be part in the link operation should be given between quotes, as in the example above. These files will be included in the definition of the link operation in the generated .m file.
The make file will, however, not include any definition of how to compile the corresponding source files, as it is impossible for the C Code Generator to know the compilation options or even what compiler the user wants. A user that knows how to interface routines in other languages in a C program, can with this knowledge and the #WITH directive link modules written in another language together with the generated program.
-------------------------------------------------------------------- Note: The #WITH directive will only affect the generated make file. There will be no change in the generated C code when a #WITH directive is introduced. --------------------------------------------------------------------
Example 103 : #ID DIrective /*#ID 'Identification of task' */The C Code Generator will use the first ID directive it finds in a TASK (if any). An ID directive may be placed:
--------------------------------------------------------------------- Note: This section is only applicable if you have compiled versions of the runtime library that can be linked together with the object files pro duced by the C++ compiler. In most cases this means that you must have the SDT Master Library, i.e. have source code for the runtime library. ---------------------------------------------------------------------
Example 104 : Using C++ ClassesCODE directive containing declarations (see page 2032) which should be placed among the SDL declarations:
/*#CODE #HEADING class TEST { public: void putvar(int avar, int bvar) {a = avar; b = bvar;} int geta() {return a;} int getb() {return b;} private: int a,b; } TESTvar; */Example of usage of the class in a CODE directive in a TASK (see page 2031).
TASK '' /*#CODE TESTvar.putvar(#(I), #(I)+10); */;Example of usage of the class in a CODE operators in expressions (see page 2035).
OUTPUT Score( #CODE('TESTvar.geta()'), #CODE('TESTvar.getb()'), #CODE('#(TClass)->getVar()') );
If you have a C++ class (from a class library or developed specifically for the project), then the following correspondence rules can be used to map the class on an abstract data type.
----------------------------------------------------- C++ SDL ----------------------------------------------------- Class definition Abstract data type definition Class instance pointer Process variable Member functions Operators new, delete Operators -----------------------------------------------------
Example 105 : SDL and C++ ClassSuppose we have a C++ class with the following interface (.h file):
class TestClass { public : TestClass (int); TestClass (); ~TestClass (); int updateVar(int); int getVar(); private : int v; };then the following abstract data type can be used to represent the class (in the example the NAME directive, see page 2041, is used to instruct the C Code Generator which name to use in C for particular SDL objects):
NEWTYPE TestClass /*#NAME 'TestClassPtr' */ LITERALS newTestClass /*#NAME 'new1TestClass' */ ; OPERATORS newTestClass /*#NAME 'new2TestClass' */ : integer -> TestClass; deleteTestClass /*#NAME 'deleteTestClass' */ : TestClass -> TestClass; updateVar /*#NAME 'updateVar' */ : TestClass, integer -> integer; /*#OP(HC) */ getVar /*#NAME 'getVar' */ : TestClass -> integer; /*#OP(HC) */ /*#ADT(T A(S) E(S) D(H) H P) #TYPE #include "TestClass.h" typedef TestClass * TestClassPtr; COMMENT((NOTE! SDL data type TestClass is pointer to C++ class TestClass)) #HEADING #define yDef_TestClassPtr(p) *(p) = 0 #define new1TestClass() new TestClass() #define new2TestClass(P) new TestClass(P) extern TestClassPtr deleteTestClass (TestClassPtr); #BODY extern TestClassPtr deleteTestClass (TestClassPtr P) { delete P; return (TestClassPtr)0; } */ ENDNEWTYPE;Note that the #ADT specification means that no code will be generated for the abstract data type. The abstract data type can utilized in the following way:
DCL TClass TestClass, I Integer; TASK TClass := newTestClass; TASK TClass := newTestClass(2); TASK I := updateVar(TClass, I); TASK I := getVar(TClass); TASK TClass := deleteTestClass(TClass);The only feature that is not described before is the C option in the #OP directive. C (class) is an alternative to I (infix) and P (prefix), and specifies that the operator call should be translated to a member function call of a C++ member function. #OP(C) means that an operator call
F(a, b, c)
is translated to
a->F(b, c)
-------------------------------------------------------------- Note: This means that each operator mapped on class member function should have the class instance pointer as first parameter. --------------------------------------------------------------