The Native Interface
Topics covered in this chapter:
- Module management in Mosel
- The initialization function
- Defining subroutines
- Defining types
- Defining services
- Service ``Reset''
- Service ``Module priority''
- Service ``Unload''
- Service ``Check Version''
- Service ``Find Parameter''
- Service ``List of Parameters''
- Service ``Inter-Module Communication Interface''
- Service ``Module Dependency List''
- Service ``IO Driver List''
- Service ``On Exit''
- Service ``Check Restrictions''
- Service ``Update Version''
- Service ``Implied Dependency List''
- Service ``Annotations''
- Service ``DSO stream''
- Service ``Required types''
- Service ``Provider''
- Service ``Namespace groups''
- Service ``Memory use''
- Service ``Static module''
- Service ``Get array indices''
- Service ``Deprecation List''
- Service ``Minimum compatible version''
- Defining IO drivers
- Static modules
Module management in Mosel
In Mosel, all basic module operations are performed by a Module Manager. This manager is in charge of loading and unloading the modules as well as maintaining various pieces of information about the modules (version number, features provided, reference counting). Whenever a module is requested either by the Model Compiler or to run a previously compiled model, the Module Manager looks for the module and, if it is not yet in core memory, loads it from the disk and initializes it by calling its initialization function (see Section The initialization function). After a period of inactivity or on request, the Module Manager may unload unused modules.
Use of modules for compiling a model
The directive uses "modname" informs the Model Compiler that the module modname is required by the model being compiled. For every module that appears in a uses directive, the compiler queries the Module Manager in order to extend its dictionary with the symbols and operators defined by the module. The following information is therefore recorded for every symbol:
- identifier (i.e. name of the subroutine or type)
- origin: the identity of the module that defines the symbol
- depending on the type of the symbol:
- constant: the value associated to the identifier
- procedure/function/operator: internal code in the module and types of the result and parameters
- type: internal code in the module and properties (e.g. can this type be translated into a string?)
During the compilation, each time an external symbol is encountered, assuming it is used appropriately (type of result as required, valid parameters for routines, etc.), the following operation is performed depending on the category of the symbol:
- constant: the symbol is replaced by its value
- procedure, function or operator: a function call is prepared using the internal code of the symbol
- type: the function call that corresponds to the required operation (e.g. creation) is prepared using the internal code of the symbol
In the BIM file that is generated during the compilation of a model, the compiler saves the table of modules it requires together with their version numbers. Only the modules that are effectively required (those from which functions are to be called) are stored in this table.
Use of modules for running a model
When the BIM file is loaded, Mosel queries the Module Manager for the modules required by the model. The model is ready for execution only if all the modules with their specified versions are available. The modules are considered to be ``in use'' as long as the model is in core memory.
During the execution of the model, the function calls prepared during the compilation phase are executed.
The initialization function
Once a module is loaded in core memory, the Module Manager calls a special function: the initialization function. Through this function, the constant symbols (Section Table of constants), subroutines (Section Table of functions), types (Section Table of types), and services (Section Table of services) that are provided by the module are passed on to Mosel. The control parameters of the module are not communicated in this way, they require the definition of a dedicated service function (Section Service ``Find Parameter'') and the implementation of two specific library functions (Section Table of functions).
In order for Mosel to find this initialization function, it must be named modulename_init (where modulename is the name given to the module) and have the following signature:
DSO_INIT modulename_init(XPRMnifct nifct, int *interver, int *libver, XPRMdsointer **interf)
The parameters are used to exchange information about version numbers and the functionality provided by the module.
- nifct is a table of functions provided by Mosel that can be called from the module during its processing. They are used to access the data of the running model (see Section Functions of the Native Interface) and is usually saved in a global variable for later use
- interver is used to tell Mosel which version of the Native Interface is employed for the implementation of this module. This parameter must always be assigned the value XPRM_NIVERS. By default, the interface version is the one of the current Mosel version but it is possible to select an older release (such that the generated module can be used with this release) by defining the macro XPRM_NICOMPAT before including the "xprm_ni.h" header file. This macro must refer to the Mosel target version. For instance:
#define XPRM_NICOMPAT 3000000 #include "xprm_ni.h"
will use the API version of Mosel 3.0.0 (functions introduced after this release are disabled) - libver is the version number of the module: it is generated using the macro XPRM_MKVER(M,n,r) where (M,n,r) stands for (major version number, minor version number, release number). Each number must be an integer between 0 and 999.
- interf is a structure composed of 4 tables (and their respective sizes) describing the constants, the functions, the types and the services implemented by the module
The format of the main interface structure XPRMdsointer is the following:
{ int sizec; XPRMdsoconst *tabconst; int sizef; XPRMdsofct *tabfct; int sizet; XPRMdsotyp *tabtyp; int sizes; XPRMdsoserv *tabserv; }
Every table in this structure (constants, subroutines, types, and services) is preceded by its size. The four tables are described in detail in the following sections.
Example:
static XPRMnifct mm; DSO_INIT mymodule_init(XPRMnifct nifct, int *interver, int *libver, XPRMdsointer **interf) { mm=nifct; /* Save the table of NI functions */ *interver=XPRM_NIVERS; /* Mosel NI version */ *libver=XPRM_MKVER(0,0,1); /* Module version */ *interf=&dsointer; /* Pass info about module contents to Mosel */ return 0; }
Table of constants
Each entry of the table of constants is a pair ( constant name, constant value ). If a constant is to be declared as part of a namespace its name must be fully qualified (e.g. "mynamspc∼mycst"). A module can define integer, real, Boolean and string constants. The initialization of the table can be done using the following macros:
XPRM_CST_INT(char *name, int value)
XPRM_CST_BOOL(char *name, int value)
XPRM_CST_STRING(char *name, char *value)
XPRM_CST_REAL(char *name, static const double value)
Note that for real constants, a static variable has to be provided instead of a constant number.
static const double myreal=12.456; static XPRMdsoconst tabconst[]= { XPRM_CST_INT("MYINT", 10), XPRM_CST_BOOL("MYBOOL", XPRM_TRUE), XPRM_CST_STRING("MYSTR", "text"), XPRM_CST_REAL("MYREAL", myreal) };
The information provided by the table of constants is used only during the compilation phase of the model: constants are immediatly replaced by their values. As a consequence, a module that only defines constants is only required for the compilation of a model using it; at execution time it is not loaded again.
Table of functions
The table of functions describes the functions, procedures, and operators that will be available in the Mosel language. Each entry of the function table is of the following structure:
{ char *name; int code; int type; int nbpar; char *parstr; int (*fct)(XPRMcontext ctx, void *libctx); }
- name:
-
The name
that will be used in the Mosel language. If the subroutine is to be declared as part of a namespace its name must be fully qualified (
e.g.
"mynamspc∼myfct").
Note that different subroutines (or operators) may have the same name as long as they are not expecting the same parameters ( overloading). It is also possible to overload a predefined function or procedure with the same restriction as for user defined symbols. Overloading cannot apply between function and procedure ( i.e. a procedure cannot overload a function and vice versa). - code:
-
An internal
code for the subroutine or operator.
This code must be either an integer value ≥1000 or a predefined code. Note that the entries in the table of functions must be sorted in ascending order of their internal code. - type:
-
The type
returned by the routine.
For a function, the possible values are: XPRM_TYP_INT, XPRM_TYP_REAL,
XPRM_TYP_STRING, XPRM_TYP_BOOL to indicate an integer, real, string or Boolean return value respectively. The type must be XPRM_TYP_EXTN if the function returns an entity of a type managed by the module ( i.e. the function is a constructor) — in this case, the first word of the parameter string is the name of this type followed by a colon. If the function returns a set or a list the type must also be XPRM_TYP_EXTN and the first word of the parameter string must start with "&{" for a set or "&[" for a list followed by the element type and a colon. The element type is specified by a letter for basic types (same convention as for the parst) or the letter n and the type name for a native type. If the subroutine is a procedure the type must be XPRM_TYP_NOT.
By default a function returning a non-basic type is considered to be a constructor ( i.e. the returned value is a newly created entity). If such a function returns a reference to an existing entity it must increase the reference count of this object (see Section Reference counting) and have the flag XPRM_FTYP_PTR as part of its type.
Functions the name of which starts with the string "get", returning a basic type ( i.e. integer, real, string or Boolean) and taking as their only argument a native type, linctr or mpvar are identified as attribute accessors (see findattrdesc). For instance the function "getsol(mpvar):real" returns the sol attribute of a decision variable. Adding XPRM_FTYP_NOATTR to the type of a function of this kind prevents Mosel from recording it as an attribute accessor. - nbpar:
- The number of parameters required by the routine
- parstr:
-
The parameter
string is used to describe the type of each parameter.
This string is composed with the following characters:i an integer r a real s a registered text string (see regstring) S a text string (it might be deleted after the end of execution of the routine) b a Boolean v a decision variable (type mpvar) c a linear constraint (type linctr) I a range set a an array (of any kind) e a set (of any type) l a list (of any type) u a union (of any kind) f a procedure or function (of any kind) |xxx| external type named `xxx' !xxx! the set named `xxx' Andx.t an array indexed by `ndx' of the type `t'. `ndx' is a string describing the type of each indexing set. `ndx' may be omitted in which case any array of type `t' is a valid parameter. Et a set of type `t' Lt a list of type `t' F(sig) a procedure with signature `sig' (this signature cannot include the tag F) Ft(sig) a function of type `t' with signature `sig' (this signature cannot include the tag F) ? any type, the routine will receive actually 2 parameters for this marker: the first one, an integer, is the type of the following one (the effective value of the argument) * must be the last character: the function has a variable number of arguments
Moreover, if the function is of type XPRM_TYP_EXTN, the string starts with the name of the type followed by a colon.
Example: "mytype:ir|mytype|s*" is the signature of a function of type `mytype' expecting at least 4 parameters (integer, real, mytype and string).
The signature for a function that returns a list of text and take a real as only argument will be: "&[ntext:r". - fct:
-
The function
Mosel has to call to perform the operation.
The prototype of all functions must be (see Section Defining subroutines):int functionname(XPRMcontext ctx, void *libctx);
Example:
static int my_getsol(XPRMcontext ctx, void *libctx); static int my_getname(XPRMcontext ctx, void *libctx); static int my_eql(XPRMcontext ctx, void *libctx); static int my_new(XPRMcontext ctx, void *libctx); static XPRMdsofct tabfct[]= { {"getsolarray", 1000, XPRM_TYP_NOT, 2, "aa", my_getsol}, {"getname", 1005, XPRM_TYP_STRING, 1, "|mytype|", my_getname}, {"@=", 1011, XPRM_TYP_BOOL, 2, "|mytype||mytype|", my_eql}, {"@&", 1015, XPRM_TYP_EXTN, 3, "mytype:sri", my_new} }
For details on the definition of operators (such as the third and fourth entries in this example) the reader is referred to Section Special functions/operators.
Table of types
Types introduced by modules are handled by Mosel just like any other standard type (integer, real...). To define a type, some specific functions have to be provided by the module. Each entry of the table of types contains the following items:
- name (char *):
- The name of the type that will be used in the Mosel language. If the type corresponds to a problem extension, the name must have the form mainpbtyp.extn (see Section Problem types). If the type is to be declared as part of a namespace its name must be fully qualified ( e.g. "mynamspc∼mytype").
- code (int):
- An internal code for the given type. This code is an integer value not larger than 65535. Note that types must be sorted in ascending order of their internal code.
- props (int):
-
A bit coded set of properties. The supported properties are:
- XPRM_DTYP_PNCTX: If this flag is set, the function tostring (see below) can be called with a NULL context.
- XPRM_DTYP_RFCNT: If this flag is set, the module handles reference count for this type. As a consequence Mosel may call the function create (see below) with a reference to a previously created object for increasing its reference count. The function delete (which is mandatory in this case) is then called as many times as the create function has been used for a given object before this object is effectively released. When this property is not available for a type, Mosel handles itself the reference counting: this is in general less efficient except if the type is a problem extension (reference counting is not used in this case).
- XPRM_DTYP_APPND: If this flag is set, the function copy supports appending.
- XPRM_DTYP_ORSET: If this flag is set, the function copy can be called only for resetting an object.
- XPRM_DTYP_PROB: This flag must be set if the type corresponds to a problem.
- XPRM_DTYP_SHARE: If this flag is set, the create function of this type supports creation of shared entities.
- XPRM_DTYP_TFBIN: If this flag is set, the tostring and fromstring functions of this type support exportation and exportation in binary format.
- XPRM_DTYP_ORD: If this flag is set, the compare function implements all comparison operators (i.e. objects of this type are ordered).
- XPRM_DTYP_CONST: If this flag is set, the create function supports creation of constants and the copy function can compute hash values.
- XPRM_DTYP_ANDX: The type is an array indexer when this flag is set, and the service XPRM_SRV_ARRIND must be defined (see Section Service ``Get array indices'').
- XPRM_DTYP_NAMED: If this flag is set, the create function of this type supports name association (see setentname).
- create function (void *):
-
The
function Mosel has to call for creating an object of this type. The function must return a pointer to this new object or
NULL in case of failure. The prototype of create is:
void *(*create)(XPRMcontext ctx, void *libctx, void *ref,int tnop)
This function is mandatory. If the module does not support reference count for this type, the parameter ref is always NULL and can be ignored. Otherwise, the flag XPRM_DTYP_RFCNT has to be set (see above) and whenever this function is called with a valid pointer as the third parameter, the reference count for the corresponding object must be incremented (no new object has to be created). The value returned should be the provided reference. The last parameter is the order number associated to this type for the running model.
If the type has property XPRM_DTYP_SHARE, XPRM_DTYP_CONST or XPRM_DTYP_NAMED the semantic of the last parameter changes: it stores both the type order number (which can be extracted using the macro XPRM_TYP(tnop)) and a code indicating what operation to perform. The macro XPRM_CREATE(tnop) returns the following operation codes:- XPRM_CREATE_NEW: The routine behaves as described above (i.e. handling of an ordinary entity).
- XPRM_CREATE_SHR: The routine has to return a shared entity: if the pointer ref is NULL a newly created entitiy must be returned. Otherwise the provided reference ref points to the initial object that is to be shared (that has been created by a preceding call to this routine from another model). The function does not have to return this reference (this can be another datastructure) but it is expected that both pointers refer to the same entity. Note that several models may call this function with the same reference object at the same time. This option will be used if the type has the property XPRM_DTYP_SHARE.
- XPRM_CREATE_CST: The function has to return a constant copy of the provided object. The module must make sure that any attempt at modifying such a constant (e.g. by using an assignment) will cause a runtime error. This option will be used if the type has the property XPRM_DTYP_CONST.
- XPRM_CREATE_NAMED: The entity ref has been associated with a name with setentname (this identifier can be retrieved with getentname), the function must return ref without updating its reference count. It is recommended to release this association when the entity is deleted from the delete function using setentname.
- delete function (void *):
-
The function
Mosel has to call for deleting an object previously allocated using the
create function.
void (*fdelete)(XPRMcontext ctx, void *libctx, void *todel,int typnum)
This function is optional (the entry may be NULL) when reference count is not handled by the module, if defined, it is used to delete local and temporary objects. Note that if reference count is implemented, this function will be called as many times as the create function has been called for a given reference, the object must be deleted only the last time the function is used. Note that global objects are not explicitly deleted: it is the responsibility of the module to release the resources associated to these objects using the reset and onexit services (see Section Service ``Reset'' and Service ``On Exit''). The last parameter is the order number associated to this type for the running model. - tostring function (void *):
-
This function
has to be called by Mosel for getting a textual representation of an object.
int (*tostring)(XPRMcontext ctx, void *libctx, void *obj, char *dest, int maxsize,int typnum)
The textual representation of obj encoded in UTF-8 has to be copied into dest the maximum length of which is maxsize. The reference obj might be NULL: in this case the function is expected to return the textual representation of an entity in its initial state ( i.e. just after having been created). The function must return the length of the generated string (excluding the terminating null byte) or a negative value in case of error. If the string that is to be returned in dest exceeds the given maximum length, function tostring returns the required length but not the string itself : it is then called a second time with a sufficiently large maximum size.
If the type has property XPRM_DTYP_TFBIN the system may use this routine to generate a binary representation of the entity: in this case the last parameter is bit encoded (type number can still be retrieved using XPRM_TYP(typnum)) and the bit XPRM_TFSTR_BIN is set when the binary fomat is requested. The result of the call is expected to be a platform independent sequence of bytes (instead of the default textual representation) that can be decoded by the function fromstring.
This function is optional (the entry may be NULL), if defined, it is used for displaying values (procedures write/ writeln) and by the initializations to procedure. - fromstring function (void *):
-
This
function has to be called by Mosel for initializing an object from a textual representation.
int (*fromstring)(XPRMcontext ctx, void *libctx, void *obj, const char *src,int typnum, const char **end)
The object obj is initialized with the content of the string src encoded in UTF-8. When the end parameter is not NULL, the pointer to the first character not used by the conversion has to be returned via this parameter. It must receive a copy of src in case of failure. If successful, the function must return 0; any other value is interpreted as a failure.
If the type has property XPRM_DTYP_TFBIN the system may use this routine to initialize the entity from a binary representation produced by the tostring function: in this case the last parameter is bit encoded (type number can still be retrieved using XPRM_TYP(typnum)) and the bit XPRM_TFSTR_BIN is set when the input buffer is in binary fomat. In this mode the parameter end is initialized with a reference to the first byte after the data ( i.e. the length of the input buffer is *end-src).
This function is optional (the entry may be NULL), if defined, it is used by the initializations from procedure. - copy function (void *):
-
This
function is required for assignments not explicitly stated (
e.g. in array initialization or when assigning records) and may be used to generate some assignments (as a replacement for the
"@:" and
"@P" operators, see section
Special functions/operators).
int (*copy)(XPRMcontext ctx, void *libctx, void *dest,void *src,int tnop)
The parameter tnop is bit encoded: it stores both the type order number (which can be extracted using the macro XPRM_TYP(tnop)) and a code indicating what operation to perform. The macro XPRM_CPY(tnop) returns the following operation codes:- XPRM_CPY_COPY: The object dest receives the content (or becomes a copy) of src (which may be NULL). This operation is not used if the type has property XPRM_DTYP_ORSET.
- XPRM_CPY_RESET: The object dest is reset (i.e. it returns to its initial state). This operation is essentially used to implement the reset function of the Mosel language.
- XPRM_CPY_APPEND: The object dest is extended with a copy of src (which may be NULL). This operation is used if the type property XPRM_DTYP_APPND is set and property XPRM_DTYP_ORSET is not set.
- XPRM_CPY_HASH: The pointer dest references an unsigned integer that must be populated with a hash value computed from the object src (which may be NULL), the implementation may use hashmix for this calculation. This operation is used if the type property XPRM_DTYP_CONST is set and property XPRM_DTYP_ORSET is not set.
This function is optional (the entry may be NULL) but if it is missing, the operations where it is necessary are disabled by the compiler for the corresponding type. - compare function (void *):
-
This
function is required for comparison of aggregated objects (
e.g. when testing equality of records including fields of this type), it may also be used to implement comparators if the corresponding functions are not defined.
int (*compare)(XPRMcontext ctx, void *libctx, void *obj1,void *obj2,int tnop)
The parameter tnop is bit encoded: it stores both the type order number (which can be extracted using the macro XPRM_TYP(tnop)) and a code indicating what operation to perform. The macro XPRM_COMPARE(tnop) returns the following operation codes:- XPRM_COMPARE_EQ: Test whether obj1 and obj2 are equal.
- XPRM_COMPARE_NEQ: Test whether obj1 and obj2 are different.
- XPRM_COMPARE_LTH: Test whether obj1 is less than obj2.
- XPRM_COMPARE_LEQ: Test whether obj1 is less or equal than obj2.
- XPRM_COMPARE_GEQ: Test whether obj1 is greater or equal than obj2.
- XPRM_COMPARE_GTH: Test whether obj1 is greater than obj2.
- XPRM_COMPARE_CMP: Return 0 if obj1 and obj2 are the same, -1 if obj1 is less than obj2 and 1 otherwise.
This function is optional (the entry may be NULL) but if it is missing, the operations where it is necessary are disabled by the compiler for the corresponding type. By default the compiler expects that only the 2 first operations are defined, the type property XPRM_DTYP_ORD indicates that the function supports all operations.
static XPRMdsotyp tabtyp[]= { {"firsttype", 1, 0, createfirst, del1, tostr1, fromstr1,copy1,cmp}, {"secondtype", 2, XPRM_DTYP_RFCNT, create2, delete2, NULL, NULL, NULL,NULL}, {"mpproblem.mypb", 3, XPRM_DTYP_PROB, newpb, delpb, NULL, NULL, cppb,NULL} };
Table of services
Services are special tasks that are not directly linked to the Mosel language itself (that is, they are not visible to the user of a module). Under some particular circumstances, Mosel looks for a service. If this service is implemented by the module, the corresponding function is called. Each entry of the table of services is the pair ( service code, function to call ).
Example:
static XPRMdsoserv tabserv[]= { {XPRM_SRV_RESET,(void *)my_reset}, {XPRM_SRV_PRIORITY,XPRM_MKPRIORITY(-1)}, {XPRM_SRV_UNLOAD,(void *)my_unload} };
Note that all services are optional. See section Defining services for a comprehensive list of services.
Defining subroutines
All library functions that implement subroutines (or operators, see Section Special functions/operators) have the same prototype
int functionname(XPRMcontext ctx, void *libctx);
The first parameter of such a function is the Mosel execution context under which the function is called. This context describes the state of the Mosel Virtual Machine plus various pieces of information related to the model that is executed. It is required by most functions of the Native Interface. The second parameter is the module context defined by the reset service (see Section Service ``Reset''). If this service is not implemented, the value of this parameter is NULL.
The return value of a library function must be XPRM_RT_OK if the function succeeded, XPRM_RT_ERROR if it failed (in which case the execution terminates with an error) or XPRM_RT_STOP to interrupt the execution of the model. It is also possible to terminate the execution in the same way as exit(code) does by pushing onto the stack the exit code then returning XPRM_RT_EXIT.
If the execution of a routine is not immediate (it performs a solution algorithm that requires several seconds for instance), it is recommanded to implement cancelation points: from time to time, the routine should call the NI function chkinterrupt to check whether execution is to be continued. If the return value of this function is not 0, the execution is expected to terminate and the routine has to interrupt its processing as soon as possible then return.
During the execution of the function, the actual parameters of the subroutine (in the Mosel language) have to be taken from the Mosel stack and if the routine is a function, the return value must be put onto this stack before the termination of the function (this is not required if the routine terminates with XPRM_RT_ERROR that results in the interruption of the execution). The following macros are provided for accessing the Mosel stack.
Macros for taking objects from the stack:
int XPRM_POP_INT(XPRMcontext ctx)
double XPRM_POP_REAL(XPRMcontext ctx)
XPRMstring XPRM_POP_STRING(XPRMcontext ctx)
void *XPRM_POP_REF(XPRMcontext ctx)
XPRMalltypes XPRM_POP_ANY(XPRMcontext ctx)
Macros for putting objects onto the stack:
XPRM_PUSH_INT(XPRMcontext ctx, int i)
XPRM_PUSH_REAL(XPRMcontext ctx, double r)
XPRM_PUSH_STRING(XPRMcontext ctx, XPRMstring s)
XPRM_PUSH_REF(XPRMcontext ctx, void *r)
XPRM_PUSH_ANY(XPRMcontext ctx, XPRMalltypes a)
Macro for accessing the top of the stack:
XPRMalltypes *XPRM_TOP_ST(XPRMcontext ctx)
The macro XPRM_FREE_ST(XPRMcontext ctx) returns the number of free elements on the stack (i.e. that can be used for pushing values). Note that at the beginning of the execution of a native call there are always at least 4 free positions on the stack.
Note that the Mosel stack manipulates only three basic types: integer, real, and string. Boolean values are handled as integers (0 is false, 1 is true); all other types (including arrays, sets and external types) are passed by reference (the macros XPRM_POP_REF and XPRM_PUSH_REF have to be used for those types). Routines taking references as parameters must have appropriate handling for the NULL pointer: this is the representation of an empty string and the value returned when accessing an uninitialized object (set, array or non existent dynamic array entry).
Text strings manipulated by Mosel are stored in a dictionary. As a consequence, strings produced by a native routine (for instance as the result of a concatenation) have to be registered using the function regstring before being sent to Mosel either as a return value (macro XPRM_PUSH_STRING) or stored in a Mosel object (as an identifier, set element or array entry).
Mosel maintains a reference count of all referenced objects (decision variables, linear constraints, lists, sets, arrays and external types): an object is released when its last reference is deleted. If a native routine needs to keep a reference to an object after its termination (to be used later in a subsequent call for instance) it should save the reference using newref in order to make sure the object will not be deleted. The native code should then use delref after it no longer requires the saved reference.
Example:
Assume the routine myfct takes an integer, a real and a set of decision variables as parameters and returns a string. If the C function providing the implementation of this routine is c_myfct and its internal code is 1010, the declaration in the table of functions is:
{"myfct",1010,XPRM_TYP_STRING,3,"irEv",c_myfct}
The function c_myfct has to get from the Mosel stack the three parameters and before its termination, to put back the result value. The skeleton of this function is therefore:
int c_myfct(XPRMcontext ctx, void *libctx) { int int_param; char *result; double real_param; XPRMset set_param; int_param =XPRM_POP_INT(ctx); /* Get the first parameter */ real_param=XPRM_POP_REAL(ctx); /* Get the second parameter */ set_param =XPRM_POP_REF(ctx); /* Get the third parameter */ /* Body of the function: assigns the result variable */ /* Put the result onto the stack */ XPRM_PUSH_STRING(ctx,mm->regstring(ctx,result)); return XPRM_RT_OK; /* The function succeeded */ }
If a module defines control parameters, it needs to implement the special routines getparam and setparam for its parameters. Function getparam takes as its only argument the code of the control parameter in the module (obtained at compilation through the service ``find parameter'', see Section Service ``Find Parameter'') and returns the current value of the parameter. The procedure setparam has two arguments, the code of the parameter and its new value.
At the beginning of the table of functions, the following two lines must be added in the order shown here (assuming that my_getpar and my_setpar are the implementations of the two subroutines):
{"", XPRM_FCT_GETPAR, XPRM_TYP_NOT, 0, NULL, my_getpar}, {"", XPRM_FCT_SETPAR, XPRM_TYP_NOT, 0, NULL, my_setpar},
Defining types
A module defines a type via an entry in the table of types (see Section Table of types) that specifies the type creation function and optionally, functions for type deletion and transformation to/from string and copy. Besides these five functions, a module needs to implement certain other library functions when it defines a type.
Memory management
Mosel does not guarantee that it will call the delete function for each object created with the create function. It is therefore required to either use memalloc/memfree for allocation of entities or implement a module context in order to keep track of all created objects and delete them all at once when the context has to be released (using the reset service, see Section Service ``Reset'').
Reference counting
The Mosel language makes possible for a given object to be referenced several times in a model. If such an object has to be deleted, Mosel must make sure that there is no remaining reference to this object before releasing it. A typical example of this situation is when an object is defined in a subroutine and added to a set defined globally. In this case, when the subroutine terminates, the object can be deleted only if it is not included in any set. The same remark applies to decision variables mpvar: locally defined variables can be deleted only if they do not appear in any constraint.
In order to know when a referenced object (basically all external types as well as mpvar and linctr) can be deleted, Mosel maintains a counter of references for each object: whenever a new reference is created for an object its counter is increased and each time this object should be deleted its counter is decreased. Objects are created with a counter initialised to 1 and effectively deleted when their counter reaches 0.
Although Mosel can handle reference counting for external types, it is recommended for modules to provide support for this mechanism for the types they publish. The interface relies on the create and delete functions that are used respectively to increase and decrease the reference counts of the associated objects (see Section Table of types).
Problem types
Problem types are implemented as special module types: the property XPRM_DTYP_PROB is set; both create and delete routines are provided; tostring and fromstring functions are not used. The creation and deletion functions are used by the system to manage problem contexts: the current context of each problem type is stored in the pbctx table of the Mosel execution context which is passed to all NI routines. The index of a particular problem type in this table can be obtained using the function gettypeprop to get the XPRM_TPROP_PBID property (use findtypecode for getting the type code required by this routine). Since this index is not changed during the execution of the model, it can be retrieved and saved when creating the module context (see Section Service ``Reset''). The table of problem contexts is automatically updated whenever a with construct is open or closed: native routines have to refer to this table to get their active context when processing operations related to problems.
A problem type may be extended by means of problem extensions. When a problem is created/deleted, a corresponding context of each of its extensions is also created/deleted. Similarly, when a problem is activated (through a with construct in the model), all of its associated extensions are also made active. A problem extension is identified by its name that must have the form mainpb.extn where mainpb is the name of the problem to be extended and extn the extension name. For instance, the Xpress Optimizer is declared as an extension to mpproblem - it is named mpproblem.xprs. Since it is not referenced directly, reference counting is not required for a type representing a problem extension. Note also that extensions may restrict the capabilities of a problem type: for instance, the copy operator of a problem type will be disabled if one of its extensions does not support the operation.
Special functions/operators
For handling the types introduced by the module, it is possible to define operators that are declared as functions (cf function table, Section Table of functions) with a special name: all operators have a two-character name, the first character of which is always '@'. Operators can be defined for any type and return any type; however, they cannot replace a predefined operator. For instance the addition of reals @+(r,r):r cannot be re-defined in a module. Furthermore, it is not possible to re-define directly any aggregate operators (prod, sum, and, or) or set operators (inter, union, in, max, min).
Basic constructors
The duplication operator is never called explicitly but its definition is required, for instance, by certain types of assignment.
A constructor may also be named "@&I" if it takes a single parameter of a basic type: the compiler will use such a constructor to perform implicit conversions in subroutine parameters.
The definition of @0 for a given type C implies the aggregate operator SUM if @+(C,C):C is defined for this type and the aggregate OR if @o(C,C):C is defined. Similarly, with the 1-element @1 the Mosel compiler can generate the aggregate operator PROD if @*(C,C):C is defined and the aggregate AND if @a(C,C):C is defined.
The definition of @2 for a given type C implies the aggregate operator MIN if the type has property XPRM_DTYP_ORD. Similarly, with @3 the Mosel compiler can generate the aggregate operator MAX if the type is ordered.
Arithmetic operators
If @S and @0 are defined for a given type, the aggregate operator SUM can be generated by the Mosel compiler (if @S is not defined, @+ will be used instead, the difference is the order of operands). The same generation occurs for the aggregate operator PROD if @1 and @* are defined. The SUM operator can also be generated when the addition returns a different type. In this case, if type1+type1->type2, operator @0 for type2 is required as well as the operation type1+type2->type2 (commutativity does not apply for this construct).
Where operations are marked `commutative', Mosel deduces the result for (B,A) if the operation is defined for (A,B), assuming that A and B are of different types.
A and B (if of an external type) must be deleted by the operator.
Logical operators
Usually, if a type is defined for these operators, it is not defined for the arithmetic operators.
Operator | Operation | Return value |
---|---|---|
@a(A,B):C | logical `and' | A and B → C |
@o(A,B):C | logical `or' | A or B → C |
@n(A):C | logical negation | not A → C |
If @a and @1 are defined for a given type, the aggregate operator AND can be generated by the Mosel compiler. The same generation occurs for the aggregate operator OR if @0 and @o are defined.
A and B (if of an external type) must be deleted by the operator.
Comparators
A, B and C may be of any type. If C is of an external type, the operator is therefore a constructor. If a comparator is not defined but the indicated complementary and the negation exist, Mosel constructs the missing operator. The compiler will also generate the operators returning a boolean result if both A and B are of the same type and the corresponding compare function is fully implemented (cf Section Table of types).
``is_'' operators
Operator | Operation | Return value |
---|---|---|
@e(B):C | SOS type 1 | B is_sos1 → C |
@t(B):C | SOS type 2 | B is_sos2 → C |
@f(A):C | free | A is_free → C |
@c(A):C | continuous | A is_continuous → C |
@i(A):C | integer | A is_integer → C |
@b(A):C | binary | A is_binary → C |
@p(A,B):C | partial integer | A is_partint B → C |
@s(A,B):C | semi continuous | A is_semcont B → C |
@r(A,B):C | semi continuous integer | A is_semint B → C |
A, B and C may be of any type. If C is of an external type, the operator is therefore a constructor. A is always a reference to an existing object and B can be any expression.
Assignment
A must be deleted by the operator. The compiler will use the copy function of the type (see Section Table of types) to implement a direct or additive assignment (between 2 objects of the same type) if the corresponding operators are not defined or if C requires to be duplicated.
"As statement" operator
Operator | Operation | |
---|---|---|
@_(A) | expression A is accepted as statement |
A must be deleted by the operator.
This operator is used when an expression can be used in place of a statement (normally, an expression must be either assigned to an identifier or employed as parameter for a routine). Mosel implements this operator on linear expressions. For instance, the expression `x <= 10' can be assigned to an identifier of type linctr or used as is in place of a statement.
Defining services
Services are declared in the table of services (see Section Table of services). Each entry of the table is the pair ( service code, service implementation ) (a pointer). This section describes the available codes together with the functionality the user has to provide in order to implement the corresponding service.
Service ``Reset''
XPRM_SRV_RESET: void *reset(XPRMcontext ctx, void *libctx, int version)
During the execution of a model, a module may have to store some data that is specific to this particular session. This information is called its context of execution. Each function of the module is always called with 2 contexts: the first one is the Mosel execution context and the second one the module context. The service XPRM_SRV_RESET is used to handle this module context: when the execution of the model starts, this function is called with a Mosel context and the constant NULL as its parameters. At this call, the reset function must return a pointer that will be used as the module context for the following calls to functions of the module.
When the model is reset (either before a new execution or before deleting it from memory) the reset function is called again but with the pointer it returned after its first execution. This time, the reset function has to release the resources used by the module context.
The last parameter sent to this function is the version number required by the running model. This information may be useful if the module can emulate different versions: from this function it may build an appropriate context depending on the version requested.
Service ``Module priority''
XPRM_SRV_PRIORITY: int priority
This service may be used to define a priority value for the module. Modules with lower priority values are initialised before those with an higher priority. The resetting (i.e. call to the onexit function and second call to the reset function) is performed in the reverse order of the initialisation. The default priority value is 0, specifying a new value requires the use of the XPRM_MKPRIORITY macro (e.g. XPRM_MKPRIORITY(100)).
Service ``Unload''
XPRM_SRV_UNLOAD: void unload(void)
Mosel may decide to unload a module when it is not required any more. In some cases, a module has to release some resources it has allocated during its initialization (for instance, it uses a licensed software and has to release a license or some global data). For this purpose, this function is called just before Mosel unloads the module.
Service ``Check Version''
XPRM_SRV_CHKVER: int chkvers(int requested_version)
The convention for module version numbers is to use a code version with 3 numbers ( major, minor, release ). When loading a module at runtime, Mosel checks if the obtained module is compatible with the one requested by the model (at compile time, the version numbers of all used modules are stored in the BIM file). A module version A can be used in place of module version B if major(A) = major(B) and minor(A) ≥ minor(B). With the ``check version'' service a module can change this default behavior. This may be useful if, for instance, a module can emulate the behaviour of an older version. The parameter requested_version is the version number expected by the model to be able to run. If this function returns 0, the module is accepted; otherwise it is rejected due to the incompatibility in version numbers. As an alternative to this function the service XPRM_SRV_COMPAT can be defined (see Service ``Minimum compatible version'').
Service ``Find Parameter''
XPRM_SRV_PARAM: int findparm(const char *pname, int *type, int why, XPRMcontext ctx, void *libctx)
This function is used to translate a parameter name into an internal code. This code is then used at run time by the Mosel Virtual Machine for calling the special routines getparam and setparam. If the function returns a value smaller than 0, the parameter named pname is not supported by the module; otherwise, the value returned is the code expected by getparam and setparam. Moreover, the function findparm has to set the paramater type to the type of this parameter. The parameter type is bit encoded using the type constants (XPRM_TYP_INT, XPRM_TYP_REAL, XPRM_TYP_STRING, XPRM_TYP_BOOL) plus the access rights (XPRM_CPAR_READ,
XPRM_CPAR_WRITE): a parameter tagged XPRM_CPAR_READ can be accessed with the getparam function and a parameter tagged XPRM_CPAR_WRITE can be set using setparam.
The two last arguments passed to this function are usual execution contexts while the third one indicates how the function is used: argument why is XPRM_FNDP_MCREAD (0) when the compiler looks for a parameter to be read (with getparam); it is XPRM_FNDP_MCWRITE (1) when the compiler requires a parameter for writing (call to setparam). In both cases the module context libctx is NULL. When the execution of the model is starting and module parameters are set via the model parameter string this function is called with a value of XPRM_FNDP_RTWRITE (2) for why (before a call to setparam). During execution when another module requests the value of a parameter using getdsoparam this function is called with a value of XPRM_FNDP_NIREAD (3) for why. Finally, why takes the value XPRM_FNDP_RTREAD (4) when the function is used after execution by the library function XPRMgetdsoparam.
This service must be defined if the module provides control parameters.
Service ``List of Parameters''
XPRM_SRV_PARLST: void *nextpar(void *ref, const char **name, const char **desc, int *type)
This service is used to display the list of parameters provided by the module (for instance by using the command examine of the command line interpreter). It is called repeatedly until it returns NULL. The first argument is a pointer in the internal table of parameters (handled by the module). When this pointer is NULL, the function has to return the first parameter. The information about the parameter is its name, a textual description desc of its meaning (may be an empty string) and its type (using the same encoding as for the service XPRM_SRV_PARAM). The function must return a pointer to the next entry in the list of parameters that can be used as input for the next call to this function. When the information about the last parameter of the module has been returned, the return value must be NULL.
Service ``Inter-Module Communication Interface''
XPRM_SRV_IMCI: void *imci
This service may be used to implement communication between modules at the C language level. The value of this service (a pointer) is returned by the function getdsoctx. Typically, this pointer references a table of functions which are entry points to the module.
Service ``Module Dependency List''
XPRM_SRV_DEPLST: const char *deplst[]
This service defines a table of modules that are automatically loaded when compiling models using this module. Note that this service is used only at compilation time and the corresponding modules will not be loaded during execution if they are not effectively required by the model. Every entry of this table is the name of a module as it is used with the uses directive. The last entry of the list must be NULL.
Service ``IO Driver List''
XPRM_SRV_IODRVS: XPRMiodrvtab iodrvs[]
This service defines the table of IO drivers implemented by this module. Every entry of this table is a pair ( driver name, table of IO functions ). The driver name corresponds to the identifier to be used in file names and the table of functions lists operations supported by the driver (Section Defining IO drivers). The last entry of the list must be the pair ( NULL,NULL ).
Service ``On Exit''
XPRM_SRV_ONEXIT: void onexit(XPRMcontext ctx, void *libctx, int status)
This service may be useful when some clean up operations have to be performed after the model has been run. The onexit function is called just before the end of the execution of the model if the service reset is implemented and has succeeded (i.e. the module context libctx is not NULL). The status provided here is the execution status of the model: it indicates why processing is terminating.
Service ``Check Restrictions''
XPRM_SRV_CHKRES: int chkres(int restr)
This service must be defined for the module to be loaded when Mosel is running in restricted mode (refer to the Mosel Reference Manual for further details). The chkres function is called just after the module has been initialised such that it can check whether it supports the active restrictions: a return value of 0 indicates that the module will observe the stated restrictions; any other value will cause the module to be unloaded.
The restrictions are passed to this routine through the restr parameter as a bit encoded integer. The following restrictions may be set:
- XPRM_RESTR_NOWRITE
- Disable write access
- XPRM_RESTR_NOREAD
- Disable read access
- XPRM_RESTR_NOEXEC
- Disable routines allowing to execute external commands
- XPRM_RESTR_WDONLY
- Restrict disk access to current working directory
- XPRM_RESTR_NOTMP
- Disable temporary directory
- XPRM_RESTR_NODB
- Disable database access
The Mosel file management routines (like fopen) already enforce read/write restrictions for the files they handle. It is the responsiblity of the module to implement the restrictions when it uses any other file management routines. For instance, accessing directly a file using a system routine should only be done after having checked the validity of the file name with pathcheck; starting a separate process to execute an external command should be disabled if the restriction NOEXEC is active etc.
Service ``Update Version''
XPRM_SRV_UPDVERS: void updvers(int event, int what, int *version)
This service is used by the Mosel compiler only: the function is called whenever a feature of the module (routine,type or parameter) is used by the model being compiled. The first argument indicates why the function is called and how to interpret the second argument (what). Possible values are:
- XPRM_UPDV_INIT
- module loaded for compilation of a model ( what=0) or a package ( what=1)
- XPRM_UPDV_FUNC
- function code what required
- XPRM_UPDV_TYPE
- module type code what required
- XPRM_UPDV_GPAR
- control parameter code what required for getparam
- XPRM_UPDV_SPAR
- control parameter code what required for setparam
- XPRM_UPDV_ENDP
- parser has finished analysing the file. Argument what is 0 if this operation succeeded
The last argument is a pointer to the current version number for this module that will be stored in the BIM file after compilation: the function may update this version number depending on the requirements of the model.
When the function is called for the first time (XPRM_UPDV_INIT) the version number may be 0: in this case, it must be changed to the minimum version supported by the module (that must be accepted by the service CHKVER). When the function is called for the last time (XPRM_UPDV_ENDP), changing the version number will make the parser fail.
Service ``Implied Dependency List''
XPRM_SRV_IMPLST: const char *implst[]
This service defines a table of modules that imply the use of this module. At compile time, the module is added to the dependency list (see Section Service ``Module Dependency List'') of each of the listed modules. Every entry of this table is the name of a module as it is used with the uses directive. The last entry of the list must be NULL.
Service ``Annotations''
XPRM_SRV_ANNOT: const char *anns[]
This service defines a table of global annotations. These annotations are added to any model using the module. Note that when compiling a package only "mc." annotations are processed. In the table an annotation is represented by 2 entries: the annotation name followed by its value (for instance {"mc.def","myann global",NULL}). The last entry of the list must be NULL.
Service ``DSO stream''
XPRM_SRV_DSOSTRE: void* fct_open(XPRMmodel model, int mode, char *opts, char *params, int (*fct_data)(void *fctx, char *buf, int len), int (*fct_close)(void *fctx), char *errmsg, int errmsglen)
This service defines a function to open a stream to the module from a remote instance. The function is called when a remote instance opens the file "mcmd:dsostream dsoname parameters" where dsoname is the name of the target module. The stream to create is characterised by a model reference (that may be NULL), the open mode, the mcmd opening options and the remaining parameters of the open string. If the operation is successful the function must return a non-null context pointer and update the parameter fct_data: for an output stream it will be called whenever data is available for reading (any return value smaller than len is interpreted as an error); for an input stream it will be used when the remote instance is expecting data: in this case up to len bytes have to be copied into buf and the return value is the amount of data sent. A return value of 0 indicates an end of stream and a negative value an error. Optionally fct_close may also be set: it will be used when the stream is closed to release its resources. In case of error the function must return NULL and copy an error message into the provided buffer.
Service ``Required types''
XPRM_SRV_REQTYPS: const char *reqtyp[]
This service defines a table of native types that are required by a module and that must be available in order to be able to execute models using this module. Every entry of this table is the name of a type (e.g. "text") optionally prefixed by the name of the module defining it (e.g. "mmsystem.text"). The last entry of the list must be NULL. Note that the modules defining the required types must be first listed in the dependency list (see Section Service ``Module Dependency List'').
Service ``Provider''
XPRM_SRV_PROVIDER: const char *prov
This service defines the identity of the provider for this module as returned by the function getdsoprop for the property XPRM_PROP_SYSCOM.
Service ``Namespace groups''
XPRM_SRV_NSGRP: const char **nsgrps
This service may be used to define namespace groups from a module similar to those created via the nsgroup contruct in the language. The data of this service is an array of constant strings terminated by a NULL reference. Each record consists in two strings: the first one is the name of the namespace and the second defines the list of packages belonging to the group (package names separated by comas without space). If this list is empty the namespace can be used by any package.
Service ``Memory use''
XPRM_SRV_MEMUSE: size_t memuse(XPRMcontext ctx, void *libctx, void *ref, int code)
This routine is used by Mosel to report memory usage of the entire module or of an instance of one of its type. When it is called with NULL for ref and 0 for code the function is expected to return the total amount of memory the module has allocated so far for the running model. When ref is not NULL it is a reference to an entity of type code (internal code as specified in the table of types, see Section Table of types) and the function has to return the amount of memory used by this entity. If the information is not available -1 must be returned.
Service ``Static module''
XPRM_SRV_STATIC: XPRM_STATIC_INST|XPRM_STATIC_PROC
If this service is defined and its associated value is the constant XPRM_STATIC_INST the module is handled as a static module: it will not be unloaded (even if it is no longer required) until the Mosel library itself is released. If the value is XPRM_STATIC_PROC the module will never be unloaded (although the service unload will be called when the Mosel library is released).
Service ``Get array indices''
XPRM_SRV_ARRIND: int getarrind(XPRMcontext ctx, void *libctx, void *ndx, int code, XPRMarray arr, int *indices, int op)
This routine is used when Mosel needs to access an array dereferenced via an array indexer: the function is expected to return in indices the tuple of indices corresponding to the data stored in ndx for the array arr. The parameter ndx is a reference to an entity of type code (internal code as specified in the table of types, see Section Table of types). The last parameter, op, identifies the kind of access to be performed: XPRM_OPNDX_EXISTS (call to exists), XPRM_OPNDX_DEL (call to delcell), XPRM_OPNDX_GET (get the value of the cell) or XPRM_OPNDX_SET (set the value of the cell). The returned information must be suitable for using a function like getarrval for instance. The function must return 0 on success, a positive value in case of an out of range and a negative value to notify an error.
Service ``Deprecation List''
XPRM_SRV_DEPREC: unsigned int deprec[]
This service consists in a list of pairs of unsigned integers sorted in ascending order on the first component of each pair. The last entry of the list must be 0,0. The first component of each pair is interpreted as a subroutine code (see Section Table of functions) and the second as a version number encoded using XPRM_VERSION: this is the version number from which the corresponding routine is deprecated. The compiler will use this information for generating deprecation warning messages.
Service ``Minimum compatible version''
XPRM_SRV_COMPAT: int versnum
This service may be used to define the smallest version compatible with the current module. This can be used as a replacement for the service CHKVER: a version number is considered compatible if it is between the value of this service and the current version of the module. The XPRM_MKCOMPAT macro can be used to initialise this service (e.g. XPRM_MKCOMPAT(1,0,0)).
Defining IO drivers
Mosel accesses files through IO drivers: a driver provides a set of functions implementing basic IO operations (open, close, read a block, write a block). As a consequence, any kind of data source may be exposed as a file (or stream) in the Mosel environment as long as it can be accessed through the basic operations listed above.
IO drivers are declared using the XPRM_SRV_IODRVS service which points to a table of drivers. Each driver is identified by its name and a table of functions. This table of functions is of the form ( operation code, function ) which associates to each code a function performing the operation (except for operation XPRM_IOCTRL_INFO which is only a text string describing the driver). The last record of this table must be the pair ( 0,NULL ).
Example:
static XPRMiofcttab mydrv_fcts[]= { {XPRM_IOCTRL_OPEN, mydrv_open}, {XPRM_IOCTRL_CLOSE, mydrv_close}, {XPRM_IOCTRL_READ, mydrv_read}, {XPRM_IOCTRL_WRITE, mydrv_write}, {XPRM_IOCTRL_INFO, "mydrv description"}, {0,NULL} }; static XPRMiodrvtab iodrivers[]= { {"mydrv", mydrv_fcts}, {NULL, NULL} }; static XPRMdsoserv tabserv[]= { {XPRM_SRV_IODRVS, iodrivers} };
In addition to basic operations, a driver may provide implementations for the initializations block, file removal and file renaming. The ``initializations from/to'' routines get direct access to Mosel internal data and can therefore work at the binary level. The other file operations may be required for resource management.
All functions performing IO operations are sent a Mosel execution context. However, this context may be NULL if the stream is used outside of the execution of a model (for instance for compiling a model source). It is not necessary to provide an implementation for all of the possible operations. However, the open function and one of the data transfer routines are mandatory (read or write or initfrom or initto).
Operation ``Open Stream''
XPRM_IOCTRL_OPEN: void *open(XPRMcontext ctx, int *mode, const char *fname, unsigned int *enc, int *bufsize)
This function is called to open a stream associated to the given file name (the provided file name does not include driver name). Parameter mode is a pointer to the open mode. This value is bit encoded and the following bits may be set:
- XPRM_F_BINARY
- open in binary mode (default is text mode)
- XPRM_F_WRITE
- open for writing (default is for reading)
- XPRM_F_APPEND
- when open for writing, do not reset file but append data to the end of the original file
- XPRM_F_ERROR
- when open for writing, stream will be used as an error stream.
- XPRM_F_LINBUF
- when open for writing, stream is line buffered: buffer is flushed after each line (by default streams are flushed when full or when an explicit flush is executed)
- XPRM_F_INIT
- stream open for an `initialisations' block
- XPRM_F_SILENT
- stream open in silent mode (error messages are not displayed)
- XPRM_F_DELCLOSE
- delete file after the stream has been closed
These modes may be combined (for instance XPRM_F_WRITE|XPRM_F_LINBUF) so testing the mode should be done using masks. The open function may alter the mode by changing some bits (LINBUF, INIT and SILENT; others are ignored) and by using bits 16 to 31 that are not used by Mosel although saved with the stream's mode (which can be retrieved later using function fgetinfo and is provided to the close function). Note the particular meaning of INIT: if the function is called with this bit set, the stream will be used for an initialisations block. Resetting this bit indicates that the driver provides an handler for this operation and expects that Mosel will use this handler. Otherwise, even if the driver publishes the operation, the default procedures are used.
Mosel saves the current input and output streams after execution of the open function if new files have been opened using fopen. These current streams are restored when calling operations ``close'', ``read'', ``skip'', ``write'', ``init from'' and ``init to''. This is useful when the stream implements a filter (i.e. it preprocesses some data actually stored in another file).
The parameter enc is the encoding used when the file is open in text mode (i.e. flag XPRM_F_BINARY is not set). If no encoding has been explicitly stated in the file name (using the "enc:" prefix) the value of this variable is XPRM_FE_ENCDEF (that is equivalent to UTF-8). Otherwise this value consists in 2 16-bits words: the first part is the encoding ID as used by the XPRNLS library (please refer to the XPRNLS Reference Manual for further explanation), it can be extracted using the mask XPRM_FE_MSK_ENC. The second part of the value is used to record the encoding options (XPRM_FE_STRICT, XPRM_FE_UNIX, XPRM_FE_DOS, XPRM_FE_BOM, XPRM_FE_NOBOM). The function may modify the encoding or disable it entirely by switching to binary mode.
The parameter bufsize is the size (in kilobytes) of the internal buffer allocated for the stream. A driver can change this setting that must remain between 2 (the default value) and 64.
The return value of this function will be used as input for other IO functions. A return value of NULL indicates a failure: in this case, the function setioerrmsg may be used to set a descriptive text for the error that is displayed after the function returns (if the stream is not open in silent mode).
Operation ``Close Stream''
XPRM_IOCTRL_CLOSE: int close(XPRMcontext ctx, void *stream, int mode)
This optional function is called to close a stream previously open using the corresponding open function (after it has been flushed if it is an output stream). The second parameter is the pointer returned by a successful execution of the ``open'' function and the third parameter is the current mode of the stream. If an error has been detected on this stream, the bit XPRM_F_IOERR is set.
This function must return 0 in case of success, all other values are interpreted as error codes. In the later case, the function setioerrmsg may be used to set a descriptive text for the error that is displayed after the function returns (if the stream is not open in silent mode).
Operation ``Read Block''
XPRM_IOCTRL_READ: long read(XPRMcontext ctx, void *stream, void *buffer, unsigned long size)
This function is used to load the stream buffer open for reading. Parameters buffer and size define location and size of the buffer associated to the stream. This function returns the number of bytes actually copied into the buffer. A value of 0 characterises an end of file and a negative value is interpreted as an error. In the later case, the function setioerrmsg may be used to set a descriptive text for the error that is displayed after the function returns (if the stream is not open in silent mode).
Operation ``Skip Block''
XPRM_IOCTRL_SKIP: int skip(XPRMcontext ctx, void *stream, int size)
This optional function is used to skip a block of data from an input stream. If the operation is successful a positive value has to be returned; the special value -2 indicates that the corresponding stream does not support the operation. In this case the system will read (and discard) the number of bytes to be skipped. A negative value is interpreted as an error and the function setioerrmsg may be used to set a descriptive text for the error that is displayed after the function returns (if the stream is not open in silent mode).
Operation ``Write Block''
XPRM_IOCTRL_WRITE: long write(XPRMcontext ctx, void *stream, void *buffer, unsigned long size)
This function is used to flush the stream buffer open for writing. Parameters buffer and size define location and size of the buffer associated to the stream. This function must return a positive value if successful - any negative value or zero is interpreted as an error status. In the later case, the function setioerrmsg may be used to set a descriptive text for the error that is displayed after the function returns (if the stream is not open in silent mode).
Operation ``Initializations From''
XPRM_IOCTRL_IFROM: int initfrom(XPRMcontext ctx, void *stream, int nbrec, const char **labels, int *types, XPRMalltypes **adrs, int *nbread)
This function implements an initializations from block. For this function to be called, the open function must reset the INIT bit of the open mode. The information provided describes the block to be processed: number of records nbrec, for each record i its label labels[i], its type types[i] and its address adrs[i] (direct address for Mosel objects and indirect address for objects of external types). If a label is associated to a tuple of arrays, the corresponding address is a list of arrays.
If auto finalization is in use (this is indicated by the control parameter "autofinal", see getparam), sets must be finalized using fnlset just after they have been initialized. Also, in order to handle the case of index sets of implicit dynamic arrays, it is necessary to call beginarrinit before the initialization of each array and endarrinit after the process has completed.
During its execution the function should set to NULL each entry i of the adrs array for which reading has been completed and store in nbread[i] the number of successfully input lines (i.e. when reading a tuple of arrays, 1 line means 1 value for each array). The return value of initfrom is interpreted as the number of records that have not been successfully read in (i.e. 0 for success). In case of an IO error the function should return -1.
Operation ``Initializations To''
XPRM_IOCTRL_ITO: int initto(XPRMcontext ctx, void *stream, int nbrec, const char **labels, int *types, XPRMalltypes **adrs)
This function implements an initializations to block. For this function to be called, the open function must reset the INIT bit of the open mode. The information provided describes the block to be processed: number of records nbrec, for each record i its label labels[i], its type types[i] and its address adrs[i] (direct address for Mosel objects and indirect address for objects of external types). If a label is associated to a tuple of arrays, the corresponding address is a list of arrays.
During its execution the function should set to NULL each entry i of the adrs array for which saving has been completed. The return value of initto is interpreted as the number of records that have not been successfully saved (i.e. 0 for success). In case of an IO error the function should return -1.
Operation ``Remove File''
XPRM_IOCTRL_RM: int remove(XPRMcontext ctx, const char *todel)
This function is called to remove (or delete) a file. Parameter todel is the name of the file to be deleted (IO driver name is not included). Mosel does not check whether the file is currently open: depending on the driver this may make the operation impossible and should be checked by the function if necessary. The convention for return values is as follows: 0 indicates a success, 1 if the file cannot be accessed and 4 if the operation is not possible (e.g. file open or protected).
Operation ``Move File''
XPRM_IOCTRL_MV: int move(XPRMcontext ctx, const char *src, const char *dst)
This function is called to move (or rename) a file. Parameters src and dst are names of the source and destination files (IO driver name is not included). Mosel does not check whether the files are currently open: depending on the driver this may make the operation impossible and should be checked by the function if necessary. The convention for return values is as follows: 0 indicates a success, 1 if the source file cannot be accessed, 2 if destination file cannot be open for writing, 3 if the copy failed and 4 if the source cannot be removed after copy. The special value -1 can be used to tell Mosel to perform the operation by deleting the file after having made a copy of it.
Operation ``File Size''
XPRM_IOCTRL_SIZE: size_t fsize(XPRMcontext ctx, const char *file)
This function is called to retrieve the size of a file. Parameter file is the name of the file to be considered (IO driver name is not included). Mosel does not check whether the file is currently open: depending on the driver this may make the operation impossible and should be checked by the function if necessary. On error (size_t)-1 must be returned.
Static modules
Modules are usually implemented as dynamic libraries: modules are represented as files that Mosel loads when required. It is also possible to embed a module into a program using the Mosel Libraries. In this case, the module must be registered in Mosel before it is used by any model. This registration is performed by a call to the function XPRMregstatdso which receives as parameters the name of the module (this is the name one uses to request the module in a uses directive in the model) and the reference to the initialization function of this module. The registration function initializes immediately the module by calling its initialization function and records the module as a static module (it cannot be unloaded). Note that the type of an initialization function of a static module is int instead of DSO_INIT.
Example:
#include "xprm_mc.h" #include "xprm_ni.h" int mymodule_init(XPRMnifct nifct, int *interver, int *libver, XPRMdsointer **interf); ... int main() { XPRMinit(); XPRMregstatdso("mymodule", mymodule_init); /* Now the module "mymodule" is available */ XPRMcompmod(NULL, "test.mos", NULL, NULL); ... } /* Functions of the module must be included in the program */ ...
© 2001-2025 Fair Isaac Corporation. All rights reserved. This documentation is the property of Fair Isaac Corporation (“FICO”). Receipt or possession of this documentation does not convey rights to disclose, reproduce, make derivative works, use, or allow others to use it except solely for internal evaluation purposes to determine whether to purchase a license to the software described in this documentation, or as otherwise set forth in a written software license agreement between you and FICO (or a FICO affiliate). Use of this documentation and the software described in it must conform strictly to the foregoing permitted uses, and no other use is permitted.