Call S-PLUS from a C Routine

DESCRIPTION:

A C function that allows a 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 a S-PLUS function. This will have been passed through via an argument in a .C call; e.g., myfun <- function(x) {x+1} .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:

, 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 a 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 a S-PLUS integer not a 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;                  /* a S-PLUS list as received from .C */ 
double *our_doubles;    /* a S-PLUS double */ 
long *our_doubles_n;    /* a 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