Skip to main content

Module call_marker

Module call_marker 

Source
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
wrappersys.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§

call_marker_impls 🔒

Structs§

Call
The call this wrapper attributes conditions to: the call as the user wrote it (sys.call() in the generated wrapper), or under call_arg the call passed as .call. Taking it selects wrapper attribution. See the module docs.
CallerCall
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 .call formal. Taking it selects caller attribution; the function must be noexport or internal. See the module docs.