Back
Splus Guides and Release Notes: Unix/Linux Windows
/* Example C program that is called from Splus and uses an Splus
function via "call_S()".
H. Seltman 7/28/95
This C function, r02tst() calculates the average, min and max of "n"
random n(0,2) numbers. The numbers are generated in a call from C
back to S-plus to use rnorm().
Compile this program with "cc -c -Aa +Z callc.c"
where callc.c is this file. (Note -Ae is very similar to -Aa.)
The +Z option is VERY important.
From Splus, use:
# Load C program into S-plus
dyn.load2("callc.o")
# The C program needs the address of the S function rnorm()
fl_list(rnorm)
# The easiest approach is to preallocate space for arguments to
# be returned by the C function.
avg_as.double(0)
rng_as.double(rep(0,2))
# make the call to C with "n=5".
rslt_.C("r02tst",fl,mean=avg,range=rng,count=as.integer(5))
# note: C can't find array lengths, hence count is needed
# report results
cat("Mean=", round(rslt$mean,3), " N=",rslt$count, "\n", sep="")
cat("Range=", round(rslt$range[1],3), " to ",
round(rslt$range[2],3), "\n", sep="")
As required, the results of the call to C are returned to Splus as
components of the value list of the ".C" call. In this case the
average is returned in "avg", and the range is in a length-2 vector
called "rng". These variables are included in the call to reserve
places for the results.
From inside the C function r02tst, we use "call_S()" to make the S call
"rnorm(n, 0.0, 2.0)". This is a bit complicated because
the arguments to the Splus function that is called by C (n, 1.0 and 2.0 here)
can be of any type (integer, double, etc.) and can be atomic, vector
(or matrix). The same "richness" applies to the returned value(s) of the
Splus function. This example show how to handle some of these problems.
Passing the function pointers from S to C must be done with "list(fn)".
Although somewhat awkward, it does have the advantage
that multiple function pointers could be passed in one argument, if
the C function needs to call more than one S function.
*/
#include <stdlib.h> /* note that e.g. printf() can be used */
#include <stdio.h>
/* Splus's rnorm() function has 3 arguments and 1 (vector) result */
#define NUM_ARGS 3
#define NUM_RSLTS 1
/* Non-varying declaration of call_S*/
extern void call_S(char *func, long nargs, char **arguments,
char **modes, long *lengths, char **names, long nres,
char **results);
void r02tst(void **pLogFun, double *dAvg, double *dRng, long *lCnt) {
char *arguments[NUM_ARGS]; /*arguments to rnorm()*/
long lRndCnt=*lCnt;
double dMean=0.0;
double dSd=2.0;
/* rnorm() needs n,mean,sd */
char *modes[NUM_ARGS]={"integer","double","double"};
/* arguments passed to rnorm() are atomic */
long lengths[NUM_ARGS]={1L,1L,1L};
/* I need to allocate space for pointers to the result(s) passed back from
Splus. For rnorm() the result is a single vector. The call_S fucntion will
fill in addresses of the results which will point to some space it
allocates. */
char *results[NUM_RSLTS]; /* result pointers from rnorm() go here */
double *dPtr,dTmp,dMin,dMax,dSum=0.0;
int i;
/* Assign arguments to rnorm() */
arguments[0]=(char *) (&lRndCnt);
arguments[1]=(char *) (&dMean);
arguments[2]=(char *) (&dSd);
/* Make the call back to rnorm() in Splus */
call_S(pLogFun[0], (long)NUM_ARGS, arguments, modes, lengths,
NULL, (long)NUM_RSLTS, results);
/* Manipulate the results */
dPtr=(double *)results[0]; /* dPtr[] is the vector of n random numbers */
dMin=dPtr[0];
dMax=dMin;
for (i=0; i<lRndCnt; i++) {
dTmp=dPtr[i];
printf("%d=%lf\n",i+1,dTmp);
if (dTmp<dMin) dMin=dTmp;
if (dTmp>dMax) dMax=dTmp;
dSum=dSum+dTmp;
}
/* Pass results back to S */
*dAvg=dSum/lRndCnt;
*dRng=dMin;
*(dRng+1)=dMax;
return;
}
R. A. Becker and J. M. Chambers (1985), "Extending the S System," Chapman and Hall, London.