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.

>From there, it is just a pair list. You can select specific elements like
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.pa...@gmail.com> wrote:

> *Question:* what is the best way to access a DOTSXP from R's C API in a
> function call.
>
> Over at extendr (1) we'd like to be able to utilize `...` from the body of
> a Rust function—with initial motivation being to support S3 generics like
> print(x, ...).
>
> I don't see any recommendations / or mention of accessing DOTSXP in WRE.
> The closest I can find is from R internals (2) repo which says
>
> "Easiest to get to with findVar(R_DotsSymbol, env) or similar."
>
>
> From R's C API would this mean we would want to use something like:
>
> findVar(R_DotsSymbol, R_GetCurrentEnv()) to get the dots pairlist from the
> function's environment?
>
>
> 1) https://extendr.rs
> 2)
>
> https://github.com/hadley/r-internals/blob/16c1d73b635ccecec8b1f396a8935a8dd96126e4/pairlists.md
>
>         [[alternative HTML version deleted]]
>
> ______________________________________________
> R-package-devel@r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-package-devel
>

        [[alternative HTML version deleted]]

______________________________________________
R-package-devel@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-package-devel

Reply via email to