Integrate Fortran, C++ with R

后端 未结 1 385
迷失自我
迷失自我 2020-12-14 08:26

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().

相关标签:
1条回答
  • 2020-12-14 09:20

    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.

    0 讨论(0)
提交回复
热议问题