Integrate Fortran, C++ with R
Asked Answered
C

1

22

My task it to rewrite a R function in C++ to accelerate the while loops. All R codes has been rewritten in the help of Rcpp and Armadillo except the .Fortran(). I try to use Rinside to at first and it works at a very slow speed as Dirk indicated. (It is expensive for data to go through R -> C++ -> R -> Fortran)

Since I don't want to rewrite the Fortran codes in C++ and vice versa, it looks natural to accelerate the programs by linking C++ directly to Fortran: R -> C++ -> Fortran.

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

#include <RcppArmadillo.h>
using namespace Rcpp;

extern "C"{
   List f_(int *n,NumericMatrix a, NumericVector c, double* eps);
}

The problem is that I can integrate C++ with Fortran and integrate R with C++, but I can't make these three things work together!

I try to compile the C++ in Linux but it just can't find RcppArmadillo.h and namespace Rcpp:

 error: RcppArmadillo.h: No such file or directory
 error: 'Rcpp' is not a namespace-name

When I call sourceCpp("test.cpp") in R directly, the console would display:

test.o:test.cpp:(.text+0x20b2): undefined reference to `f_'
collect2: ld returned 1 exit status
Error in sourceCpp("test.cpp") : Error occurred building shared library.

I also try to combine all these things in a package by

RcppArmadillo::RcppArmadillo.package.skeleton("TTTest")

But I don't know how to deal with the package TTTest (I believe it could not be installed) after I add the .cpp and .f files to /src and run compileAttributes.

So, is it possible to do things like what I imagine by Rcpp? Or it is necessary to convert Fortran codes to C/C++ codes?

Thanks for your help.

Cawley answered 14/7, 2015 at 2:49 Comment(3)
Your compiler (well, preprocessor) is unable to find file "RcppArmadillo.h". Ensure it is in the same directory as your "*.cpp" file(s) or it's in any other way reachable (hint: "-I")Speedwriting
Yeah, it should be like include "RcppArmadillo.h" but it still cant find the other header files. And I doubt that to compile the file with Rcpp woud take more time since finally I would call the function in RCawley
If this question is still relevant, than have a look at this answer. The function called there is still a C function, but in the end a Fortran subroutine is used.Rampageous
A
29

I would suggest for such projects to roll your code into a package. I created a simple example of such a package I called mixedlang that is available at this GitHub repo. I will describe the process of creating the package here.

The steps I took were as follows:

  1. Set up the package structure from R with RcppArmadillo::RcppArmadillo.package.skeleton("mixedlang") (I only used RcppArmadillo rather than Rcpp since the OP was -- there's nothing Armadillo specific to this example)
  2. Added the C++ and Fortran code files described below to the src/ folder
  3. In R, run Rcpp::compileAttributes("mixedlang/") then devtools::install("mixedlang/")

The Code

I created a simple C++ function whose only purpose (essentially) was to call a Fortran function. The example function takes in a numeric vector, multiplies each element by its index, and returns the result. First let's look at the Fortran code:

fortranfunction.f90

This function just takes in two doubles and multiplies them, returning the result:

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

test_function.cpp

Now we need to call this Fortran code from our C++ code. When doing this, we need to take into account a few things:

  1. Fortran arguments are passed by reference, not by value.
  2. Since MULTIPLY is defined in another file, we need to declare it in our C++ file so the compiler knows the argument and return types.

    a. When declaring the Fortran function for our C++ file, we'll drop the case of the function name and append an underscore, since the Fortran compiler should do this by default.

    b. We have to declare the function within an extern "C" linkage specification; C++ compilers cannot typically use function names as unique identifiers since it allows overloading, but for calling Fortran functions, we need it to do exactly that which the extern "C" linkage specification accomplishes (see, for example, this SO answer).

#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;
}

Example output

After installing the package, I ran as an example

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

Likely sources of the original poster's problems

  1. When attempting to compile initially, they did not let the compiler know where RcppArmadillo.h was.
  2. Trying to do this with sourceCpp is just asking for trouble; it wasn't really made to handle multiple files (see for example this answer by Dirk Eddelbuettel), which is necessary when dealing with multiple languages.

I'm not sure what happened when they tried to roll it into a package, which is why I drew up this example.

Acadian answered 2/9, 2018 at 12:23 Comment(5)
Nicely done, and needs more exposure. Would you be open to doing an Rcpp Gallery post along the same lines?Bleareyed
@DirkEddelbuettel AbsolutelyAcadian
Interesting, in cran.r-project.org/doc/manuals/r-release/… it says: "It is not portable (and may not be possible at all) to mix all these languages in a single package, and we do not support using both C++ and Fortran 9x." Maybe that statement has to be reconsidered! Or maybe it works because you are not interfacing R with Fortran directly?Rampageous
@RalfStubner Good catch on the R Extensions manual! I am kind of surprised to see that statement there, but I think there's something to the last sentence in your comment -- it is likely because the interface to Fortran is strictly from C++. R can only call Fortran subroutines, not functions, etc., while calling Fortran from C++ is no problem generally, and I see no reason why the fact that R is calling the C++ function should change that. I may update the answer with some more details on these issues.Acadian
@Acadian Your answer is more than I expected. Thanks for this explanation.Privet

© 2022 - 2024 — McMap. All rights reserved.