Home  |  News   |  Site Search  |  E-mail Search  |  A-Z  |  Directories  |  Contact
Acadia Centre for Mathematical Modelling and Computation

Calling C Functions From R

While it is generally recommended that when you integrate C code into R, you should package and install it as an R library (optionally releasing it to the public), you will often want to first test to see if your code even works before packaging, and sometimes you may just want to run the C code from R a few times, and not worry about packaging it properly for re-use by you or others later.

This particular page shows how to compile C code and call it from R directly without setting up an R package. Details on setting up an R package are described elsewhere.

Steps to Profiling a Program

  1. Obtain or write the C code.
  2. Write wrapper C functions if necessary
  3. Compile the C code into a shared library
  4. Write an R wrapper function
  5. Source and use your R wrapper

1. Obtain or write C code

Usually, you're either writing new C code, or have existing C code you wish to call from R. You only need a copy of this code.

For purposes of this example, we will implement a factorial function, as follows (assuming the code is in a file called 'factorial.c'):

int factorial(int n) { if (n < 2) return 1; return n * factorial(n-1); }

2. Write wrapper C functions if necessary

There are strict constraints on what types of functions R can call. These include:

  • The return type MUST be void
  • The arguments must be pointers to normal data types

Because the function above breaks one (actually, both) of the above rules, we must write a wrapper function which complies with R's rules, and which calls our desired function. We'll call it factorial_R_wrapper, and place it in the file wrapper.c:

int factorial(int); void factorial_R_wrapper(int *n, int *result) { *result = factorial(*n); }

Notice that all input and output parameters are pointers to data

3. Compile the C code into a shared library

R only works with shared libraries (DLL's on Windows, .so files on Unix). This tutorial will show the Unix method for two reasons:

  1. large computational tasks are more likely on Unix
  2. building shared libraries is FAR, FAR more complicated on Windows. What you want to do are defaults on Unix. In Windows, you have to do more work. If you really want to do this in Windows, the general directions to go are:
    1. Use Cygwin gcc to build DLL files for Windows (the easiest)
    2. Use an IDE, like MS Visual Studio to build a DLL project, and find and follow instructions to export your wrapper function(s) as symbols.

To compile multiple files into a shared library, you can run:

gcc -c -fPIC factorial.c gcc -c -fPIC wrapper.c gcc -shared -o factorial.so factorial.o wrapper.o

Please note - there are periodic differences between different machines and OS's. Sometimes -fPIC is not required (it is on Linux/Opteron). Usually, it works with -fPIC, and sometimes without. There are other possible options that could be required, but in 99% of situations, the above will do the job.

Now we have a shared library called factorial.so, which supplies a function called factorial_R_wrapper which can be called by R.

4. Write an R wrapper function

R's interface for calling C functions is not the way you or anyone else will want to call the function in R. The best option is to write a small R function to act as a more convenient wrapper.

Following our example, we would want an R function similar to:

factorial <- function(n) { # If the library hasn't been loaded yet, load it if (!is.loaded(symbol.C('factorial_R_wrapper'))) { dyn.load('factorial.so') } # Call the C function, giving n as a C integer, and giving # result as an array with space for 1 integer. # A list of parameters values after the function is called # is returned, assigned to the same names as are given before the # = signs in the arguments. returned_data = .C('factorial_R_wrapper', n=as.integer(n), result=integer(1)) # Return the value of the result parameter return(returned_data$result) }

This R function first makes sure the C function called factorial_R_wrapper is loaded by testing if the function is currently loaded, and if not, loads the library it is in.

Then, it calls the function. .C() calls a C function. The name is the first argument. The rest of the arguments are to be passed to the C function, and must be in the same order as they appear in the C function. Notice the difference between the input parameter n, and the output parameter result. The input parameter is prepared using as.integer. The "as" functions convert from R data formats to C data formats. There are as.double, as.integer, and several other like them. The output parameter is given as integer(1), which means "make space for 1 C integer." double(10) would mean "provide space to 10 C double values."

Stylistically, you should give names to at least all of your output parameters. If the parameter has a name, the list that is returned by .C will have that parameter's value as an element by that name. That it why saying returned_data$result works to return the value in the result, or 2nd, parameter.

It's easiest to put this R code in its own file, which we'll call 'factorial.R'.

5. Use your R wrapper function

Now to use your function in an R session or program, you only need to source() your R file, and use the R function created in the previous step.

For example:

source('factorial.R') # Simple calls factorial(5) n <- 12 factorial(n) # And to prove it works like any R function apply(as.array(1:8), 1, factorial)

And the copy of such a session should include:

> source('factorial.R') > > # Simple calls > factorial(5) [1] 120 > n <- 12 > factorial(n) [1] 479001600 > > # And to prove it works like any R function > apply(as.array(1:8), 1, factorial) [1] 1 2 6 24 120 720 5040 40320

Wrap-Up

Calling C functions from R is a relatively straight-forward process, particularly if you're dealing with arrays of primitive types, which suits a large number of computational and statistical tasks. More infromation can be found in R help files, and in the manual for Writing R Extensions.

For those who wish to conveniently have their code as an R library, and especially those who want to share their work, it is recommended to eventually package such work up as an R library.

Disclaimer

Acadia University and the author claim no responsibility for anything resulting from the use or misuse of information presented here. Read and use at your own risk.