私の仕事は、while ループを高速化するために、R 関数を C++ で書き直すことです。 を除くすべての R コードは、Rcpp と Armadillo の助けを借りて書き直されました.Fortran()
。 最初は Rinside を使用しようとしましたが、Dirk が指摘したように非常に遅い速度で動作しました。(データが R -> C++ -> R -> Fortran を通過するのはコストがかかります)
Fortran コードを C++ で書き直したり、その逆を行ったりしたくないので、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);
}
問題は、C++ を Fortran と統合し、R を C++ と統合することはできるが、これら 3 つを連携させることができないことです。
Linux で C++をコンパイルしようとしましたが、見つかりませんRcppArmadillo.h
。namespace Rcpp
error: RcppArmadillo.h: No such file or directory
error: 'Rcpp' is not a namespace-name
R を直接呼び出すとsourceCpp("test.cpp")
、コンソールに次のように表示されます。
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.
私はこれらすべてをパッケージにまとめようとしています
RcppArmadillo::RcppArmadillo.package.skeleton("TTTest")
しかし、 とファイルを追加して を実行した後、パッケージをどのように処理すればよいかわかりませんTTTest
(インストールできなかったと思います) 。.cpp
.f
/src
compileAttributes
それで、私が想像しているようなことは Rcpp でできるのでしょうか? それとも Fortran コードを C/C++ コードに変換する必要があるのでしょうか?
ご協力いただきありがとうございます。
ベストアンサー1
このようなプロジェクトでは、コードをパッケージにまとめることをお勧めします。私が作成した簡単なパッケージの例は、次のURLmixedlang
で入手できます。このGitHubリポジトリここではパッケージの作成手順を説明します。
私が実行した手順は次のとおりです。
- R からパッケージ構造を設定します
RcppArmadillo::RcppArmadillo.package.skeleton("mixedlang")
(OP が Rcpp ではなく RcppArmadillo を使用しただけなので、この例には Armadillo 固有のものはありません) - 以下に説明するC++およびFortranコードファイルを
src/
フォルダに追加しました - Rでは、
Rcpp::compileAttributes("mixedlang/")
次のように実行します。devtools::install("mixedlang/")
コード
私は、Fortran 関数を呼び出すことだけを目的とした単純な C++ 関数を作成しました。この例の関数は、数値ベクトルを受け取り、各要素をそのインデックスで乗算し、結果を返します。まず、Fortran コードを見てみましょう。
fortranfunction.f90
この関数は、2 つの double を受け取り、それらを乗算して結果を返します。
REAL*8 FUNCTION MULTIPLY (X, Y)
REAL*8 X, Y
MULTIPLY = X * Y
RETURN
END
テスト関数.cpp
ここで、この Fortran コードを C++ コードから呼び出す必要があります。これを行うときは、いくつかの点を考慮する必要があります。
- Fortran 引数は値ではなく参照によって渡されます。
は別のファイルで定義されているため
MULTIPLY
、コンパイラが引数と戻り値の型を認識できるように、C++ ファイルで宣言する必要があります。a. C++ ファイルの Fortran 関数を宣言するときに、関数名の大文字と小文字を区別せず、アンダースコアを追加します。これは、Fortran コンパイラがデフォルトでこれを行うためです。
b. 関数を
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;
}
出力例
パッケージをインストールした後、例として実行しました
mixedlang::test_function(0:9)
# [1] 0 1 4 9 16 25 36 49 64 81
元の投稿者の問題の原因と思われるもの
- 最初にコンパイルしようとしたとき、コンパイラにその場所を知らせませんでした
RcppArmadillo.h
。 - これを使おうとすると
sourceCpp
トラブルを招くだけです。複数のファイルを扱うために作られたものではありません(例えばこの答えによるディルク・エデルビュッテル) は、複数の言語を扱うときに必要になります。
それをパッケージにまとめようとしたときに何が起こったのかはわかりませんが、そのためにこの例を描きました。