Skip to content
Prev 11708 / 12125 Next

[R-pkg-devel] Using DOTS from C API

Generally, the best way to get access to the current environment is passing
environment() as an argument to your C function. I know that people will
tell you to use R_GetCurrentEnv() in C, but that does not work when the C
call is an argument to another function. For example:

tryCatch({
   .External(.C_<funname>, <other args>)
})

If you tried to call R_GetCurrentEnv() inside your C function, it would
return the environment of doTryCatch, and that's not what you. Better
instead to just do this:

tryCatch({
   .External(.C_<funname>, environment(), <other args>)
})


Next, once you're in C, and you have the environment, presumably using
something like

SEXP rho = CAR(args); args = CDR(args);

you would find it in the environment:

SEXP dots = Rf_findVarInFrame(rho, R_DotsSymbol);

or

SEXP dots = Rf_findVar(R_DotsSymbol, rho);

The best way to determine the length of the object is this:

R_xlen_t n = (TYPEOF(dots) == DOTSXP) ? Rf_length(dots) : 0;

The reason you should switch on TYPEOF is in case ... contains zero
arguments, in which case it is R_MissingArg.
this:

CAR(Rf_nthcdr(dots, <index>))

or you can loop over them like this:

SEXP d = dots;
for (R_xlen_t i = 0; i < n; i++, d = CDR(d)) {
    di = CAR(d);
}

However, the values of di are R_MissingArg if that element is empty or a
promise. And while you cannot access the components of a promise, you are
allowed to evaluate it.

SEXP d = dots;
for (R_xlen_t i = 0; i < n; i++, d = CDR(d)) {
    di = CAR(d);
    if (di == R_MissingArg) {
        <do something if missing>
    }
    else {
        di = Rf_eval(di, R_EmptyEnv);
        <do something with promise value>
    }
}

The environment used in Rf_eval does not matter, when evaluating a promise,
the promise environment is always used.


That being said, Rchk may complain of some objects not being protected. To
make it happen, you should protect dots right after you use
Rf_findVarInFrame, as well as protect di right after you use Rf_eval (don't
forget to unprotect di at the end of each loop).

Regards,
    Iris
On Mon, Aug 18, 2025, 14:26 Josiah Parry <josiah.parry at gmail.com> wrote: