Extending R with C++ and Fortran

Dirk Eddelbuettel and JBrandon Duck-Mayr — written Feb 5, 2023 — source

A recent social-media question by James Curran inquired about the best, or recommended ways, to extend R with Fortran code. Part of the question was whether the .Fortran() interface was still recommended or not as there is ‘conflicting advice’ out there. Dirk then followed up and pointed to the (stunning!) performance gains reported by glmnet which switched from .Fortran() to a C++ interface using Rcpp and the (now much preferred) .Call() interface. One key reason behind the performance gains is that .Fortran() requires copies of all arguments, just like the (also effectively deprecated) .C() interface. Whereas .Call() works with SEXP objects which are pointers: this can be dramatically faster and more efficient as object sizes increase.

A few years earlier, and for a related question, JBrandon Duck-Mayr had written a very comprehensive answer on StackOverflow. It is backed by an example package mixedlang which implements the recommendation.

It starts from a Fortran90 function multiplying two ‘real’ aka double valued inputs:

REAL*8 FUNCTION MULTIPLY (X, Y) 
REAL*8 X, Y
MULTIPLY = X * Y
RETURN
END

This can be connected quite easily to C++ code using the common extern "C"declaration (specifying that a C calling convention is used from the C++ code). It still shows the Rcpp::depends() used when sourceCpp()-ing a function, it is not needed in a package like mixedlang.

#include "RcppArmadillo.h"

// [[Rcpp::depends(RcppArmadillo)]]

// First we'll declare the MULTIPLY Fortran function
// as multiply_ in an extern "C" linkage specification
// making sure to have the arguments passed as pointers.
extern "C" {
    double multiply_(double *x, double *y);
}

// Now our C++ function
// [[Rcpp::export]]
Rcpp::NumericVector test_function(Rcpp::NumericVector x) {
    // Get the size of the vector
    int n = x.size();
    // Create a new vector for our result
    Rcpp::NumericVector result(n);
    for ( int i = 0; i < n; ++i ) {
        // And for each element of the vector,
        // store as doubles the element and the index
        double starting_value = x[i], multiplier = (double)i;
        // Now we can call the Fortran function,
        // being sure to pass the address of the variables
        result[i] = multiply_(&starting_value, &multiplier);
    }
    return result;
}

Once both functions are compiled and loaded (as e.g. in package mixedlang) the wrapper function can be called from R as usual:

mixedlang::test_function(0:9)
# [1]  0  1  4  9 16 25 36 49 64 81

We hope the (recently updated) package at GitHub serves as starting point for others wanting to combine R and Fortran via Rcpp.

tags: basics 

Related Articles