interoptopus/patterns/
api_entry.rs

1//! Helpers for API loading patterns as [described in this blog post](https://anteru.net/blog/2016/designing-c-apis-in-2016/).<sup>🚧</sup>
2//!
3//! See the macro [api_entry](crate::api_entry) for details.
4
5/// Defines a new API entry function and corresponding struct.
6///
7/// The resulting function can be called via FFI and will return a struct, pointing to all exported functions.
8///
9/// # Example
10///
11/// In this example, other languages can call `my_api_init_v1` to obtain a struct of type `MyAPIv1`
12/// exporting `f1` and `f2`.
13///
14/// ```rust
15/// use interoptopus::{api_entry, ffi_function};
16///
17/// #[ffi_function]
18/// extern "C" fn f1() {}
19///
20/// #[ffi_function]
21/// extern "C" fn f2() {}
22///
23/// api_entry!(MyAPIv1, my_api_init_v1, [f1, f2]);
24/// ```
25#[doc(hidden)]
26#[macro_export]
27macro_rules! api_entry {
28    (
29        $struct:ident,
30        $init:ident,
31        [
32            $($function:ident),*
33        ]
34    ) => {
35        pub struct $struct {
36            $($function: <$function as interoptopus::lang::rust::FunctionInfo>::Signature,)*
37        }
38
39        #[interoptopus::ffi_function]
40        #[no_mangle]
41        pub unsafe extern "C" fn $init(api: *mut $struct) {
42            if api.is_null() {
43                return;
44            }
45
46            let s = $struct {
47                $(
48                    $function: $function,
49                )*
50            };
51
52            unsafe { *api = s; }
53        }
54
55
56        unsafe impl interoptopus::lang::rust::CTypeInfo for $struct {
57            fn type_info() -> interoptopus::lang::c::CType {
58                let mut fields = Vec::new();
59
60                $(
61                    {
62                        use interoptopus::lang::rust::FunctionInfo;
63                        use $function as x;
64                        let function: interoptopus::lang::c::Function = x::function_info();
65                        let t = interoptopus::lang::c::CType::FnPointer(interoptopus::lang::c::FnPointerType::new(function.signature().clone()));
66                        let field = interoptopus::lang::c::Field::new(function.name().to_string(), t);
67                        fields.push(field);
68                    }
69                )*
70
71                let composite = interoptopus::lang::c::CompositeType::new(stringify!($struct).to_string(), fields);
72                interoptopus::lang::c::CType::Composite(composite)
73            }
74        }
75    };
76}