1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
//! FFI bindings for OpenDP.
//!
//! # Overview
//!
//! This module contains utilities necessary for the FFI bindings.
//! It is public so you can write your own lightweight FFI for quick one-off language integrations,
//! but its use should be rare: More often, you should use the Rust API directly.
//!
//! # 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<T: std::fmt::Display>(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<Type>` 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
//!

#[macro_use]
pub mod dispatch;
pub mod any;
pub(crate) mod util;

// replacement for ? operator, for FfiResults
#[macro_export]
macro_rules! try_ {
    ($value:expr) => {
        match $value {
            Ok(x) => x,
            Err(e) => return e.into(),
        }
    };
}
// attempt to convert a raw pointer to a reference
//      as_ref      ok_or_else       try_!
// *mut T -> Option<&T> -> Fallible<&T> -> &T
#[macro_export]
macro_rules! try_as_ref {
    ($value:expr) => {
        try_!($crate::ffi::util::as_ref($value)
            .ok_or_else(|| err!(FFI, concat!("null pointer: ", stringify!($value)))))
    };
}

#[macro_export]
macro_rules! try_as_mut_ref {
    ($value:expr) => {
        try_!($crate::ffi::util::as_mut_ref($value)
            .ok_or_else(|| err!(FFI, concat!("null pointer: ", stringify!($value)))))
    };
}