Expand description
Condition-call markers (Call / CallerCall) for #[miniextendr] parameters.
Condition-call markers: Call and CallerCall.
Every generated R wrapper hands its C entry point a call object in a hidden
first slot, .Call(C_pkg_f, .call = <call>, ...). Conditions raised from
Rust (a panic, an Err, error!() & co.) and, for caller attribution,
the wrapper’s own R-side checks are attributed to that call, so
conditionCall() names the function as the user wrote it. Which call it is
comes from the wrapper’s attribution:
| attribution | .call = | names |
|---|---|---|
wrapper | sys.call() | the wrapper’s own call (the default) |
caller | .mx_call (caller frame) | the hand-written R function delegating |
A #[miniextendr] function chooses its attribution in one of three
equivalent spellings, most specific first: a marker parameter on the
signature, the call = wrapper | caller attribute, or the
crate-wide default in Cargo.toml
([package.metadata.miniextendr] call_attribution = "..."). The framework
default is wrapper.
use miniextendr_api::{Call, CallerCall, miniextendr};
/// `.call = sys.call()`; `call` is that call, available to the body.
#[miniextendr]
pub fn scale(x: f64, call: Call) -> f64 { let _ = call.sexp(); x * 2.0 }
/// Internal entry point behind a hand-written `scale2()` in R/: the caller's
/// call, as written, reaches Rust as `call`.
#[miniextendr(noexport)]
pub fn scale2_impl(x: f64, _call: CallerCall) -> f64 { x * 2.0 }The marker parameter is not an R argument: the R wrapper’s formals are the
other parameters, and the C wrapper binds the marker from its hidden call
slot. Call selects wrapper attribution, CallerCall selects caller
(and, like the attribute, needs noexport or internal: an exported
function’s caller is arbitrary user code). A caller wrapper, however
spelled, takes one extra R formal, .call = NULL: a hand-written helper
between the public function and the entry point passes its caller’s frame
there (call = parent.frame()) or a call object, and the body receives the
call that resolves to. #[miniextendr(call_arg)] gives a wrapper
function, exported or not, the same formal, whose NULL is the wrapper’s
own call: an R function composing it (through do.call(), say) passes
.call = environment() or a call object, and a Call parameter receives
the call that resolves to. A marker and a call = ... attribute that disagree
are a compile error, as are two markers on one
function, a marker on an extern "C-unwind" function (no generated call
slot) and a marker on a class method (methods keep the wrapper’s own call).
A function taking a marker runs on R’s main thread, like one taking SEXP.
Both types are transparent newtypes over SEXP: the language object of
the call as written (is.call()). They are handles for inspection,
deparse() in a log line say, never roots: the object lives only as long
as the .Call() frame that carried it, so it must not outlive the function
call it was passed to.
Macros§
Structs§
- Call
- The call this wrapper attributes conditions to: the call as the user wrote
it (
sys.call()in the generated wrapper), or undercall_argthe call passed as.call. Taking it selectswrapperattribution. See the module docs. - Caller
Call - The call of the R function that called this wrapper, as written
(
.miniextendr_caller_call(.call)in the generated wrapper), or the call a helper passed as the wrapper’s.callformal. Taking it selectscallerattribution; the function must benoexportorinternal. See the module docs.