0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【R】 C++を使って関数を書く

Last updated at Posted at 2020-08-18

R初心者なのですが、tidyverseなどの登場でプログラミングがだいぶ楽になったように思います。R自体はそこそこの計算速度はあるのですが、部分的にはC++で書ければと思いSWIGとc++で関数を書いてみました。

1. C++で関数を書く

例として次のような関数を書いてみました。C++で書いた普通の関数です。

twicefold: 整数ベクトルxsを二倍した値を返す(Rからは浮動小数点型で良いが整数に変換してから計算する)
addv: ベクトルxsにyを加えた値を返す
string_length: 文字列のベクトルを渡し、文字列の長さのベクトルを返す

test.cpp
# include "test.hpp"
using namespace std;

vector<int> twicefold(vector<int> xs){
    vector<int> result;
    for(const auto& x : xs){
        result.push_back(x*2.0);
    }
    return(result);
}

vector<double> addv(vector<double>xs, double y){
    vector<double> result;
    for(const auto& x : xs){
        result.push_back(x+y);
    }
    return(result);
}

vector<int> string_length(vector<string> str_ary){
    vector<int> lengths;
    for(const auto& str : str_ary){
        lengths.push_back(str.length());
    }
    return(lengths);
}
test.hpp
# include <string>
# include <vector>
using namespace std;

vector<int> twicefold(vector<int> xs);
vector<double> addv(vector<double>xs, double y);
vector<int> string_length(vector<string> str_ary);

ヘッダファイルも作っています。Rの関数の引数は、ベクトルを受け取れる方が使える範囲が広くなりますので、なるべくそのように書いた方が便利です。

2. SWIGのインターフェース定義ファイル作成

test.i
%module test
%{
# include "test.hpp"
%}

%include <std_string.i>
%include <std_vector.i>
%template(stringVector) std::vector<std::string>;
%include test.hpp  // 上の行の%template~より後に

vector<string>の引数や戻り値があるときには、下から2行目の%template...を最終行の%include...の前に入れておいてください。名称は重ならなければ何でも良いです。
vector<int>やvector<double>の場合には必要ないようです。

3. 拡張ライブラリを作成

以上のファイルを同じフィルダに入れて、次のコマンドを実行します。
2行目のCPLUS_INCLUDE_PATHはRdefines.hのあるフォルダを指していますので、環境に応じて書き換えてください。

swig -c++ -r test.i
CPLUS_INCLUDE_PATH=/usr/include/R R CMD SHLIB -fPIC test.cpp test_wrap.cxx

test.Rとtest.so(拡張子はOSにより異なる)ができます。他のファイルもできますが実行にはこの2つのファイルが必要です。

4. テスト用のプログラム

test01.R
dyn.load(paste("test", .Platform$dynlib.ext, sep=""))
source("test.R")
cacheMetaData(1)

print(twicefold(2))
print(twicefold(c(2,3,4)))
print(addv(c(2,3,4),2))
print(string_length("12345"))
print(string_length(c("12345", "678")))

library(tidyverse)
c(2,3,4) %>% addv(2) %>% print()

最初の3行はライブラリを読み込む時に必要ですので忘れないように。
以上で拡張ライブラリができるはずです。

5. クラスのメンバ関数を作る

C++でクラスを定義し、メンバ関数を作ると最後のtest02.Rに書いてあるような使い方ができます。
上と同じフィイル名を使っていますので、別のフォルダを作成して試してみてください。

test.cpp
# include "test.hpp"
using namespace std;

Test::Test(double x){
    num=x;
}
vector<double> Test::mul(vector<double> ys){
    vector<double> result;
    for(const auto& y : ys){
        result.push_back(num*y);
    }
    return(result);
}
test.hpp
# include <vector>
using namespace std;

class Test{
private:
    double num;
public:
    Test(double x);
    vector<double> mul(vector<double> ys);
};
test.i
%module test
%{
# include "test.hpp"
%}

%include <std_vector.i>
%include test.hpp

拡張ライブラリを作成するためのコマンドは上と同じです。

テスト用プログラム

test02.R
dyn.load(paste("test", .Platform$dynlib.ext, sep=""))
source("test.R")
cacheMetaData(1)

v <- Test(4.0)
print(v$mul(3.3))
print(v$mul(c(2,3.3)))

library(tidyverse)
c(2,3.3) %>% v$mul() %>% print()

CentOS 8.2
macOS 10.13.6
SWIG 4.0.2
R 4.0.2

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?