User Functions

It is possible to include your own functions within Gsharp. These functions can be written in either C or FORTRAN and can be used to get data in or out of Gsharp or to process data already within Gsharp.

There are two steps required to include these functions

  1. Writing the function - see below
  2. Compiling the function
  3. Relinking Gsharp

A full list of functions that can be used in writing your user function can be found in the Reference Manual.

Writing The Function

The source for a skeleton user function file is provided in UserFunc.c in the $UNIDIR\misc\Gsharp directory.

Datum Structure

All I/O parameters to a user-defined function are passed as a structure called Datum. This structure is defined as follows: 

typedef struct {
  char *name;
  DatumDimension dimension;
  Datacode type;
  VarPointer *dp;
} *Datum;

An example of a user-defined function will illustrate how this structure is used. 

To define the GSL function: 

import_user(filename, data_type, rec_type) 

This function declaration is added to UserFunc.c.

static Stack IMPORT_USER( int argc, Stack *argv) 
{
/*
* parameters:
*
*/
Datum filename; /* filename (required) */
Datum rec_type = NULL; /* 1-info,2-coor, 3-measurements */
Datum data_type = NULL; /* 1-float, 2-string */

Parameters are passed to user functions via the pointer stack variable argv. Depending on the function, the parameter(s) are either required or optional. Required parameters must be specified, otherwise Gsharp will generate an error when it parses the function reference. 

The function in following example has one required parameter and two optional parameters. The variable filename and pointer to a datum structure is initialized with the following assignment. 

filename = *argv++;

This next two parameters are optional, so a test must be done to see if they are present:

if( argc >= 2) rec_type = *argv++;
if( argc >= 3) data_type = *argv+0+;

I/O parameters are defined as required when the user function is registered with Gsharp. Refer to Registering User-defined Functions for details.

Variables are referenced through the structure member dp. For example, the first parameter could be reference (printed) with this statement:

printf("import_user: filename=%s\n",*(char**)filename->dp);

Registering User-defined Functions 

All user-defined functions must be registered with Gsharp by calling the function InstallCFunction from UserFunc.c. InstallCFunction has the following parameters:

InstallCFunction(
  (string) gsl_name,
  (pointer) c_function,
  (int) return_type,
  (int) MinNumArgs, 
  (int) MaxNumArgs,
  (int) arg_type, ByRef,
  ... 
  (int) arg_type, ByRef, 
  (int) AEND
)

return_type and arg_type must be set to one of the following enumerated types: RealData, TextData, DateData, or TimeData. 

MinNumArgs specifies the number of required arguments to the function. If at least this number of arguments are not present, Gsharp issues a warning when parsing the GSL reference - the C function will not be called. 

MaxNumArgs is set to 0 if the number of arguments is unlimited, or it is set to n if the number of arguments of fixed.

The example below demonstrates how InstallCFunction is used to register a function with one required and two optional arguments. 

InstallCFunction("import_user",&IMPORT_USER, RealData, 1, 3,
  TextData, ByRef,
  RealData, ByRef,
  RealData, ByRef,
  AEND
);

Managing the Output of User-defined Functions

User functions can output messages to the Gsharp message area (and message log file if message logging is on) using the functions UpgMsgHandler or CLRuntimeMessage. CLRuntimeMessage always writes its output, while UpgMsgHandler output is filtered according to the message status set by the function set_messages.

Example

UpgMsgHandler(
(int) message_type,
(char*)message 
)

UpgMsgHandler(CL_USER,"File could not be opened\n");

Example

sprintf(buf, "%s\n", "no data read");
CLRuntimeMessage(CL_INFO, buf);

Some Examples

static Stack GETENV (int argc, Stack *argv)
{
  Datum d1, dt;
  VarString str, env;

  d1 = *argv;
  dt = VarCreate(TextData, Scalar);

  env = VarGetStringData(d1, 1, 1, 1);

  if(env && (str = UpgGetEnv(env)))
  VarSetStringData(dt, 1, 1, 1, str);

  return dt;
}
static Stack PUTENV(int argc, Stack *argv)
{
  Datum d1, dt;
  VarString str, env;

  d1 = *argv;
  dt = VarCreate(RealData, Scalar);

  env = VarGetStringData(d1, 1, 1, 1);

  if(env && putenv(strdup(env)))
  VarSetRealData(dt, 1, 1, 1, 1.0);

  return dt;
}
/* Convert an array of floats into a string. e.g. "1:4,8:9,11"  */
static Stack CREATEEXPRESSION(int argc, Stack *argv)
{ Datum d1, dt;
  char out[10024], num[10];
  int i, nlines;
  VarFloat *vals;

  d1 = *argv;

  nlines = VarNumRows(d1);
  vals = (VarFloat *) VarCopyData(d1);

  sprintf(out, "%g", vals[0]);
  for (i=1; i<nlines; i++)
  { if (vals[i]==vals[i-1]+1 )
    { if ((i<nlines-1) && (vals[i+1]==vals[i]+1)) continue;
      else strcat(out,":");
    }
    else
    { strcat(out,",");
    }
    sprintf(num,"%g",vals[i]);
    strcat(out,num);
    if (strlen(out)>10000) break; 
  }
  dt = VarCreate(TextData, Scalar);
  VarSetStringData(dt,1,1,1,out);
  if (VarNumRows(d1)==1 && vals[0]>999.99 && vals[0]<999.9999)
  VarSetStringData(dt,1,1,1,"");
  free(vals);
  return dt;
}

/* Return the elements of d1 that can be found in d2 */
static Stack VALIDLINES(int argc, Stack *argv)
{ Datum d1, d2, dt;
  DatumDimension dim;
  VarFloat *arr1, *arr2;
  VarFloat *out;
  int i, j, n, n1, n2;

  d1 = *argv++; d2 = *argv;
  n2 = VarNumRows(d2); n1 = VarNumRows(d1);
  n = 0;

  out = (VarFloat *) calloc(n1,sizeof(int));
  arr1 = VarCopyData(d1);
  arr2 = VarCopyData(d2);

  for(i=0; i<n2; i++)
    for(j=0; j<n1; j++)
      if (arr1[j] == arr2[i])
        out[n++] = (VarFloat) j+1;
  free(arr1); free(arr2);
  if (n>n1)
  { puts("Overflow in VALIDLINES");
  }
  if (n==0)
  { n=1; out[0]=999.999;
  }

  dim[0] = n; dim[1]=dim[2]=1;
  dt = VarCreate(RealData,dim);

  for (i=0; i<n; i++)
    VarSetRealData(dt,i+1,1,1,out[i]);

  free(out);

  return dt;
}

static Stack SHOWHOURGLASS(int argc, Stack *argv)
{ Datum d1, dt;
  void GshShowHourglass(), GshRemoveHourglass();

  d1 = *argv;
  if (VarGetRealData(d1, 1, 1, 1) == 0.0)
    GshRemoveHourglass();
  else
    GshShowHourglass();

  dt = VarCreate(RealData, Scalar);
  VarSetRealData(dt, 1, 1, 1, 0.0);
  return dt;
}
void RegisterUserFunc(void)
{
  InstallCFunction("getenv", &GETENV, TextData, 1, 1, 
    TextData, ByRef,
    ANAMES, "environment_variable",
    AMSGS, 0, 
    ACAT, FInterface,
    AFMSG, MSG_DP_F_GETENV,
    AEND);
  InstallCFunction("putenv", &PUTENV, RealData, 1, 1,
    TextData, ByRef,
    ANAMES, "environment_assignment",
    AMSGS, 0, 
    ACAT, FInterface,
    AFMSG, MSG_DP_F_PUTENV,
    AEND);
  InstallCFunction("CreateExpression",&CREATEEXPRESSION, TextData, 1, 1,
    RealData, ByRef,
    ACAT, FUser,
    AEND);
  InstallCFunction("ValidLines",&VALIDLINES, RealData, 2, 2,
    RealData, ByRef,
    RealData, ByRef,
    ACAT, FUser,
    AEND);
InstallCFunction("ShowHourglass",&SHOWHOURGLASS, RealData, 1, 1, 
    RealData, ByRef,
    AEND);