This section details the syntax for creating headers and source code that Trick can process.
It also details the operation of the Trick Interface Code Generator (ICG) that processes headers, and the Module Interface Specification Processor (MIS) that processes source code.
The majority of model source for simulations is written in C and C++. Trick supports auto generating IO code to peek and poke C and C++ structures, classes, and enumerations. Trick also generates the necessary makefile rules to compile and link C and C++ model code into the simulation.
Models written in other languages may be included in the simulation. It is possible to include Fortran 77, Fortran 90, Ada, and/or Java code in the simulation. These models cannot be called directly from the Trick scheduler, but may be called through C language wrapper functions provided by the user that executes the other language calls.
Trick processes header files in order to auto generate IO source code for the simulation. IO source code is the heart of how Trick does its input processing. The following describes the syntax for a header file.
If a header file has a C++ extension (e.g *.hh ) Trick’s parsers will realize that it is a C++ file and handle it appropriately. If the extension is *.h, Trick will assume it is a C file (not C++). If you want to make a C++ header file name with the *.h extension, you must explicitly tell Trick it is a C++ file with the `LANGUAGE: (C++)` declaration in the Trick comment header.
If `ICG: (No)` is in the comment header, Trick will not to process the header. This is useful if the header contains anything that Trick cannot process, or if the programmer wishes Trick to skip this header. For skipping entire sets of headers, see next item.
If `ICG: (Nocomment)` is in the comment header, Trick will not process any comments within the file. This option is useful if the user wants ICG to process the file but the file does not have comments that are Trick compliant.
For references to objects outside the current source directory, the directory paths must be specified relative to the bin_${TRICK_HOST_CPU} directory. In this example, the that.c function might also have its own library dependency list, but the that.c dependencies do not need to appear in the this.c dependency list. The CP will automatically search and sort all the object code dependencies; the developer just has to make sure all dependencies are listed in the appropriate files.
There are two ways to specify dependencies to actual libraries, i.e. lib\*.a files:
If you use a relative path to the library, Trick will search the TRICK_CFLAGS for a directory that contains source code for the library. Once Trick locates the source code, it will automatically build the library and link it in the simulation.
If you do NOT specify a relative path, Trick will NOT build the library for you. It will simply search your -L paths in your `TRICK_USER_LINK_LIBS` for the library. If found, it will link the library into the simulation.
You may also have Trick link in a shared (`lib*.so`) library. You must supply the `*.so` extension. Trick will not automatically build a shared library, but it is smart enough to use it during link time.
The LIBRARY DEPENDENCY field also handles the `#ifdef`, `#else` and `#endif` statements such that different object files and libraries may be linked for different cases. The previous example might look like this:
The `ICG IGNORE TYPES` field lists the structs or classes to be ignored. Any parameters of this type or inherited from are ignored. The `ICG IGNORE TYPES` field is only valid for the current file. It does not extend to included header files.
Specifying a `python_module` name will place any class/struct and function definitions in this header file in a python module of the same name. All classes and functions are flattened into the python `trick` namespace by default. This capability allows users to avoid possible name collisions between names when they are flattened. An empty `python_module` statement will be ignored.
Trick handles all compiler directives (`#if`, `#ifdef`, `#endif`, `#define`, `#include`, etc.) ICG also uses the -D and -U command line arguments for defines and undefines, respectively.
The trick_parse directive is a Doxygen style field serving the same functionality as the `PURPOSE:` keyword and `ICG: (No)`. The trick_parse directive like all Doxygen style directives are prefixed with either a `\` or an `@` character.
`@trick_parse(everything)`: Treat this comment as the Trick header comment. Search for library dependencies in this comment, and treat all comments following variable definitions as Trick comments.
`@trick_parse(attributes)`: Treat this comment as the Trick header comment. Search for library dependencies in this comment. Create I/O attributes for the classes and structures in the file, but do not read comments following variable definitions.
`@trick_parse(dependencies_only)`: Treat this comment as the Trick header comment. Search for library dependencies in this comment. Do not process the file any further.
`@trick_exclude_typename(type)` is equivalent to `ICG_IGNORE_TYPES` in a Doxygen style field. The `trick_exclude` field lists the structs or classes to be ignored. Multiple `trick_exclude_typename` fields may be used to ignore multiple types.
The data structure type definition statements, `typedef struct { ... } name;`, and `typedef union { ... } name;``struct Foo { } name;` follows standard C syntax, and are supported by Trick. However, Trick requires a C comment immediately following every parameter declaration.
Trick allows any data type declaration within the data structure `typedef` statement. However, only the following data types will be processed by Trick:
1 previously processed structure, union, enumerated types and typedefs.
All other types are ignored. Types may be defined and used within the same header if the types are defined before they are used (this is a C syntax rule, too).
Any combination of pointers and array dimensions up to 8 dimensions may be used for parameter declarations; for example, `double ** four_dimensional_array[2][2];`, will be processed. Void pointers and function pointers are not processed. Parameters declared with pointers (like `four_dimensional_array` example), are treated differently; these are called unconstrained arrays. Trick will generate dynamic memory allocation source code for the developer which allows the developer to size the array dimensions (represented by the pointers) via special syntax in the runstream input file. The developer may 1) use the input file to input data to the arrays, 2) output the data via standard Trick logging functions, or 3) share the data through the variable server.
The user does have the option to perform their own memory management for parameters declared as pointers. In this case, instead of specifying the allocation in the input file, the user may allocate the data in a job. In order for Trick to process the data as if it was its own managed memory (and provide capabilities like logging, checkpointing, etc.), the memory address, and number and size of the allocation must be passed to the Trick `TMM_declare_extern_var` function. The user is also responsible for freeing the memory when done.
Types declared using `typedef struct`, `typedef union`, and `typedef enum` are recognized by Trick. Intrinsic typedefs are supported as well and may be nested in structures. The example that follows details a header that Trick will handle:
Each parameter declaration within a data structure definition may be accompanied by a trailing comment. There are six possible fields in the parameter comment, but only two are required. All six fields of the parameter comment are stored for later reuse at simulation runtime.
The first three fields in the parameter comment are optional and specify the input/output processing for the parameter. I/O permissions may be set globally or individual capabilities may set their permissions separately. I/O permissions for checkpointing is available to set separately.
To set all permissions for general variable access, start the comment with one of the following fields, `[**|*i|*o|*io]`, `trick_io([**|*i|*o|*io])` or `io([**|*i|*o|*io])`. These are equivalent forms to set general variable access.
*`**` indicates that Trick will not allow input or output for this parameter; i.e. the user can not input this parameter, record this parameter, or view its value.
*`*i` indicates that only input is allowed for the parameter. Parameter may be input through the checkpoint file or ref_assignment, but the parameter will not be recordable or written to a checkpoint file.
*`*o` indicates only output is allowed for the parameter. Parameter may be checkpointed or logged only. They are not reloaded during a checkpoint reload.
*`*io` specifies that both input and output are allowed for the parameter. This is the default condition if the field is omitted from the comment. Parameter may be in input file, may be checkpointed and logged.
Checkpoint I/O may be set separately by adding `trick_chkpnt_io([**|*i|*o|*io])` or `cio([**|*i|*o|*io])` to the comment. If this optional field is not present, the general I/O access field is used to determine checkpoint permissions.
The second field, `trick_units([measurement_units])`, is a required field and specifies the internal source code units for the parameter. These units are important because they give the input processor the knowledge of what units the user's input data needs to be converted to. Trick uses a third-party package, UDUNITS, for units support. It's syntax is specified [here](https://www.unidata.ucar.edu/software/udunits/udunits-2.2.28/udunits2lib.html#Syntax).
Following the measurement units specification, in the parameter comment, are two optional, user-defined attribute fields. Using these fields, a user can associate (up to 2) character strings with a parameter. These strings are stored in the ATTRIBUTES structures (in the io_src directory) generated by ICG. The first of these optional fields is delimited by brackets (‘[‘ and ‘]’) and is stored in the element ATTRIBUTES->alias. The second is delimited by braces (‘{‘ and ‘}’) and is stored in the element ATTRIBUTES->user_defined. The definition of the ATTRIBUTES structure is found in $TRICK_HOME/trick_source/sim_services/include/attributes.h.
The description field is required and must be the last field of the comment. The description field is basically everything after the first three fields. The description field may span multiple lines.
C++ headers may include constructs and concepts not found in C header files. In addition to all C syntax, Trick parses and understands many C++ features.
Trick generates several files to support its various features. The data recorder and checkpointer rely on code produced by the Interface Code Generator (ICG), which bookkeeps the memory layout of variables within the simulation. `public` members are always available to these features. `protected` and `private` data is also available if there is no use of `TRICK_ICG` in the header file. If use is found, Trick will issue a warning during simulation compilation, and `private` and `protected` data will only be accessible to Trick if the following `friend`s are added to the offending classes:
The input processor and variable server rely on code produced by a third-party tool, the [Simplified Wrapper and Interface Generator (SWIG)](http://www.swig.org/). SWIG provides the functions that allow access to simulation variables from Python contexts. These features can only access `public` members. It is not possible to expose `protected` and `private` data to them.
Trick may use model code with any type of inheritance. Some limitations are present to Trick's ability to input process, checkpoint, etc. inherited variables.
* Public and protected inherited variables are available for access.
* Protected and private inheritance is ignored.
* Multiple inheritance is processed but not well tested.
* Template inheritance is not currently supported.
Currently one level of namespace is supported. Additional levels of namespaces are ignored. Similarly classes and enumerations embedded in other classes are ignored.
Trick parses function declarations for input file use. The python input processor understands class method overloading. Overloaded methods with different arguments may be called in the input files. Default arguments are to methods are understood and honored in the input file. Operator overloading is skipped by Trick processors. Operator overloading is not implemented in the input file.
Trick attempts to process user defined templates. Simple templates are handled. We do not have a good definition of simple. Typedefs of templates is supported and encouraged. All protected and private data is ignored within templates. This is because it is not possible to specify the correct io_src friend function. Templates within templates are not processed. Finally abstract templates are not supported by Trick. These templates should be excluded from Trick processing. See below to see how to exclude code from processing.
STLs may be used in models. However, STL variables are not data recordable, they are not visible in the variable server, nor are they directly accessible in the input file. Some STLs are automatically checkpointed: `array`, `vector`, `list`, `deque`, `set`, `multiset`, `map`, `multimap`, `stack`, `queue`, `priority_queue`, `pair`.
Sometimes classes contain members that are not copyable or the math modeler wants to prevent the class from being copied. Declaring an unimplemented private copy constructor and assignment, "=", operator prevents the class from being copied.
When using such classes in Trick, classes that include non copyable classes must also declare themselves not copyable. this extends all the way up to sim objects in the S_define.
Trick attempts to skip over class code in header files while searching for class variables and method declarations. However, code can sometimes confuse Trick and cause it to abort processing of header files. It is recommended to keep code out of the header file.
The Trick header is an optional comment block at the top of each source file. It is used for auto-documentation, and more importantly is the means of specifying dependencies to objects or libraries not processed by Trick. Separate functions within a source file do NOT require additional headers. Since parentheses, ( ), are used to delineate fields within the comment header, parentheses are not allowed as characters within the comment fields. NOTE: Even if you are coding a C++ file, you must still specify the comment header using C style comments (not C++ style comments).
* The PURPOSE field should be a brief description of what the module does.
* The REFERENCES field may contain any number of references, with each reference possessing any number of sub items; notice the nested parentheses for the REFERENCES field.
* The ASSUMPTIONS AND LIMITATIONS field may contain any number of assumptions and limitations delimited by parentheses.
* The LIBRARY DEPENDENCIES. See Library_Dependencies section in the model header section
* The PROGRAMMERS field may contain any number of programmer fields, each of which may contain any number of sub items; e.g. programmer name, company, mod date, etc. The programmer fields are meant to provide an in-code means to track code changes.
Trick is always changing. The interface to Trick functions may change with each major version. Sometimes even minor version upgrades change the interface. When Trick builds model source code, it includes -DTRICK_VER=<version> and -DTRICK_MINOR=<minor_version> to the TRICK_CFLAGS and TRICK_CXXFLAGS. This allows developers to key off the Trick version in model source code. If there are any compile issues dependent on Trick version, this #define may be useful.