Calling a Fortran Routine from Rcpp

Bridging Fortran and C++ with iso_c_binding

programming
cpp
Author

James Balamuta

Published

January 1, 2019

This post is a version of the rcpp-and-fortran README from the R&D Rcpp collection of compiled-code examples. The repository holds the full, runnable source.

This small example R package demonstrates how to call a Fortran routine from C++ code that is exposed to R with Rcpp, using Fortran’s iso_c_binding module to give the routine C linkage so the two languages can interoperate. It is one of the R&D Rcpp prototype packages, a collection of minimal examples for integrating compiled code with R.

The fRcpp R package provides an example of calling a Fortran routine from C++ code that is, in turn, exposed to R with Rcpp. The Fortran routine is given C linkage through the iso_c_binding module so the C++ side can call it directly.

Usage

To install the package, you must first have a compiler on your system that is compatible with R, including a Fortran compiler such as gfortran. For help on obtaining a compiler consult either macOS or Windows guides.

With a compiler in hand, one can then install the package from GitHub by:

# install.packages("remotes")
remotes::install_github("coatless-rd-rcpp/rcpp-and-fortran")
library("fRcpp")

Implementation Details

Within this project, a numerical routine is written in Fortran and called from an Rcpp function. The bridge between the two languages is Fortran’s iso_c_binding module, which gives the routine C linkage: a predictable symbol name and argument types that C++ can match. The example routine sums the elements of a numeric vector.

.
├── DESCRIPTION                         # Package metadata
├── NAMESPACE                           # Function and dependency registration
├── R                                   # R functions
   ├── RcppExports.R                   # Autogenerated R to C++ bindings by Rcpp
   └── fRcpp-package.R                 # Package documentation
├── README.md
├── man                                 # Package documentation
   ├── fRcpp-package.Rd
   └── fortran_sum.Rd
├── rcpp-and-fortran.Rproj
└── src                                 # Compiled code
    ├── Makevars                        # Links the Fortran runtime via FLIBS
    ├── Makevars.win
    ├── RcppExports.cpp                 # Autogenerated R bindings
    ├── fsum.f90                        # The Fortran routine (bind(C))
    └── surface_fortran.cpp             # Rcpp wrapper that calls the Fortran routine

The Fortran Routine

The routine lives in src/fsum.f90. The bind(C, name = "fsum") attribute fixes its symbol name to fsum and, together with the iso_c_binding kinds (c_double, c_int), pins its argument types. The length n is passed by value, while the array x and the result total are passed by reference, matching how C would pass them.

subroutine fsum(x, n, total) bind(C, name = "fsum")
  use, intrinsic :: iso_c_binding, only : c_double, c_int
  implicit none

  integer(c_int), intent(in), value :: n
  real(c_double), intent(in)        :: x(n)
  real(c_double), intent(out)       :: total

  integer :: i

  total = 0.0_c_double
  do i = 1, n
    total = total + x(i)
  end do
end subroutine fsum

Calling Fortran from Rcpp

The wrapper in src/surface_fortran.cpp declares the routine inside an extern "C" block so the C++ compiler resolves it with C linkage and the unmangled name fsum. The exported fortran_sum() function then hands the vector’s underlying buffer and length to the routine and returns the computed sum.

#include <Rcpp.h>

// Declare the Fortran routine with C linkage.
extern "C" {
  void fsum(const double* x, int n, double* total);
}

// [[Rcpp::export]]
double fortran_sum(Rcpp::NumericVector x) {
  int n = x.size();
  double total = 0.0;

  fsum(x.begin(), n, &total);

  return total;
}

The exported function also carries //' roxygen comments (trimmed from the excerpt here) that Rcpp::compileAttributes() copies into R/RcppExports.R to generate the help page. From R the routine is called as:

fortran_sum(1:10)
#> [1] 55

Linking the Fortran Runtime

Because the package mixes C++ and Fortran, the Fortran runtime libraries must be linked into the shared object. src/Makevars and src/Makevars.win request them with R’s $(FLIBS) variable.

PKG_LIBS = $(FLIBS)

DESCRIPTION

Surfacing the C++ code with Rcpp requires Rcpp under both LinkingTo (for the headers used at compile time) and Imports (so it is available at run time).

LinkingTo:
    Rcpp
Imports:
    Rcpp (>= 1.0.12)

Source

The full package source is available on GitHub, and the rendered package site can be found at rd-rcpp.thecoatlessprofessor.com/rcpp-and-fortran.