Contexts and the Mosel stack
The implementation of a new subroutine (function ar_getsol in the previous section) introduces several notions that may require further explanation: the Mosel and module contexts and the Mosel stack.
Mosel and module contexts
Any library function that implements a subroutine (or operator, as shown later in this document) takes as arguments the Mosel and the module contexts. The Mosel context communicates the current state of the Mosel program in question. This is necessary because several models may be executed simultaneously. Consequently, most functions of the Native Interface take the Mosel context as their first argument.
A module may also have a context of its own. The context of a module may be any structure that saves information about the current state of the module. Defining a module context becomes necessary when any information needs to be preserved between different calls to functions of the module during the execution of a model. In the examples discussed so far in this document (definition of constants and subroutines) this is not the case, so we do not use this parameter. Typical uses for a module context are to save the current values of control parameters published by the module or to keep track of memory allocated by the module during the execution of a model so that it may be freed at its termination. In the following chapters we give examples of these uses.
Working with the Mosel stack
In the case of a C library function that defines a subroutine for the Mosel language, we need to obtain the values of its parameters that have been specified in the model. The prototype for such library functions as fixed by Mosel does not allow any parameters to be passed directly; instead, the parameter values, and also the return value (if the implemented subroutine is a function), are communicated via the stack of Mosel.
The stack is accessed via the stack access macros XPRM_POP_type where type is one of
- INT
- an integer or Boolean (C type int),
- REAL
- a real value (C type double),
- STRING
- a string (C type const char*),
- REF
- any reference.
The parameter values need to be taken in the same order as they appear in the subroutine in the Mosel program. For example, if we want to implement a procedure do_something with the following prototype
procedure do_something(val1:real, num:integer, arr:array(range) of mpvar, val2:real)
we need to take the parameters in the following order from the stack (ctx is the Mosel context):
XPRMarray arr; int i; double r1,r2; r1=XPRM_POP_REAL(ctx); i=XPRM_POP_INT(ctx); arr=XPRM_POP_REF(ctx); r2=XPRM_POP_REAL(ctx);
In the example above where we implement a procedure, there is no return value. In the case of a function, the returned value must be put onto the stack using another type of stack access macro: XPRM_PUSH_type where type is one of the 4 types listed above. To implement a function with the prototype
function return_two:integer
that simply returns the integer value 2, we write the following:
static int my_return_two(XPRMcontext ctx,void *libctx) { XPRM_PUSH_INT(ctx, 2); return XPRM_RT_OK; }