Introduction
Calling an R function from C involves:
- Creating a special list of function name and arguments
- Optionally setting the argument names with
SET_TAG()
- Calling
eval()
Creating the special list for the call can be done using the helper
methods lang1()
to lang6()
, or for R 4.4.1 or
later allocLang()
can provide more flexibility if
needed.
lang1()
- Calling an R function with no arguments
lang1()
is for assembling a call to an R function
without specifying any arguments.
In this example getwd()
is called from C using
lang1()
#include <R.h>
#include <Rinternals.h>
SEXP call_r_from_c_with_lang1(void) {
SEXP my_call = PROTECT(lang1(
PROTECT(Rf_install("getwd"))
));
SEXP my_result = PROTECT(eval(my_call, R_GlobalEnv));
UNPROTECT(3);
return my_result;
}
Click to show R code
code = r"(
#include <R.h>
#include <Rinternals.h>
SEXP call_r_from_c_with_lang1(void) {
SEXP my_call = PROTECT(lang1(
PROTECT(Rf_install("getwd"))
));
SEXP my_result = PROTECT(eval(my_call, R_GlobalEnv));
UNPROTECT(3);
return my_result;
}
)"
callme::compile(code)
call_r_from_c_with_lang1()
#> [1] "/Users/mike/projects/callme/vignettes"
lang2()
- Calling an R function with a single unnamed
argument
lang2()
is for assembling a call to an R function and
specifying a single argument.
In this example abs(-1)
is called from C using
lang2()
#include <R.h>
#include <Rinternals.h>
SEXP call_r_from_c_with_lang2(void) {
SEXP val = PROTECT(ScalarReal(-1));
SEXP my_call = PROTECT(lang2(
PROTECT(Rf_install("abs")),
val
));
SEXP my_result = PROTECT(eval(my_call, R_GlobalEnv));
UNPROTECT(4);
return my_result;
}
Click to show R code
code = r"(
#include <R.h>
#include <Rinternals.h>
SEXP call_r_from_c_with_lang2(void) {
SEXP val = PROTECT(ScalarReal(-1));
SEXP my_call = PROTECT(lang2(
PROTECT(Rf_install("abs")),
val
));
SEXP my_result = PROTECT(eval(my_call, R_GlobalEnv));
UNPROTECT(4);
return my_result;
}
)"
callme::compile(code)
call_r_from_c_with_lang2()
#> [1] 1
lang2()
- Calling an R function with a single named
argument
In this example tempfile(fileext = ".txt")
is called
from C using lang2()
#include <R.h>
#include <Rinternals.h>
SEXP call_r_from_c_with_lang2_named(void) {
// Assemble the function + argument with name
SEXP val = PROTECT(mkString(".txt"));
SEXP my_call = PROTECT(lang2(
PROTECT(Rf_install("tempfile")),
val
));
// Set the argument name
SEXP t = CDR(my_call);
SET_TAG(t, Rf_install("fileext"));
// Evaluate the call
SEXP my_result = PROTECT(eval(my_call, R_GlobalEnv));
UNPROTECT(4);
return my_result;
}
Click to show R code
code = r"(
#include <R.h>
#include <Rinternals.h>
SEXP call_r_from_c_with_lang2_named(void) {
// Assemble the function + argument with name
SEXP val = PROTECT(mkString(".txt"));
SEXP my_call = PROTECT(lang2(
PROTECT(Rf_install("tempfile")),
val
));
// Set the argument name
SEXP t = CDR(my_call);
SET_TAG(t, Rf_install("fileext"));
// Evaluate the call
SEXP my_result = PROTECT(eval(my_call, R_GlobalEnv));
UNPROTECT(4);
return my_result;
}
)"
callme::compile(code)
call_r_from_c_with_lang2_named()
#> [1] "/var/folders/kq/h7dv19mj00947dthlyb5w2780000gn/T//RtmpFh7U8o/filee95b397b38a6.txt"
Using allocLang()
(R >= 4.4.1 only)
The C code is equivalent to this R code:
print(pi , digits = 3)
#include <R.h>
#include <Rinternals.h>
SEXP call_print_from_c(SEXP value, SEXP digits) {
// Allocate a new call object
SEXP my_call = PROTECT(allocLang(3));
// Manipulate a pointer to this call object to
// fill in the arguments and set argument names
SEXP t = my_call;
SETCAR(t, install("print")); t = CDR(t);
SETCAR(t, value) ; t = CDR(t);
SETCAR(t, digits);
SET_TAG(t, install("digits"));
eval(my_call, R_GlobalEnv);
UNPROTECT(1);
return R_NilValue;
}
Click to show R code
code = r"(
#include <R.h>
#include <Rinternals.h>
SEXP call_print_from_c(SEXP value, SEXP digits) {
// Allocate a new call object
SEXP my_call = PROTECT(allocLang(3));
// Manipulate a pointer to this call object to
// fill in the arguments and set argument names
SEXP t = my_call;
SETCAR(t, install("print")); t = CDR(t);
SETCAR(t, value) ; t = CDR(t);
SETCAR(t, digits);
SET_TAG(t, install("digits"));
eval(my_call, R_GlobalEnv);
UNPROTECT(1);
return R_NilValue;
}
)"
callme::compile(code)
call_print_from_c(pi, digits = 3)