call_S(func, nargs, arguments, modes,
lengths, names, nres, results)
char *func;
char **arguments, **modes, **names, **results;
long nargs, *lengths, nres;
.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.
char *
when put into
arguments:
double *xarg;
...
arguments[0] = (char *)xarg;
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 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.
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];
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.
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