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.