将Fortran,C++与R集成

Daj*_* Xu 19 c++ fortran r armadillo rcpp

我的任务是用C++重写R函数来加速while循环.所有R代码都在Rcpp和Armadillo的帮助下重写,除了.Fortran().我尝试首先使用Rinside,它的工作速度非常慢,正如Dirk所指出的那样.(数据通过R - > C++ - > R - > Fortran是昂贵的)

由于我不想用C++重写Fortran代码,反之亦然,通过将C++直接链接到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);
}
Run Code Online (Sandbox Code Playgroud)

问题是我可以将C++与Fortran集成并将R与C++集成,但我不能让这三个东西一起工作!

我尝试在Linux中编译C++,但它无法找到RcppArmadillo.h并且namespace Rcpp:

 error: RcppArmadillo.h: No such file or directory
 error: 'Rcpp' is not a namespace-name
Run Code Online (Sandbox Code Playgroud)

当我sourceCpp("test.cpp")直接打电话给R时,控制台会显示:

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.
Run Code Online (Sandbox Code Playgroud)

我也尝试将所有这些东西组合在一起

RcppArmadillo::RcppArmadillo.package.skeleton("TTTest")
Run Code Online (Sandbox Code Playgroud)

但是我TTTest添加了.cpp.f文件/src并运行后,我不知道如何处理包(我相信它无法安装)compileAttributes.

那么,是否有可能像Rcpp那样做我想象的事情?或者有必要将Fortran代码转换为C/C++代码?

谢谢你的帮助.

duc*_*ayr 25

我建议这些项目将代码转换为包.我创建了一个这样的包的简单示例,我mixedlang此GitHub仓库中提供了包.我将在这里描述创建包的过程.

我采取的步骤如下:

  1. 从R设置包结构RcppArmadillo::RcppArmadillo.package.skeleton("mixedlang")(我只使用RcppArmadillo而不是Rcpp,因为OP是 - 这个例子没有特定的Armadillo)
  2. 将下面描述的C++和Fortran代码文件添加到该src/文件夹中
  3. 在R中,Rcpp::compileAttributes("mixedlang/")然后运行devtools::install("mixedlang/")

代码

我创建了一个简单的C++函数,其唯一目的(本质上)是调用Fortran函数.示例函数接受一个数字向量,将每个元素乘以其索引,并返回结果.首先让我们看一下Fortran代码:

fortranfunction.f90

这个函数只需要两个双精度并将它们相乘,返回结果:

REAL*8 FUNCTION MULTIPLY (X, Y) 
REAL*8 X, Y
MULTIPLY = X * Y
RETURN
END
Run Code Online (Sandbox Code Playgroud)

test_function.cpp

现在我们需要从C++代码中调用这个Fortran代码.这样做时,我们需要考虑以下几点:

  1. Fortran参数通过引用传递,而不是通过值传递.
  2. 由于MULTIPLY在另一个文件中定义,我们需要在C++文件中声明它,以便编译器知道参数和返回类型.

    一个.在为我们的C++文件声明Fortran函数时,我们将删除函数名称的情况并附加下划线,因为Fortran编译器默认情况下应该这样做.

    湾 我们必须在extern "C"链接规范中声明该函数; C++编译器通常不能将函数名称用作唯一标识符,因为它允许重载,但是为了调用Fortran函数,我们需要它extern "C"完成链接规范完成的操作(例如,参见此SO答案).

#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;
}
Run Code Online (Sandbox Code Playgroud)

示例输出

安装包后,我跑了一个例子

mixedlang::test_function(0:9)
# [1]  0  1  4  9 16 25 36 49 64 81
Run Code Online (Sandbox Code Playgroud)

可能是原始海报问题的根源

  1. 在最初尝试编译时,他们没有让编译器知道在哪里RcppArmadillo.h.
  2. 试图这样做sourceCpp只是在寻找麻烦; 它并没有真正做出处理多个文件(例如,见这个答案德克Eddelbuettel),多语言处理时是必要的.

我不确定当他们试图将它卷成一个包时会发生什么,这就是我制作这个例子的原因.

  • @RalfStubner很好地了解R扩展手册!我很惊讶地看到那里的陈述,但我认为你的评论中的最后一句话有一些东西 - 很可能是因为Fortran的界面严格来自C++.R只能调用Fortran子程序,而不能调用函数等,而从C++调用Fortran一般都没问题,而且我没有理由认为R调用C++函数的事实应该改变它.我可以用这些问题的更多细节来更新答案. (4认同)
  • 有趣的是,在https://cran.r-project.org/doc/manuals/r-release/R-exts.html#Package-subdirectories中它说:"它不可移植(可能根本不可能)将所有这些语言混合在一个软件包中,我们不支持同时使用C++和Fortran 9x." 也许这个说法必须重新考虑!或者也许它有效,因为你没有直接与Fortran接口? (3认同)
  • 做得很好,需要更多曝光.您是否愿意沿着相同的路线做一个Rcpp Gallery的帖子? (2认同)