Next: compile_dos Up: User Function Help File Previous: c_macros   Contents


Type userfunhelp(user_fun) for information on the structure of user
Type userfunhelp(callback_fun) for information on the structure of user
  functions making "call backs" to MacAnova.
Type userfunhelp(arginfo_fun) for information on how to enable automatic
  checking of arguments to a user function.

Keywords: user functions, coding, sample source
This topic provides a brief introduction to the form of a user function
that makes "call backs" (executes functions internal to MacAnova).
Because of the inherent dependence on the computer and operating system,
there are many details that are not covered here.  Additional details
may be found in topics compile_dos, compile_mac, compile_unx and

It presumes familiarity with topic user_fun which describes the
structure of user functions not making call backs.

See headerfile Userfun.h distributed with MacAnova for C macros that are
helpful in writing user functions.

See loadUser() and User() for information on how to load and execute a
user function.

See topic arginfo_fun for information on how to make it possible for
MacAnova to obtain information about a user function for automatic
argument checking.

Since we have no experience in making call backs from a Fortran routine,
no Fortran tips are given.

In the following, 'handle' is used in the Macintosh OS sense, as a
pointer to a pointer.

          Structure of user functions calling back to MacAnova
In addition to regular arguments (pointers or handles to data or
symbols; see topic user_fun), a user function that calls back to
MacAnova must have an extra argument providing a list of functions that
can be called.  This is either a pointer (non-Macintosh) or handle
(Macintosh) to a MacAnovaCBS structure (defined in header file
dynload.h, included by header file Userfun.h).

Example of non-Macintosh declaration
  void fooclbck(char * m, MacAnovaCBS * funlist)
  void fooclbck(char * m, MacAnovaCBSPtr funlist)

Example of Macintosh declaration
  void fooclbck(char ** m, MacAnovaCBS ** funlist)
  void fooclbck(char ** m, MacAnovaCBSH funlist)

Here is the current definition of a MacAnovaCBS structure taken from
header file dynload.h:

  typedef struct MacAnovaCBS
      void          (*print)(char *);
      void          (*alert)(char *);
      Symbol **     (*eval)(char **);
      long          (*ismissing)(double *);
      void          (*seterror)(long);
      void *        (*findfun)(char *);
  } MacAnovaCBS, *MacAnovaCBSPtr, **MacAnovaCBSH;

All the components are pointers to single argument functions internal to

'print' points to mvPrint which expects a pointer to null terminated
character vector (a "string") as argument. It inserts the string in the
MacAnova output stream, usually the screen or command/output window.
Virtually all MacAnova output is printed with mvPrint.  If output is
being spooled to a file (see spool()), mvPrint correctly handles it.

'alert' points to mvAlert() which expects a pointer to a string as
argument.  In a windowed version (Macintosh, Windows, Motif), this
displays the string in a dialog box.  In other versions, 'alert' is
equivalent to 'print'.

'eval' points to mvEval which expects a handle to a character string
(type char **) as argument.  mvEval evaluates this string as if it were
input to MacAnova, almost as if it were the text of a macro, and returns
a handle of type Symbolhandle as value.  Just as in a macro, this is the
value of the last expression evaluated.  C macros in dynload.h allow
access to the type (REAL, CHARACTER, ...), dimension and value of the
value returned by mvEval.  The argument to mvEval must be a handle (char
**) even in a non-Macintosh user function.

'ismissing' points to mvIsmissing which expects a pointer to double
(double *) as argument.  If x is a pointer to a double vector,
mvIsmissing(&x[i]) (or mvIsmissing(x+i)) returns 1 if x[i] is MISSING
and 0 otherwise.

'seterror' points to mvSeterror which expects a long integer as
argument.  mvSeterror(code) sets a variable that will be checked by
User() on return.  If the value is non-zero User() treats it as an
error.  Unless code = silentCallbackError (defined whenever MacAnovaCBS
is defined), User will print the value.

'findfun' points to mvFindfun which expects a pointer to a string
containing the name of an internal MacAnova function as argument.
mvFindfun returns a pointer to void (C type void *) which must be cast
to a function pointer of the appropriate type.  A NULL return value
indicates the function could not be found.

On some systems, you may be able to access any function known to
MacAnova; on others, the available functions are limited to those in a
short list.  The functions available always include the following
functions that can be used to allocate and de-allocate memory and to
create and decode character strings.  Additional functions may be
available on other systems.

  char ** mygethandle(long n)             Allocate n bytes of memory
                                          and return handle to the
                                          space allocated
  void mydisphandle(char ** x)            De-allocate memory referenced
                                          by handle x
  char ** mygrowhandle(char **x, long n)  Allocate n bytes, copy at most
                                          n bytes of x to it and then
                                          de-allocate x.
  int sprintf(char * bf, char * fmt, ...) Formatted "print" to buffer bf
  int sscanf(char * bf, char * fmt, ...)  Formatted "scan" of buffer bf

C types for these functions are defined in header file Userfun.h so that
you can use the following to declare local pointers to them:

  mygethandletype      mygethandle;
  mydisphandletype     mydisphandle;
  mygrowhandletype     mygrowhandle;
  sprintftype          sprintf;
  sscanftype           sscanf.

See below for an example.

Memory management functions mygethandle, mydisphandle and mygrowhandle
work with handles (pointers to pointers) in all versions.

The char ** arguments to mydisphandle and mygrowhandle must have been
allocated by mygethandle.

On a Macintosh, although memory is allocated using Macintosh OS function
NewHandle, the values returned by mygethandle and mygrowhandle cannot be
used as handle arguments to Macintosh OS functions such as DisposHandle.

It appears that calling back to these sprintf and sscanf is the only way
to use them in the protected mode DOS version; in other versions, you
can probably use them directly.

In writing a 68K Macintosh user function, to ensure correct compilation,
all declarations of call back and arginfo functions must be bracketed by

  #pragma mpwc on
  #pragma mpwc off

This is because the released 68K versions of MacAnova are compiled using

Here is an example of a function that calls back to MacAnova.  It uses
mvEval to evaluate its first argument as a command and then uses call
back functions to print a message describing the result of the
evaluation.  A typical use might be User("fooeval","sqrt(2*PI)").

Non-Macintosh version:

  #include "Userfun.h"

  void fooeval(char * commandarg, MacAnovaCBSPtr funlist)
      char              **commandH = &commandarg;
      Symbolhandle        result;
      char                line[200];
      void              (*mvPrint)(char *) = funlist->print;
      void              (*mvAlert)(char *) = funlist->alert;
      long              (*mvIsmissing)(double *) = funlist->ismissing;
      Symbolhandle      (*mvEval)(char **) = funlist->eval;
      void              (*mvSeterror)(long) = funlist->seterror;
      void             *(*mvFindfun)(char *) = funlist->findfun;
      sprintftype         sprintf;

      /* the code from here to END is the same for any version */
      sprintf = (sprintftype) mvFindfun("sprintf");

      result = mvEval(commandH); /* have MacAnova evaluate the command*/

      if (result != (Symbolhandle) 0)
            C macros TYPE, STRINGPTR, DATAVALUE and constants
            CHAR, REAL, LOGIC, and NULLSYM are defined in Userfun.h
            along with other macros for working with Symbols
          switch (TYPE(result))
            case CHAR:
                      "STRINGPTR(result) = '%s'", STRINGPTR(result));

            case REAL:
              if (!mvIsmissing(&DATAVALUE(result,0)))
                          "DATAVALUE(result,0) = %.17g", DATAVALUE(result,0));
                  sprintf(line, "DATAVALUE(result,0) = MISSING");


            case LOGIC:
              sprintf(line, "DATAVALUE(result,0) = %c",
                      (DATAVALUE(result,0)) ? 'T' : 'F');

            case NULLSYM:
              sprintf(line, "Result is NULL");

               "Type of result not CHARACTER, REAL, LOGICAL, or NULL");
          mvAlert("ERROR: Command produced error");
  /*Tell User an error has occurred but code should not be printed*/
      /* END */

Macintosh version:

  #include "Userfun.h"
    On a Macintosh, the main entry must be called main;  the function
    is found by the name of its code resource
  void main(char ** commandarg, MacAnovaCBSH funlist)
      char              **commandH = commandarg;
      Symbolhandle        result;
      char                line[200];
  #ifndef powerc /*if compiled for 68K Macintosh*/
  #pragma mpwc on
      void              (*mvPrint)(char *) = funlist->print;
      void              (*mvAlert)(char *) = funlist->alert;
      long              (*mvIsmissing)(double *) = funlist->ismissing;
      Symbolhandle      (*mvEval)(char **) = funlist->eval;
      void              (*mvSeterror)(long) = funlist->seterror;
      void             *(*mvFindfun)(char *) = funlist->findfun;
  #ifndef powerc
  #pragma mpwc off
  /* the code from here to END is the same for any version */
      . . . . . . . . . . . see above . . . . .
  /* END */

  #ifdef powerc /*powerc defined means compiling for Power PC*/
   RoutineDescriptor fooeval =
  #endif /*powerc*/

Gary Oehlert 2003-01-15