interoptopus/lib.rs
1#![cfg_attr(docsrs, feature(doc_cfg))] // does this work?
2//!
3//! [![Latest Version]][crates.io]
4//! [![docs]][docs.rs]
5//! ![MIT]
6//! [](https://github.com/ralfbiedert/interoptopus)
7//! [](https://github.com/ralfbiedert/interoptopus/actions/workflows/rust.yml)
8//!
9//! 
10//!
11//! # Interoptopus 🐙
12//!
13//! The polyglot bindings generator for your library.
14//!
15//! Write a robust library in Rust, easily access it from your second-favorite language:
16//!
17//! - Design a single `.dll` / `.so` in Rust, consume it from anywhere.
18//! - Get QoL features (e.g., classes, strings) in languages that have them.
19//! - Always have a sane, C-compatible API.
20//! - Painless workflow, no external tooling required.
21//! - Easy to support more languages, backends fully decoupled from main project.
22//!
23//! We strive to make our generated bindings _zero cost_. They should be as idiomatic
24//! as you could have reasonably written them yourself, but never magic or hiding the interface
25//! you actually wanted to expose.
26//!
27//!
28//!
29//! ## Code you write ...
30//!
31//! ```rust
32//! use interoptopus::{ffi_function, ffi_type, Inventory, InventoryBuilder, function};
33//!
34//! #[ffi_type]
35//! #[repr(C)]
36//! pub struct Vec2 {
37//! pub x: f32,
38//! pub y: f32,
39//! }
40//!
41//! #[ffi_function]
42//! #[no_mangle]
43//! pub extern "C" fn my_function(input: Vec2) {
44//! println!("{}", input.x);
45//! }
46//!
47//! // Define our FFI interface as `ffi_inventory` containing
48//! // a single function `my_function`. Types are inferred.
49//! pub fn ffi_inventory() -> Inventory {
50//! InventoryBuilder::new()
51//! .register(function!(my_function))
52//! .inventory()
53//! }
54//!
55//! ```
56//!
57//!
58//! ## ... Interoptopus generates
59//!
60//! | Language | Crate | Sample Output<sup>1</sup> |
61//! | --- | --- | --- |
62//! | C# | [**interoptopus_backend_csharp**](https://crates.io/crates/interoptopus_backend_csharp) | [Interop.cs](https://github.com/ralfbiedert/interoptopus/blob/master/backends/csharp/tests/output_safe/Interop.cs) |
63//! | C | [**interoptopus_backend_c**](https://crates.io/crates/interoptopus_backend_c) | [my_header.h](https://github.com/ralfbiedert/interoptopus/blob/master/backends/c/tests/output_nodocs/my_header.h) |
64//! | Python | [**interoptopus_backend_cpython**](https://crates.io/crates/interoptopus_backend_cpython) | [reference.py](https://github.com/ralfbiedert/interoptopus/blob/master/backends/cpython/tests/output/reference_project.py) |
65//! | Other | Write your own backend<sup>2</sup> | - |
66//!
67//! <sup>1</sup> For the [reference project](https://github.com/ralfbiedert/interoptopus/tree/master/reference_project/src). <br/>
68//! <sup>2</sup> Add support for a new language in just a few hours. No pull request needed. [Pinkie promise](https://github.com/ralfbiedert/interoptopus/blob/master/FAQ.md#new-backends).
69//!
70//!
71//!
72//! ## Getting Started 🍼
73//!
74//! If you want to ...
75//! - **create a new API** see the [**hello world**](https://github.com/ralfbiedert/interoptopus/tree/master/examples/hello_world),
76//! - **understand what's possible**, see the [**reference project**](https://github.com/ralfbiedert/interoptopus/tree/master/reference_project/src),
77//! - **support a new language**, [**copy the C backend**](https://github.com/ralfbiedert/interoptopus/tree/master/backends/c).
78//!
79//!
80//! ## Supported Rust Constructs
81//!
82//! See the [**reference project**](https://github.com/ralfbiedert/interoptopus/tree/master/reference_project/src) for an overview:
83//! - [functions](https://github.com/ralfbiedert/interoptopus/blob/master/reference_project/src/functions.rs) (`extern "C"` functions and delegates)
84//! - [types](https://github.com/ralfbiedert/interoptopus/blob/master/reference_project/src/types.rs) (composites, enums, opaques, references, ...)
85//! - [constants](https://github.com/ralfbiedert/interoptopus/blob/master/reference_project/src/constants.rs) (primitive constants; results of const evaluation)
86//! - [patterns](https://github.com/ralfbiedert/interoptopus/tree/master/reference_project/src/patterns) (ASCII pointers, options, slices, classes, ...)
87//!
88//!
89//! ## Performance 🏁
90//!
91//! Generated low-level bindings are _zero cost_ w.r.t. hand-crafted bindings for that language.
92//!
93//! That said, even hand-crafted bindings encounter some target-specific overhead
94//! at the FFI boundary (e.g., marshalling or pinning in managed languages). For C# that cost
95//! is often nanoseconds, for Python CFFI it can be microseconds.
96//!
97//! While ultimately there is nothing you can do about a language's FFI performance, being aware of call costs
98//! can help you design better APIs.
99//!
100//! Detailed call cost tables can be found here: <sup>🔥</sup>
101//!
102//! - [**C# call overhead**](https://github.com/ralfbiedert/interoptopus/blob/master/backends/csharp/benches/BENCHMARK_RESULTS.md)
103//! - [**Python call overhead**](https://github.com/ralfbiedert/interoptopus/blob/master/backends/cpython/tests/output/BENCHMARK_RESULTS.md)
104//!
105//! For a quick overview, this table lists the most common call types in _ns / call_:
106//!
107//! | Construct | C# | Python |
108//! | --- | --- | --- |
109//! | `primitive_void()` | 7 | 272 |
110//! | `primitive_u32(0)` | 8 | 392 |
111//! | `many_args_5(0, 0, 0, 0, 0)` | 10 | 786 |
112//! | `callback(x => x, 0)` | 43 | 1168 |
113//!
114//! <br/>
115//!
116//!
117//!
118//! ## Feature Flags
119//!
120//! Gated behind **feature flags**, these enable:
121//!
122//! - `derive` - Proc macros such as `ffi_type`, ...
123//! - `serde` - Serde attributes on internal types.
124//! - `log` - Invoke [log](https://crates.io/crates/log) on FFI errors.
125//!
126//!
127//! ## Changelog
128//!
129//! - **v0.14** - Better inventory UX.
130//! - **v0.13** - Python backend uses `ctypes` now.
131//! - **v0.12** - Better compat using `#[ffi_service_method]`.
132//! - **v0.11** - C# switch ctors to static methods.
133//! - **v0.10** - C# flavors `DotNet` and `Unity` (incl. Burst).
134//! - **v0.9** - 150x faster C# slices, Python type hints.
135//! - **v0.8** - Moved testing functions to respective backends.
136//! - **v0.7** - Make patterns proc macros for better FFI docs.
137//! - **v0.6** - Renamed and clarified many patterns.
138//! - **v0.5** - More ergonomic slice usage in Rust and FFI.
139//! - **v0.4** - Enable logging support in auto-generated FFI calls.
140//! - **v0.3** - Better compatibility with generics.
141//! - **v0.2** - Introduced "patterns"; _working_ interop for C#.
142//! - **v0.1** - First version.
143//!
144//! Also see our [upgrade instructions](https://github.com/ralfbiedert/interoptopus/blob/master/UPGRADE_INSTRUCTIONS.md).
145//!
146//!
147//! ## FAQ
148//!
149//! - [FAQ and Safety Guides](https://github.com/ralfbiedert/interoptopus/blob/master/FAQ.md).
150//!
151//!
152//! ## Contributing
153//!
154//! PRs are welcome.
155//!
156//! - Submit small bug fixes directly. Major changes should be issues first.
157//! - Anything that makes previously working bindings change behavior or stop compiling
158//! is a major change;
159//! - This doesn't mean we're opposed to breaking stuff just that
160//! we'd like to talk about it before it happens.
161//! - New features or patterns must be materialized in the reference project and accompanied by
162//! an interop test (i.e., a backend test running C# / Python against a DLL invoking that code)
163//! in at least one included backend.
164//!
165//! [Latest Version]: https://img.shields.io/crates/v/interoptopus.svg
166//! [crates.io]: https://crates.io/crates/interoptopus
167//! [MIT]: https://img.shields.io/badge/license-MIT-blue.svg
168//! [docs]: https://docs.rs/interoptopus/badge.svg
169//! [docs.rs]: https://docs.rs/interoptopus/
170
171pub use crate::core::{merge_inventories, non_service_functions, Inventory, InventoryBuilder, InventoryItem, Symbol};
172pub use error::Error;
173pub use generators::Interop;
174#[cfg(feature = "derive")]
175#[cfg_attr(docsrs, doc(cfg(feature = "derive")))] // does this work?
176pub use interoptopus_proc::{ffi_constant, ffi_function, ffi_service, ffi_service_ctor, ffi_service_ignore, ffi_service_method, ffi_surrogates, ffi_type};
177
178mod core;
179mod error;
180mod generators;
181pub mod patterns;
182pub mod testing;
183pub mod util;
184pub mod writer;
185
186pub mod lang {
187 //! Abstractions for authors of backends.
188 //!
189 //! A a rule of thumb, types in the [`rust`](rust) module generate instances
190 //! of types in the [`c`](c) module.
191 //!
192 //! Normal users of Interoptopus probably won't have to concern
193 //! themselves with any of the items in this module.
194 //! A notable exception to this rule is having to manually implement
195 //! a [`CType`](`c::CType`) surrogate for un-owned types. See the
196 //! [surrogates section in `ffi_type`](crate::ffi_type#surrogates).
197 pub mod c;
198 pub mod rust;
199}
200
201/// Register a function with an [`InventoryBuilder`].
202///
203/// You must also annotate the function with [`#[ffi_function]`](crate::ffi_function), and preferably with `#[no_mangle]` and `extern "C"`.
204///
205/// # Example
206///
207/// ```rust
208/// use interoptopus::{ffi_function, Inventory, InventoryBuilder, function};
209///
210/// #[ffi_function]
211/// #[no_mangle]
212/// pub extern "C" fn my_function() { }
213///
214/// pub fn inventory() -> Inventory {
215/// InventoryBuilder::new()
216/// .register(function!(my_function))
217/// .inventory()
218/// }
219/// ```
220#[macro_export]
221macro_rules! function {
222 ($x:ty) => {{
223 use $crate::lang::rust::FunctionInfo;
224 // use $x as fnc;
225 let info = <$x>::function_info();
226 $crate::Symbol::Function(info)
227 }};
228}
229
230/// Register a constant with an [`InventoryBuilder`].
231///
232/// You must also annotate the constant with [`#[ffi_constant]`](crate::ffi_constant).
233///
234/// # Example
235///
236/// ```rust
237/// use interoptopus::{ffi_constant, Inventory, InventoryBuilder, constant};
238///
239/// #[ffi_constant]
240/// pub const MY_CONSTANT: u32 = 123;
241///
242/// pub fn inventory() -> Inventory {
243/// InventoryBuilder::new()
244/// .register(constant!(MY_CONSTANT))
245/// .inventory()
246/// }
247/// ```
248#[macro_export]
249macro_rules! constant {
250 ($x:path) => {{
251 use $crate::lang::rust::ConstantInfo;
252 use $x as constant;
253 let info = constant::constant_info();
254 $crate::Symbol::Constant(info)
255 }};
256}
257
258/// Register an extra type with an [`InventoryBuilder`].
259///
260/// You must also annotate the type with [`#[ffi_type]`](crate::ffi_type) and `#[repr(C)]`.
261///
262/// Note, most types are registered automatically. You only need this to pass types not directly visible in
263/// your function signatures, e.g., when accepting multiple types via a void pointer.
264///
265/// # Example
266///
267/// ```rust
268/// use interoptopus::{ffi_type, Inventory, InventoryBuilder, extra_type};
269///
270/// #[ffi_type]
271/// #[repr(C)]
272/// pub struct S<T> {
273/// t: T
274/// };
275///
276/// pub fn inventory() -> Inventory {
277/// InventoryBuilder::new()
278/// .register(extra_type!(S<f32>))
279/// .inventory()
280/// }
281#[macro_export]
282macro_rules! extra_type {
283 ($x:ty) => {{
284 let info = <$x as $crate::lang::rust::CTypeInfo>::type_info();
285 $crate::Symbol::Type(info)
286 }};
287}
288
289/// Register a pattern with an [`InventoryBuilder`].
290///
291/// You only need to register [`LibraryPattern`](crate::patterns::LibraryPattern), as [`TypePattern`](crate::patterns::TypePattern) are detected automatically.
292///
293/// # Example
294///
295/// Note, as this example focuses on the `pattern` macro it omits the definition of `Error` and `MyFFIError`.
296/// Their implementation can be found in the [FFIError](crate::patterns::result::FFIError) example.
297///
298/// ```rust
299/// use interoptopus::{ffi_type, ffi_service, ffi_service_ctor, Inventory, InventoryBuilder, pattern};
300///
301/// # use std::fmt::{Display, Formatter};
302/// # use interoptopus::patterns::result::FFIError;
303/// #
304/// # #[derive(Debug)]
305/// # pub enum Error {
306/// # Bad,
307/// # }
308/// #
309/// # impl Display for Error {
310/// # fn fmt(&self, _: &mut Formatter<'_>) -> std::fmt::Result {
311/// # Ok(())
312/// # }
313/// # }
314/// #
315/// # impl std::error::Error for Error {}
316/// #
317/// # #[ffi_type(patterns(ffi_error))]
318/// # #[repr(C)]
319/// # pub enum MyFFIError {
320/// # Ok = 0,
321/// # NullPassed = 1,
322/// # Panic = 2,
323/// # OtherError = 3,
324/// # }
325/// #
326/// # impl FFIError for MyFFIError {
327/// # const SUCCESS: Self = Self::Ok;
328/// # const NULL: Self = Self::NullPassed;
329/// # const PANIC: Self = Self::Panic;
330/// # }
331/// #
332/// # impl From<Error> for MyFFIError {
333/// # fn from(x: Error) -> Self {
334/// # match x {
335/// # Error::Bad => Self::OtherError,
336/// # }
337/// # }
338/// # }
339/// #
340///
341/// #[ffi_type(opaque)]
342/// pub struct SimpleService {
343/// pub some_value: u32,
344/// }
345///
346/// #[ffi_service(error = "MyFFIError", prefix = "simple_service_")]
347/// impl SimpleService {
348///
349/// #[ffi_service_ctor]
350/// pub fn new_with(some_value: u32) -> Result<Self, Error> {
351/// Ok(Self { some_value })
352/// }
353/// }
354///
355/// pub fn inventory() -> Inventory {
356/// InventoryBuilder::new()
357/// .register(pattern!(SimpleService))
358/// .inventory()
359/// }
360#[macro_export]
361macro_rules! pattern {
362 ($x:path) => {{
363 let info: $crate::patterns::LibraryPattern = <$x as $crate::patterns::LibraryPatternInfo>::pattern_info();
364 $crate::Symbol::Pattern(info)
365 }};
366}