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