opendp-ffi 0.4.0

Foreign function interfaces for the OpenDP library.
opendp-ffi-0.4.0 doesn't have any documentation.
FFI bindings for OpenDP. # Overview This crate contains FFI bindings for [OpenDP](opendp). # Generic Functions OpenDP makes extensive use of generic types and functions. This presents problems for building an FFI interface. The reason is because Rust performs monomorphization of generics when compiling. This means that a single generic function is compiled to many different concrete functions, each one specific to its type parameters. That makes it impossible to have an FFI function for a Rust generic function. ## Monomorphization Monomorphization is the way Rust resolves generic types at compile time. Rather than keeping any generic type information, the compiler renders everything out into concrete versions using the specified types. For example, take the following code: ``` // Code like this: fn hello(x: T) { println!("hello, {}!", x) } fn main() { hello(10); hello(10.0); } ``` The compiler expands these functions into something like this: ``` // Expands at compile time into code like this: fn hello_i32(x: i32) { println!("hello, {}!", x) } fn hello_f64(x: f64) { println!("hello, {}!", x) } fn main() { hello_i32(10); hello_f64(10.0); } ``` Key points: * Generic functions can't be called from FFI (because there isn't a single function!) * Must have lexical call sites in Rust that calls any function with *all* desired concrete types In order to deal with this, we use a couple of different strategies, depending on the context. ## `Vec` and the Dispatch Pattern To work through a simple example, imagine we had a generic function like this: ## Dispatch Macro Why does this have to be a macro? Why couldn't we just determine the type at runtime? Because in order for the Rust compiler to monomorphize a generic function, the there must be a concrete location in the code that invokes the function with the desired type. Rust has no runtime notion of types. There's no way to have code that expands at runtime to a type that was unknown at compile time. * All generic parameters must be passed as references. In order # Combinators The dispatch pattern works well when the Cartesian product of all possible generic type parameters is relatively small. But if there is a large number of type parameters, the number of match clauses (and the resulting monomorphizations) can become huge, making for very slow compile times. This becomes an issue with the OpenDP combinators. (TODO: link to module after moving combinators to separate module.) ## Glue Structs ## # Memory Management # Bootstrap Metadata In order for external languages to know the FFI functions available, we use a simple bootstrapping mechanism. Every Rust module that wants to export FFI functions must implement a function of the form `XXX__bootstrap()`, where `XXX` is the module path with `::` replaced by `_`. For instance, the module `opendp_ffi::core` has a function `core::opendp_core__bootstrap`. This function returns a `*const c_char` containing a JSON object specifying the FFI entry points of the module. // TODO: Show JSON example.