Initializing help system before first use

Example: code generation

In this section we present a driver that includes the output of Mosel into a C program. It will typically be used for generating a C program that includes the BIM file of a given model, for example using a command like

mosel comp mymodel.mos -o export.toC:mymodel.c

As with all Mosel I/O drivers, the toC driver may be combined with other drivers. For instance, we may use the compress driver of the zlib module to obtain a compressed BIM file within the generated C program:

mosel comp mymodel.mos -o zlib.compress:export.toC:mymodel.c

Note: The deploy module that comes as a part of the standard Mosel distribution has been designed for the specific task of generating a C program that includes the BIM file of a given model; the command

mosel comp mymodel.mos -o deploy.csrc:runmymodel.c

has the same purpose as the use of 'export.toC' shown above. However, the main interest of the deploy module is the possibility to generate immediately a standalone executable to run a model:

mosel comp mymodel.mos -o deploy.exe:runmymodel

Implementation

The module export that defines the toC I/O driver has the following components (common to all I/O driver modules):

  • Interface structures
  • Module initialization function
  • Implementation of driver access functions

The file export.c implementing the module export with the I/O driver toC starts with the definition of the C code template, divided into three parts:

static char prg_part1[]=
"#include <stdio.h>\n"
"#include \"xprm_rt.h\"\n\n"

"static unsigned int bimfile[]={";

static char prg_part2[]=
"0};\n\n"

"int main(int argc,char *argv[])\n"
"{\n"
" char modname[40];\n"
" XPRMmodel mod;\n"
" int rts;\n\n"

" rts=XPRMinit();\n"
" if((rts!=0)&&(rts!=32))\n"
" {\n"
"  char msg[512];\n\n"

"  XPRMgetlicerrmsg(msg,512);\n"
"  fprintf(stderr,\"%s\",msg);\n"
"  return 1;\n"
" }\n\n"

" sprintf(modname,\"mem:%p/%u\", bimfile,";

static char prg_part3[]=
");\n"
" if((mod=XPRMloadmod(modname,NULL))==NULL)\n"
"  return 2;\n"
" if(XPRMrunmod(mod,&rts,NULL))\n"
"  return 3;\n"
" else\n"
"  return rts;\n"
"}\n";

The generated output will be inserted in between parts 1 and 2, its size will be inserted in between parts 2 and 3.

The definition of the NI interface structures follows the same scheme like what we have seen in the previous example: the table of driver functions, the table of drivers, the table of services, the main DSO interface table, and an address for storing the NI function table. The toC driver implements `open', `close', and `write' functionality and works with generalized files.

static void *toc_open(XPRMcontext ctx, int *mode, const char *fname);
static int toc_close(XPRMcontext ctx, s_tocdata *td, int mode);
static long toc_write(XPRMcontext ctx, s_tocdata *td, char *buffer,
                                                        unsigned long size);
static XPRMiofcttab iodrv_toc[]=
        {
         {XPRM_IOCTRL_OPEN, (void *)toc_open},
         {XPRM_IOCTRL_CLOSE, (void *)toc_close},
         {XPRM_IOCTRL_WRITE, (void *)toc_write},
         {XPRM_IOCTRL_INFO, "extended_filename"},
         {0,NULL}
        };
                                 /* Drivers of module `export': toC */
static XPRMiodrvtab iodrv_export[]=
        {
         {"toC", iodrv_toc},
         {NULL,NULL}
        };
                                 /* Table of services: only I/O drivers */
static XPRMdsoserv tabserv[]=
        {
         {XPRM_SRV_IODRVS, iodrv_export}
        };
                                 /* DSO interface: only services */
static XPRMdsointer dsointer=
        {
         0,NULL,
         0,NULL,
         0,NULL,
         sizeof(tabserv)/sizeof(mm_dsoserv),tabserv
        };

static XPRMnifct mm;             /* For storing Mosel NI function table */

This module's initialization function performs the standard module initialization procedure where the module's interface structure is passed on to Mosel and the NI function table is saved for use by this module.

DSO_INIT export_init(XPRMnifct nifct, int *interver, int *libver,
                     XPRMdsointer **interf)
{
 mm=nifct;                       /* Save the table of Mosel NI functions */
 *interver=XPRM_NIVERS;          /* The interface version we are using */
 *libver=XPRM_MKVER(0,0,1);      /* The version of the module: 0.0.1 */
 *interf=&dsointer;              /* Our interface */
 return 0;
}

The functions implemented by this I/O driver are restricted to opening and closing a file and writing to a file. The `open' and `close' functions print the predefined part of the generated C file. The function for writing the binary part of the output (toc_write) is somewhat tricky since it needs to make sure that all bytes are aligned correctly. This function is called with a buffer as parameter. For every four bytes it prints the corresponding integer. Since the buffer size is not necessarily a multiple of 4 there may be a few bytes left over at the end of the buffer which must be carried over to the next call to this function or to the `close' function. Below we print the `open' and `close' functions. The definition of the `write' function is left out here, the interested reader may be reminded that the complete module source is a part of the Mosel module examples.

static void *toc_open(XPRMcontext ctx, int *mode, const char *fname)
{
 s_tocdata *td;
                                       /* First time: open actual file */
 if(mm->fopen(ctx,*mode,fname)<0)
 {
  *mode|=XPRM_F_SILENT;                /* Error message already displayed */
                                       /* switch so silent mode */
  return NULL;
 }
 else
 {
  td=(s_tocdata *)malloc(sizeof(s_tocdata));
  td->total=0;
  td->nb_remain=0;
  td->nb_printed=0;
  mm->printf(ctx, "%s\n", prg_part1);  /* Display the beginning of program */
  return td;
 }
}

static int toc_close(XPRMcontext ctx, s_tocdata *td, int mode)
{
 if(td->nb_remain>0)                   /* Send bytes to be written */
 {
  td->total+=td->nb_remain;
  for(;td->nb_remain<4;td->nb_remain++)
   td->map.c[td->nb_remain]=0;
  mm->printf(ctx, "%#x, ", td->map.i);
 }                                     /* and complete the program */
 mm->printf(ctx, "%s%u%s", prg_part2, td->total, prg_part3);
 return mm->fclose(ctx,mode);
}

These two functions work with the following structure for storing information concerning the printed output. Function `open' initializes the structure (td), function `write' copies the remaining bytes from every buffer into this structure and sets the counters, and function `close' checks whether there is anything left to be printed before completing and closing the output file.

typedef struct
        {
         unsigned int total;      /* Total number of bytes written so far */
         union
         {
          char c[4];
          unsigned int i;
         } map;                   /* Mapping between 4 chars and an integer */
         int nb_remain;           /* Number of bytes not yet written */
         int nb_printed;          /* Number of numbers written on the line */
        } s_tocdata;

© 2001-2019 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.