C言語の関数ポインタを取るインターフェイスにRcpp::Functionを渡したい
最近だとtemplateの魔力を使ったC++を使えばこんなことする必要はないですが、古いC言語で書かれたライブラリなんかに、関数ポインタを引数として渡す必要がある場合のお話。
以下のコードでは、関数ポインタ(typedef double(*Simulate)(double x))を引数にとるold_c_function関数にRcpp::Functionを投げ込んでみた例。old_c_function関数自体は適当な処理(x0 + f(x0))を実行する関数としています。
library(Rcpp) sourceCpp(code=' #include <Rcpp.h> typedef double(*Simulate)(double x); //Old-C function double old_c_function(double x0, Simulate f) { return(x0 + f(x0)); } // [[Rcpp::export]] double new_interface(double x, Rcpp::Function f) { return old_c_function(x, &f); } ')
これをコンパイルしようとすると、これは無常にもというか、当たり前ではありますが「型変換できねぇよ!」と怒られる。
g++ -m64 -I"C:/R/R-32~1.0/include" -DNDEBUG -I"C:/R/R-3.2.0/library/Rcpp/include" -I"C:/Users/teramonagi/AppData/Local/Temp/RtmpMR3pkQ" -I"d:/RCompile/r-compiling/local/local320/include" -O2 -Wall -mtune=core2 -c file16514334dad9.cpp -o file16514334dad9.o file16514334dad9.cpp: In function 'double new_interface(double, Rcpp::Function)': file16514334dad9.cpp:12:32: error: cannot convert 'Rcpp::Function* {aka Rcpp::Function_Impl<Rcpp::PreserveStorage>*}' to 'Simulate {aka double (*)(double)}' for argument '2' to 'double old_c_function(double, Simulate)' file16514334dad9.cpp:13:3: warning: control reaches end of non-void function [-Wreturn-type]
これをなんとかしたい、速度とか気にしてRcpp使ってるんだけど、遅くなってもいいから無理やりRcpp::Function使いたいって時は、1枚コンバータ(アダプタ?)を噛ませばいい。staticとしてactionメソッドを取っているのがミソで、普通のメンバー関数だとポインタが変換できなくてダメ。
sourceCpp(code=' #include <Rcpp.h> typedef double(*Simulate)(double x); //Old-C function double old_c_function(double x0, Simulate f) { return(x0 + f(x0)); } //Converter for Rcpp::Function <--> Simulate struct Converter { static double action(double x){return Rcpp::as<double>((*f_)(x));} static Rcpp::Function* f_; }; Rcpp::Function* Converter::f_ = NULL; // [[Rcpp::export]] double new_interface(double x, Rcpp::Function f) { Converter::f_ = &f; return old_c_function(x, Converter::action); } ')
これはコンパイルできて、ちゃんと動く。以下は「3 + 3^2 + 2 = 14」という計算に対応し、答えもあってる。
> new_interface(3, function(x){x^2+2}) [1] 14