餡子付゛録゛

ソフトウェア開発ツールの便利な使い方を紹介。

Cの中でRを動かす

検索するとCからRを呼ぶコード例は色々とあるのですが、本家Writing R Extensionsを含めて、CでRの対話実行を制御するコードが多いので、もっと単純にRの関数を呼ぶコードを書いてみました。

Cのコード

初期化の後は.Callや.Externalと同様に、CからRの機能を呼び出すことができます。

embedding_R.c

#include<stdio.h>
#include<stdlib.h>
#include<R.h>
#include<Rinternals.h>
#include<Rembedded.h>
#include<R_ext/Parse.h>

double call_R_sum(int, double *);
double eval_R_src(unsigned char *);

int main(int argc, char *argv[])
{
   char *R_HOME;
   char *R_argv[] = {"embedding_R", "--silent"}; /* 第1引数は使われず、第2引数以降がRの起動オプションになる */
   double data[] = {1.1, 2.3, 3.1};
   char *text = "1 + 2*3 - 4";
   int i, len;

   Rf_initEmbeddedR(sizeof(R_argv)/sizeof(R_argv[0]), R_argv);

/* consitutute an abstract syntax tree and eval it. */
   len = sizeof(data)/sizeof(double);
   printf("sum(");
   for(i=0;i<len;i++){
      if(0<i) putchar(',');
      printf("%.1f", data[i]);
   }
   printf(") = %.1f\n", call_R_sum(len, data));

/* parse and eval a source text-string. */
   printf("%s = %.1f\n", text, eval_R_src(text));

   Rf_endEmbeddedR(0);
   return 0;
}

double eval_R_src(unsigned char *cmd)
{
/* copied from the Writing R Extensions 5.12 Parsing R code from C */
   SEXP cmdSexp, cmdexpr, ans = R_NilValue;
   ParseStatus status;
   int i;

   cmdSexp = PROTECT(allocVector(STRSXP, 1));
   SET_STRING_ELT(cmdSexp, 0, mkChar(cmd));
   cmdexpr = PROTECT(R_ParseVector(cmdSexp, -1, &status, R_NilValue));
   if (status != PARSE_OK) {
      UNPROTECT(2);
      error("invalid call %s", cmd);
   }
   /* Loop is needed here as EXPSEXP will be of length > 1 */
   for(i = 0; i < length(cmdexpr); i++)
      ans = eval(VECTOR_ELT(cmdexpr, i), R_GlobalEnv);
   UNPROTECT(2);

   return REAL(ans)[0];
}

double call_R_sum(int n, double *v)
{
   SEXP ans, s, t, r;
   unsigned i;
   double rv;

   t = s = PROTECT(allocList(2));

   SET_TYPEOF(t, LANGSXP);
   SETCAR(t, install("sum"));

   t = CDR(t);

   PROTECT(r = allocVector(REALSXP, n)); 
   for(i = 0; i<n; i++)
      REAL(r)[i] = v[i];

   SETCAR(t, r);

   PROTECT(ans = eval(s, R_GlobalEnv));

   if(!isReal(ans)){
      UNPROTECT(3);
      fprintf(stderr, "The length of return of 'objf' must be of numeric.");
      exit(-2);
   }

   if(0==length(ans)){
      UNPROTECT(3);
      fprintf(stderr, "The length of return of 'objf' should be one.");
      exit(-3);
   }
   rv = REAL(ans)[0];

   UNPROTECT(3);

   return rv;
}

例では抽象構文木をつくってRの関数を呼び出していますが、グローバル変数に変数をつくるなどのこともできます。また、最適化アルゴリズム積分アルゴリズムといったライブラリも使用することができます。

コンパイルと実行

教科書的にヘッダーとライブラリを指定すれば、コンパイルできます。

export R_HOME=/usr/lib/R # etcやbinやlibが下にあるRのディレクトリ
export LD_LIBRARY_PATH=${R_HOME}/lib # aptでインストールしている場合は不要
export C_INCLUDE_PATH=/usr/share/R/include # gccに-I/usr/share/R/includeをつけても可
gcc -o embedding_R.exe embedding_R.c -lR # -lRの代わりに${R_HOME}/lib/libR.soをつけても可
./embedding_R.exe
sum(1.1,2.3,3.1) = 6.5
1 + 2*3 - 4 = 3