The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.
A package that uses RZig can keep C, C++ or Fortran sources next to its Zig code, compile a C library that it ships, share a Zig module with other packages, and offer C-callable functions to other packages. This vignette shows the four forms. The scaffolds and checks are plain R; the builds run when a supported Zig compiler is found (this copy ran them).
root <- file.path(tempdir(), "sharing")
dir.create(root)
lib <- file.path(root, "lib")
dir.create(lib)
header <- c(
"const rzig = @import(\"rzig\");",
"",
"pub const panic = rzig.Panic;",
"",
"comptime {",
" rzig.registerModule(@This());",
"}",
""
)use_rzig() stops when src/ already holds
such sources, because the generated build would not compile them. With
mixed = TRUE, R compiles them with its own compilers next
to the Zig library, and src/entry.c leaves
R_init_<pkg> to the package, whose own init function
registers its routines with rzig_register(), which adds the
Zig routines:
mixed <- file.path(root, "mixedpkg")
dir.create(file.path(mixed, "src"), recursive = TRUE)
writeLines(c("Package: mixedpkg", "Version: 0.0.1"), file.path(mixed, "DESCRIPTION"))
writeLines(c(
"#include <Rinternals.h>",
"SEXP legacy_twice(SEXP x) { return Rf_ScalarReal(2 * Rf_asReal(x)); }"
), file.path(mixed, "src", "legacy.c"))
try(use_rzig(mixed))
#> Error : src/ contains compiled sources that the rzig build does not compile: legacy.c. use_rzig(path, mixed = TRUE) or use_rzig_mixed(path) builds them with R's compilers next to the Zig code; the package's R_init_mixedpkg() then calls rzig_register()
use_rzig(mixed, mixed = TRUE)
#> Generated RZig bindings for mixedpkg: 1 exported function
#> Created RZig scaffold for package mixedpkg
writeLines(c(
"#include <R_ext/Rdynload.h>",
"#include <Rinternals.h>",
"SEXP legacy_twice(SEXP x);",
"void rzig_register(DllInfo *dll, const R_CMethodDef *c_methods,",
" const R_CallMethodDef *call_methods,",
" const R_FortranMethodDef *fortran_methods,",
" const R_ExternalMethodDef *external_methods);",
"static const R_CallMethodDef call_methods[] = {",
" {\"legacy_twice\", (DL_FUNC) &legacy_twice, 1},",
" {NULL, NULL, 0}",
"};",
"void R_init_mixedpkg(DllInfo *dll) {",
" rzig_register(dll, NULL, call_methods, NULL, NULL);",
"}"
), file.path(mixed, "src", "init.c"))
status <- rzig_status(mixed)
status[status$step == "Scaffold", ]
#> RZig status of package mixedpkg
#> [ok] Scaffold build files and framework sources present
#> compiled by R's compilers (Config/rzig/mixed):
#> src/init.c, src/legacy.cA package can ship the sources of a C library and list it in
src/rzig/c_libraries.zon. Zig compiles the sources into the
package and translates the header into a Zig module; nothing is
downloaded:
clib <- file.path(root, "clibpkg")
create_rzig_package(clib, tests = FALSE)
Created package clibpkg at /private/var/folders/vh/njwnyy0141n0zbrgjw2vbz800000gn/T/RtmppmxRLi/sharing/clibpkg
Write Zig functions in src/rzig/src/main.zig, then run rzig::document(path) and rzig::build_zig(path)
vendor <- file.path(clib, "src", "rzig", "vendor", "smooth")
dir.create(vendor, recursive = TRUE)
writeLines("double smooth3(double a, double b, double c);", file.path(vendor, "smooth.h"))
writeLines(c(
"#include \"smooth.h\"",
"double smooth3(double a, double b, double c) { return (a + 2 * b + c) / 4; }"
), file.path(vendor, "smooth.c"))
writeLines(c(
".{",
" .{",
" .name = \"smooth\",",
" .header = \"vendor/smooth/smooth.h\",",
" .sources = .{\"vendor/smooth/smooth.c\"},",
" .include = .{\"vendor/smooth\"},",
" },",
"}"
), file.path(clib, "src", "rzig", "c_libraries.zon"))
writeLines(c(header,
"const smooth = @import(\"smooth\");",
"",
"/// Smooth a vector with the weights 1, 2, 1 of the C library.",
"/// @param x A numeric vector.",
"/// @return A numeric vector.",
"/// @export",
"pub fn smoothed(ctx: *rzig.Ctx, x: []const f64) rzig.Error![]f64 {",
" const out = try ctx.alloc(f64, x.len);",
" for (out, 0..) |*slot, i| {",
" const left = x[if (i == 0) 0 else i - 1];",
" const right = x[@min(i + 1, x.len - 1)];",
" slot.* = smooth.smooth3(left, x[i], right);",
" }",
" return out;",
"}"
), file.path(clib, "src", "rzig", "src", "main.zig"))
document(clib)
Generated RZig bindings for clibpkg: 1 exported functionCompiled code of other packages can call functions that a package
registers with R_RegisterCCallable(). The root Zig file
lists them, each with callconv(.c), and may declare an
interface version, which callers check because R checks neither
signatures nor versions:
callee <- file.path(root, "calleepkg")
create_rzig_package(callee, tests = FALSE)
Created package calleepkg at /private/var/folders/vh/njwnyy0141n0zbrgjw2vbz800000gn/T/RtmppmxRLi/sharing/calleepkg
Write Zig functions in src/rzig/src/main.zig, then run rzig::document(path) and rzig::build_zig(path)
writeLines(c(header,
"fn clamp(x: f64, low: f64, high: f64) callconv(.c) f64 {",
" return @min(@max(x, low), high);",
"}",
"",
"pub const rzig_callables = .{ .clamp = clamp };",
"pub const rzig_interface_version: u32 = 1;",
"",
"/// The interface version of the C-callable functions.",
"/// @return An integer.",
"/// @export",
"pub fn callee_interface() i32 {",
" return 1;",
"}"
), file.path(callee, "src", "rzig", "src", "main.zig"))
document(callee)
Generated RZig bindings for calleepkg: 1 exported function
caller <- file.path(root, "callerpkg")
create_rzig_package(caller, tests = FALSE)
Created package callerpkg at /private/var/folders/vh/njwnyy0141n0zbrgjw2vbz800000gn/T/RtmppmxRLi/sharing/callerpkg
Write Zig functions in src/rzig/src/main.zig, then run rzig::document(path) and rzig::build_zig(path)
writeLines(c(header,
"const Clamp = fn (f64, f64, f64) callconv(.c) f64;",
"",
"/// Clamp values with the function of calleepkg.",
"/// @param x A numeric vector.",
"/// @return A numeric vector.",
"/// @export",
"pub fn clamped(ctx: *rzig.Ctx, x: []const f64) rzig.Error![]f64 {",
" const clamp = try rzig.callable(ctx, Clamp, \"calleepkg\", \"clamp\", .{ .interface = 1 });",
" const out = try ctx.alloc(f64, x.len);",
" for (x, out) |value, *slot| slot.* = clamp(value, 0, 1);",
" return out;",
"}"
), file.path(caller, "src", "rzig", "src", "main.zig"))
document(caller)
Generated RZig bindings for callerpkg: 1 exported functionold_paths <- .libPaths()
.libPaths(c(lib, old_paths))
build_zig(callee, lib = lib)$ok
#> [1] TRUE
caller_build <- build_zig(caller, lib = lib)
caller_build$ok
#> [1] TRUE
clamped <- getExportedValue(loadNamespace(caller_build$package, lib.loc = lib), "clamped")
clamped(c(-1, 0.5, 2))
#> [1] 0.0 0.5 1.0
.libPaths(old_paths)rzig.callable() loads the namespace of the providing
package, which R_GetCCallable() does not do; the calling
package lists the provider in Imports.
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.