Call S-PLUS from a C Routine

DESCRIPTION:
A C function that allows an S-PLUS function to be called from inside a C function.

USAGE:
call_S(func, nargs, arguments, modes,
       lengths, names, nres, results)
char *func;
char **arguments, **modes, **names, **results;
long nargs, *lengths, nres;

REQUIRED ARGUMENTS:
func:
a pointer to an S-PLUS function. This will have been passed through via an argument in a .C call; e.g., myfun <- function(x) {x+1}

amp;.C("my_c_code",list(myfun),as.double(xx), as.integer(length(xx))) The C code in my_c_code before the call to call_S should just pass this pointer around, not doing anything with it that might alter it.

nargs:
the number of arguments to be passed to the S-PLUS function.
arguments:
an array of pointers to the data being passed to the S-PLUS function. These can point to any type of data, but must be cast to type char * when put into arguments: double *xarg; ... arguments[0] = (char *)xarg;
modes:
the modes of the arguments, as character strings containing the usual S-PLUS modes (see the table in section 7.2.3 of Becker, Chambers and Wilks).
lengths:
the number of elements in each of the arguments to the S-PLUS function.
names:
the names for the arguments to the S-PLUS function, to be matched against the argument names in the function definition. names can be 0, as can any of names[i]. If names is not 0 and names[i] is not 0, then names[i] is a pointer to the character string in C to be used as the actual argument name corresponding in the call to the S-PLUS function.
nres:
the number of components of the value of the S-PLUS function to be passed back to C. If nres is one and the function produces an atomic result (e.g., numeric), this is passed back. Otherwise, the S-PLUS function is expected to produce a list whose elements provide the results passed back. If the actual number of results exceeds nres, the rest are ignored; if it is less than nres, null pointers are returned in the corresponding elements of results.
results:
a vector into which the pointers to the data in the components of the results will be stuffed by call_S. The data may be of any mode, but will be cast to char * by call_S. The calling routine will want to cast them back to the desired type: double *yresult; ... yresult = (double *)results[0];


NOTE:
The S-PLUS function must assure the results are the mode and length expected by the C code.

Note that the calling routine is responsible for allocating space for all the arrays passed to call_S.


SEE ALSO:
amp;.C , S_alloc for storage allocation.

See section 7.2.4 of Becker, Chambers and Wilks for another example.


EXAMPLES:
rev.test <- function(fun, vec)
{
# this S-PLUS function uses .C to pass an S-PLUS function and vector to a C routine.
# The C routine can then use call_S to pass the vector on to the
# user-supplied S-PLUS function.
#
        z <- .C("rev_test0",
                list(fun),
                as.double(vec),
                as.integer(length(vec)))
        z[[2]]
}

rev.fun <- function(x, count) { # (any function of x here) x <- 2 * x + 1

# returns a list, which is what call_S expects when you tell it # you want more than one return value. The as.integer() around # count is important; the C code wants an S-PLUS integer not an S-PLUS double. return(list(x, as.integer(count))) }

/* C code for call_S example */ #include <stdio.h> /* (use this definition if your C compiler doesn't like the ANSI void declaration) */ #define void char

/* This C routine can be called using .C. It could do some calculations on the double array our_doubles, then could pass our_doubles on to the user-supplied S-PLUS function for more processing. */

void rev_test0(S_func, our_doubles, our_doubles_n) void **S_func; /* an S-PLUS list as received from .C */ double *our_doubles; /* an S-PLUS double */ long *our_doubles_n; /* an S-PLUS integer */ { long rev_test(), result;

/* (could do things with our_doubles array here) */

result = rev_test(S_func[0], our_doubles, our_doubles_n); }

/* a C routine that gets S-PLUS to call a function that expects a "double" vector and returns a "double" vector, together with its "integer" length. The routine then replaces x with the return value (which should be no longer than x) and returns the new length of x. */

long rev_test(S_func, x, nx) void *S_func; double *x; long *nx; { long lengths[2], count, i; char *arguments[2], *values[2]; char *modes[2], *names[2]; double *xnew;

/* here's the vector to be used as input to the S-PLUS function */ arguments[0] = (char *) x; modes[0] = "double"; lengths[0] = *nx; names[0] = "x";

/* here's the count, which is not really needed in this example */ arguments[1] = (char *) nx; modes[1] = "integer"; lengths[1] = 1; names[1] = NULL;

/* call the S-PLUS function, results in values array */ call_S(S_func, 2L, arguments, modes, lengths, names, 2L, values);

/* new vector, count (count isn't really used in this example) */ xnew = (double *) values[0]; count = *((long *) values[1]);

/* replace input vector with return vector from the S-PLUS function */ for (i = 0; i<count; i++) { *x++ = *xnew++; }

return count; }

rev.test(rev.fun,1:10) [1] 3 5 7 9 11 13 15 17 19 21