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
//! Helpers for API loading patterns as [described in this blog post](https://anteru.net/blog/2016/designing-c-apis-in-2016/).
//!
//! See [this macro](crate::pattern_api_entry) for details.

/// Defines a new API entry function and corresponding struct.
///
/// The resulting function can be called via FFI and will return a struct, pointing to all exported functions.
///
/// # Example
///
/// In this example, other languages can call `my_api_init_v1` to obtain a struct of type `MyAPIv1`
/// exporting `f1` and `f2`.
///
/// ```rust
/// use interoptopus::{pattern_api_entry, ffi_function};
///
/// #[ffi_function]
/// extern "C" fn f1() {}
///
/// #[ffi_function]
/// extern "C" fn f2() {}
///
/// pattern_api_entry!(MyAPIv1, my_api_init_v1, [f1, f2]);
/// ```
#[macro_export]
macro_rules! pattern_api_entry {
    (
        $struct:ident,
        $init:ident,
        [
            $($function:ident),*
        ]
    ) => {
        pub struct $struct {
            $($function: <$function as interoptopus::lang::rust::FunctionInfo>::Signature,)*
        }

        #[interoptopus::ffi_function]
        #[no_mangle]
        pub extern "C" fn $init(api: *mut $struct) {
            if api.is_null() {
                return;
            }

            let s = $struct {
                $(
                    $function: $function,
                )*
            };

            unsafe { *api = s; }
        }


        unsafe impl interoptopus::lang::rust::CTypeInfo for $struct {
            fn type_info() -> interoptopus::lang::c::CType {
                let mut fields = Vec::new();

                $(
                    {
                        use interoptopus::lang::rust::FunctionInfo;
                        use $function as x;
                        let function: interoptopus::lang::c::Function = x::function_info();
                        let t = interoptopus::lang::c::CType::FnPointer(interoptopus::lang::c::FnPointerType::new(function.signature().clone()));
                        let field = interoptopus::lang::c::Field::new(function.name().to_string(), t);
                        fields.push(field);
                    }
                )*

                let composite = interoptopus::lang::c::CompositeType::new(stringify!($struct).to_string(), fields);
                interoptopus::lang::c::CType::Composite(composite)
            }
        }
    };
}