Expand description
Raml is a library for directly interacting with the C OCaml runtime, in Rust.
Consquently, raml is designed for rust shared objects that expose raw C FFI bindings,
which are then either statically or dynamically linked against an OCaml binary, which calls into these raw FFI bindings as if they were
regular, so-called “C stubs”. Similarly, any OCaml runtime functions, such as caml_string_length
, will get their definition from the
final OCaml binary, with its associated runtime.
The benefit of this approach is that it removes any bridging C code, and allows in essence, a direct interface between Rust and OCaml.
The macro has the format: (name, |params...|, <local variables..>, { // code body }, -> optional return value listed in local variables
)
A basic example demonstrates their usage:
#[macro_use] extern crate raml;
// linking against this rust static lib, you can access this function in OCaml via:
// `external beef : int -> int = "ml_beef"`
caml!(ml_beef, |parameter|, <local>, {
let i = int_val!(parameter);
let res = 0xbeef * i;
println!("about to return 0x{:x} to OCaml runtime", res);
local = val_int!(res);
} -> local);
// this is only here to satisfy docs
fn main() {}
The macro takes care of automatically declaring CAMLparam
et. al, as well as CAMLlocal
and CAMLreturn
.
If you need more fine grained control, caml_body!
and others are available.
Some more examples:
#[macro_use] extern crate raml;
// these are two functions that OCaml code can access via `external val` declarations
caml!(ml_send_int, |v, v2|, <l>, {
let x = int_val!(v);
l = val_int!(0xbeef);
println!("send_int 0x{:x}", x);
// io::stdout().flush();
} -> l);
caml!(ml_send_two, |v, v2|, {
println!("local root addr: {:p} caml_local_roots: {:#?}, v: {:?}", &raml::memory::caml_local_roots, raml::memory::caml_local_roots, v);
let x = int_val!(v);
let len = raml::mlvalues::caml_string_length(v2);
let ptr = string_val!(v2);
let slice = ::std::slice::from_raw_parts(ptr, len);
let string = ::std::str::from_utf8_unchecked(slice);
println!("got 0x{:x}, {}", x, string);
});
// this is only here to satisfy docs, you will likely want a staticlib, not a rust executable
fn main() {}
These will be accessible to an OCaml program via external
declarations, e.g., for the above we could do something like:
external send_int : int -> int = "ml_send_int"
external send_two : int -> string -> unit = "ml_send_two"
let f x = x land 0x0000ffff
let _ =
let string = "string thing" in
let deadbeef = 0xdeadbeef in
let res = send_int 0xb1b1eb0b in
Printf.printf "send_int returned: 0x%x\n" res;
flush stdout;
send_two deadbeef string;
send_two (f deadbeef) string
Modules§
- External definitions for allocating values in the OCaml runtime
- Callbacks from C to OCaml This is also where you initialize the OCaml runtime system via
caml_startup
orcaml_main
- Defines types and macros primarily for interacting with the OCaml GC. In addition, a few extra convenience macros are added, in particular,
caml!
andcaml_body!
which are the primary API endpoints of raml. - Contains OCaml types and conversion functions from runtime representations.
Macros§
- Pointer to the first byte
- Defines an external Rust function for FFI use by an OCaml program, with automatic
CAMLparam
,CAMLlocal
, andCAMLreturn
inserted for you. - Defines an OCaml FFI body, including any locals, as well as a return if provided; it is up to you to define the parameters.
- Initializes and registers the given identifier(s) as a local value with the OCaml runtime.
- Registers OCaml parameters with the GC
- Extracts from the
$block
an OCaml value at the$ith
-field - Converts an OCaml
int
into ausize
Long_val(x) ((x) >> 1)
- Returns an OCaml
unit
value - Stores the
$val
at$offset
in the$block
. - Extracts a machine
ptr
to the bytes making up an OCamlstring
- Converts a machine
usize
into an OCamlint
(((intnat)(x) << 1) + 1)