The idea behind this package is to allow R programmers to dynamically invoke arbitrary compiled routines without having to explicitly write and compile C code to perform the invocation. This may not be of immense value as one has to compile the original routines to which you are interfacing. Furthermore, a compiled interface will be faster than one that examines its inputs at run-time. However, the dynamic interface is somewhat interesting.
The package is an interface to libffi. There are two main functions of interest to users. The first is prepCIF() which is used to create a "template" call to any native routine with a particular signature - both type of inputs and return value. Having created this interface object, we can use it to make one or more calls to any routine with that signature. We do this with callCIF() .
We specify the type of the return value and of each of the parameters in our call to prepCIF() . There are objects describing each of the basic types such as a double, various types of integers and pointers.
Table 1.
R variable | description |
---|---|
doubleType | a double in C |
floatType | a float in C |
longdoubleType | for platforms that support a long double type |
pointerType | a generic void * pointer |
sint16Type | a signed 16-bit/2 byte integer |
sint32Type | a regular signed 32-bit/4-byte integer |
sint64Type | a signed 64-bit integer |
sint8Type | a signed single byte integer |
uint16Type | an unsigned 16-bit integer |
uint32Type | an unsigned 32-bit integer |
uint64Type | an unsigned 64-bit integer |
uint8Type | an unsigned 8-bit integer |
voidType | the empty/void type of specific use when the routine of interest has no return value. |
stringType | a type object introduced for this package for representing strings, i.e. char *. This might be expanded in the future for different types of strings, i.e. signed and unsigned characters, wide characters, etc. |
In addition to these primtive types, one can create new types for describing
C-level structs. See structType()
.
Let's consider a very simple example of calling a C routine that takes no arguments and returns no value.
#include <stdio.h> void voidCall() { fprintf(stderr, "In voidCall\n"); }
This is included in the compiled code loaded in the
Rffi package.
We would create a CIF for calling a routine with this signature via
void = prepCIF(voidType)
We only have to specify the return type. We don't have to specify any additional information as there are no parameters. Note that we don't specify the name of the C routine to call. This CIF can be used to call any routine with such a signature. And we can use it for multiple calls to the same or different routines.
So now we can call the C routine voidCall via callCIF() . We have to specify the CIF and the name of the routine.
callCIF(void, "voidCall")
You'll see Again, since there are no inputs to this routine, we don't have to specify any.
Consider a C routine that takes no inputs and returns a real value:
double rdouble() { return(3.1415); }
The same steps allow us to call this routine, but we need to specify
the type of the return value differently.
cif = prepCIF(doubleType) callCIF(cif, "rdouble")
This returns the value 3.1415 as an R numeric vector of length 1.
Let's move on to where we pass values from R to the native routine. We'll use a routine (also in Rffi.so/dll) called foo that takes an integer and a double. It returns a double - the sum of the two.
#include <stdio.h> double foo(int x, double y) { fprintf(stderr, "In foo %d %lf\n", x, y); return(x + y); }
To create the CIF, we need to specify the return type as
doubleType and then a list
of the parameter types. These are
sint32Type and doubleType.
Since there are various different types of integers, we have
to be specific about which one is to be used. We use
a regular 4-byte/32-bit integer that has a bit for sign
so that negative values are possible.
So our call is
cif = CIF(doubleType, list(sint32Type, doubleType))
And now we can the routine with
callCIF(cif, "foo", -1L, pi)
What if we passed a numeric value instead of an integer for the first argument? For example,
callCIF(cif, "foo", -1.3, pi)
In the current version of the code, the numeric value is coerced to the target type - an integer. As a result, the value -1 is passed to the native routine.
What about if we pass a string such as "abc"? This is coerced to an integer and results in a NA.
Next we turn our attention to passing non-scalar values, specifically pointers. We'll deal with structures later. Consider a routine which takes a collection of real values via a pointer of type double. It also takes the number of elements as an unsigned integer. The routine retPointer provides such a routine in test.c and available in the Rffi. We can create CIF
cif = CIF(pointerType, list(pointerType, uint32Type))
noting that it returns a new array of doubles. We can then invoke this with
x = c(1, 2, 3, 4) y = callCIF(cif, "retPointer", x, length(x))
The resulting value in y is an external pointer which contains the address of the returned array newly allocated in retPointer. We can pass this to another native routine. Alternatively, we can call native code to extract values and generally manipulate it. We might do this with a regular hand-built routine that is invokable from R via the .Call() interface as we are dealing with SEXP objects throughout, i.e. inputs and outputs. One such routine is contained in test.c - R_copyDoubleArray. We call it with the external pointer to the double * and the number of elements, e.g.
val = .Call("R_copyDoubleArray", y$value, length(x))
If we wanted to release the memory associated with the array, we could call free. Again, we could use a manually created routine via .Call() . But we can also do it via libffi.
free.cif = CIF(voidType, list(pointerType)) callCIF(free.cif, "free", y$value)
libffi also allows us to work with structures. Consider a simple structure defined as
typedef struct { short s; int i; double d; char *string; } MyStruct;
Next, consider a routine that returns an instance of this structure.
char *MyString = "a string"; MyStruct getStruct() { MyStruct c = {-1, 11, 99.2}; c.string = MyString; return(c); }
How can we call this from R and get the structure back? We start by defining a new type to describe this structure. We use structType() for this. We give a list of the types of the elements within the structure.
myStruct.type = structType(list(s = sint16Type, i = sint32Type, d = doubleType, str = stringType))
Note that the short corresponds to the sint16 type.
We can optionally provide names. It is a good idea if we know them as we use the same names as in the C code to simplify interaction. At present we don't use the names, but we may in the future.
Now that we have this type information, we can use it as we did the regular built-in types, e.g. doubleType. So we create our CIF to call the routine getStruct:
cif = CIF(myStruct.type)
And now we can call this
callCIF(cif, "getStruct")
The result is an R list with 4 elements corresponding to the elements of the C structure.
Now let's explore passing an R object to a routine that expects a structure. We can use the following routine
void doStruct(MyStruct s) { fprintf(stderr, "doStruct: s = %d, i = %d, d = %lf, string = %s\n", (int) s.s, s.i, s.d, s.string); }
We create the CIF for this routine
cif = CIF(voidType, list(myStruct.type))
Now we can calls this as
callCIF(cif, "doStruct", list(-1L, 1L, 3.1415, "R string"))
At present, we have not added support for specifying the type of the pointer, e.g. a pointer to a double. This is entirely feasible and would be valuable as it provides a great deal more information. This can then be used to comprehend the contents of the values with more specificity.
Information about routines can be obtained via RGCCTranslationUnit. This can be used within the same R session to construct CIF objects for different routines.