From 64af68bbeb6401f5e5ab87b9a3201290cba40a52 Mon Sep 17 00:00:00 2001 From: Ralf Biedert Date: Sat, 5 Oct 2024 22:42:17 +0200 Subject: [PATCH] Update tests & READMEs. --- README.md | 12 ++ .../output_docs_inline/my_header.h.expected | 35 ++--- .../tests/output_nodocs/my_header.h.expected | 31 +++-- .../output_typedefs/my_header.h.expected | 35 ++--- .../output/reference_project.py.expected | 112 +++++++++------- backends/cpython/tests/output/tests.py | 7 - .../csharp/tests/output/reference_project.md | 74 +++++++---- .../tests/output_safe/Interop.cs.expected | 105 ++++++++------- .../output_unity/Assets/Interop.cs.expected | 123 ++++++++++-------- .../tests/output_unsafe/Interop.cs.expected | 123 ++++++++++-------- core/README.md | 12 ++ 11 files changed, 400 insertions(+), 269 deletions(-) diff --git a/README.md b/README.md index f8ef0d3f..838863db 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,17 @@ If you want to ... - **understand what's possible**, see the [**reference project**](https://github.com/ralfbiedert/interoptopus/tree/master/reference_project/src), - **support a new language**, [**copy the C backend**](https://github.com/ralfbiedert/interoptopus/tree/master/backends/c). +For a "production-ready" project you generally want to follow these 3 steps: + +- Write a Rust crate, such as `my_library` and solve your problem in a Rust-idiomatic way. +- Add a crate `my_library_ffi`, which holds interop types and C-style definitions for your library similar to the examples above. + - For example, for a `my_library::Container` you probably want to a FFI sibling `my_library_ffi::Container`. + - While this might seem like extra work (it is) it gives you fine-grained access to what you expose. Once your project reaches a certain size, this will be invaluable. + - You'll probably want to add some conversion methods between your types, and FFI exposed methods to allow your interop users + to interact with these types in a controlled fashion. +- Add a crate `my_library_interop`. Inside there reference the backends for which you want to create interop definitions + (either in some unit tests, a `main.rs` or `build.rs`), and invoke the interop generation when needed; again, compare examples above. + ### Supported Rust Constructs @@ -125,6 +136,7 @@ Gated behind **feature flags**, these enable: ### Changelog +- **v0.15** - Updated to syn2, bug fixes. - **v0.14** - Better inventory UX. - **v0.13** - Python backend uses `ctypes` now. - **v0.12** - Better compat using `#[ffi_service_method]`. diff --git a/backends/c/tests/output_docs_inline/my_header.h.expected b/backends/c/tests/output_docs_inline/my_header.h.expected index f4b2954a..30ddd12b 100644 --- a/backends/c/tests/output_docs_inline/my_header.h.expected +++ b/backends/c/tests/output_docs_inline/my_header.h.expected @@ -38,8 +38,6 @@ typedef struct my_library_generic3 my_library_generic3; typedef struct my_library_generic4 my_library_generic4; -typedef struct my_library_opaque my_library_opaque; - /// Some struct we want to expose as a class. typedef struct my_library_simple_service my_library_simple_service; @@ -97,6 +95,11 @@ typedef struct my_library_inner float x; } my_library_inner; +typedef struct my_library_local +{ + uint32_t x; +} my_library_local; + #pragma pack(push, 1) typedef struct my_library_packed1 { @@ -118,11 +121,6 @@ typedef struct my_library_phantomu8 uint32_t x; } my_library_phantomu8; -typedef struct my_library_some_foreign_type -{ - uint32_t x; -} my_library_some_foreign_type; - /// Documented struct. typedef struct my_library_struct_documented { @@ -200,6 +198,11 @@ typedef struct my_library_array uint8_t data[16]; } my_library_array; +typedef struct my_library_container +{ + my_library_local foreign; +} my_library_container; + typedef struct my_library_genericu32 { const uint32_t* x; @@ -388,8 +391,6 @@ my_library_tupled tupled(my_library_tupled x); my_library_ffi_error complex_args_1(my_library_vec3f32 a, const my_library_tupled* b); -const my_library_opaque* complex_args_2(my_library_some_foreign_type cmplx); - uint8_t callback(my_library_fptr_fn_u8_rval_u8 callback, uint8_t value); uint32_t generic_1a(my_library_genericu32 x, my_library_phantomu8 y); @@ -483,6 +484,8 @@ void pattern_callback_3(my_library_delegate_callback_my_callback_contextual call uint32_t pattern_callback_4(my_library_my_callback_namespaced callback, uint32_t x); +void pattern_surrogates_1(my_library_local s, my_library_container* c); + /// Destroys the given instance. /// /// # Safety @@ -511,6 +514,8 @@ uint32_t simple_service_method_value(const my_library_simple_service* context, u /// Multiple lines. void simple_service_method_void(const my_library_simple_service* context); +void simple_service_method_void2(const my_library_simple_service* context); + uint8_t simple_service_method_mut_self(my_library_simple_service* context, my_library_sliceu8 slice); /// Single line. @@ -547,17 +552,17 @@ my_library_ffi_error simple_service_method_callback(my_library_simple_service* c /// /// The passed parameter MUST have been created with the corresponding init function; /// passing any other value results in undefined behavior. -my_library_ffi_error simple_service_lt_destroy(my_library_simple_service_lifetime** context); +my_library_ffi_error simple_service_lifetime_destroy(my_library_simple_service_lifetime** context); -my_library_ffi_error simple_service_lt_new_with(my_library_simple_service_lifetime** context, const uint32_t* some_value); +my_library_ffi_error simple_service_lifetime_new_with(my_library_simple_service_lifetime** context, const uint32_t* some_value); -void simple_service_lt_method_lt(my_library_simple_service_lifetime* context, my_library_slice_bool slice); +void simple_service_lifetime_method_lt(my_library_simple_service_lifetime* context, my_library_slice_bool slice); -void simple_service_lt_method_lt2(my_library_simple_service_lifetime* context, my_library_slice_bool slice); +void simple_service_lifetime_method_lt2(my_library_simple_service_lifetime* context, my_library_slice_bool slice); -const char* simple_service_lt_return_string_accept_slice(my_library_simple_service_lifetime* anon0, my_library_sliceu8 anon1); +const char* simple_service_lifetime_return_string_accept_slice(my_library_simple_service_lifetime* anon0, my_library_sliceu8 anon1); -my_library_ffi_error simple_service_lt_method_void_ffi_error(my_library_simple_service_lifetime* context); +my_library_ffi_error simple_service_lifetime_method_void_ffi_error(my_library_simple_service_lifetime* context); #ifdef __cplusplus diff --git a/backends/c/tests/output_nodocs/my_header.h.expected b/backends/c/tests/output_nodocs/my_header.h.expected index 3d2cec9b..00843d63 100644 --- a/backends/c/tests/output_nodocs/my_header.h.expected +++ b/backends/c/tests/output_nodocs/my_header.h.expected @@ -31,7 +31,6 @@ typedef enum my_library_enumrenamed typedef struct my_library_generic2u8 my_library_generic2u8; typedef struct my_library_generic3 my_library_generic3; typedef struct my_library_generic4 my_library_generic4; -typedef struct my_library_opaque my_library_opaque; typedef struct my_library_simpleservice my_library_simpleservice; typedef struct my_library_simpleservicelifetime my_library_simpleservicelifetime; typedef enum my_library_ffierror @@ -86,6 +85,11 @@ typedef struct my_library_inner float x; } my_library_inner; +typedef struct my_library_local + { + uint32_t x; + } my_library_local; + #pragma pack(push, 1) typedef struct my_library_packed1 { @@ -107,11 +111,6 @@ typedef struct my_library_phantomu8 uint32_t x; } my_library_phantomu8; -typedef struct my_library_someforeigntype - { - uint32_t x; - } my_library_someforeigntype; - typedef struct my_library_structdocumented { float x; @@ -187,6 +186,11 @@ typedef struct my_library_array uint8_t data[16]; } my_library_array; +typedef struct my_library_container + { + my_library_local foreign; + } my_library_container; + typedef struct my_library_genericu32 { const uint32_t* x; @@ -313,7 +317,6 @@ bool ref_option(const int64_t* x); bool ref_mut_option(int64_t* x); my_library_tupled tupled(my_library_tupled x); my_library_ffierror complex_args_1(my_library_vec3f32 a, const my_library_tupled* b); -const my_library_opaque* complex_args_2(my_library_someforeigntype cmplx); uint8_t callback(my_library_fptr_fn_u8_rval_u8 callback, uint8_t value); uint32_t generic_1a(my_library_genericu32 x, my_library_phantomu8 y); uint8_t generic_1b(my_library_genericu8 x, my_library_phantomu8 y); @@ -360,6 +363,7 @@ uint32_t pattern_callback_1(my_library_mycallback callback, uint32_t x); my_library_mycallbackvoid pattern_callback_2(my_library_mycallbackvoid callback); void pattern_callback_3(my_library_delegatecallbackmycallbackcontextual callback, uint32_t x); uint32_t pattern_callback_4(my_library_mycallbacknamespaced callback, uint32_t x); +void pattern_surrogates_1(my_library_local s, my_library_container* c); my_library_ffierror simple_service_destroy(my_library_simpleservice** context); my_library_ffierror simple_service_new_with(my_library_simpleservice** context, uint32_t some_value); my_library_ffierror simple_service_new_without(my_library_simpleservice** context); @@ -368,6 +372,7 @@ my_library_ffierror simple_service_new_failing(my_library_simpleservice** contex my_library_ffierror simple_service_method_result(const my_library_simpleservice* context, uint32_t anon1); uint32_t simple_service_method_value(const my_library_simpleservice* context, uint32_t x); void simple_service_method_void(const my_library_simpleservice* context); +void simple_service_method_void2(const my_library_simpleservice* context); uint8_t simple_service_method_mut_self(my_library_simpleservice* context, my_library_sliceu8 slice); void simple_service_method_mut_self_void(my_library_simpleservice* context, my_library_slicebool slice); uint8_t simple_service_method_mut_self_ref(my_library_simpleservice* context, const uint8_t* x, uint8_t* y); @@ -380,12 +385,12 @@ my_library_slicemutu32 simple_service_return_slice_mut(my_library_simpleservice* const char* simple_service_return_string(my_library_simpleservice* context); my_library_ffierror simple_service_method_void_ffi_error(my_library_simpleservice* context); my_library_ffierror simple_service_method_callback(my_library_simpleservice* context, my_library_mycallback callback); -my_library_ffierror simple_service_lt_destroy(my_library_simpleservicelifetime** context); -my_library_ffierror simple_service_lt_new_with(my_library_simpleservicelifetime** context, const uint32_t* some_value); -void simple_service_lt_method_lt(my_library_simpleservicelifetime* context, my_library_slicebool slice); -void simple_service_lt_method_lt2(my_library_simpleservicelifetime* context, my_library_slicebool slice); -const char* simple_service_lt_return_string_accept_slice(my_library_simpleservicelifetime* anon0, my_library_sliceu8 anon1); -my_library_ffierror simple_service_lt_method_void_ffi_error(my_library_simpleservicelifetime* context); +my_library_ffierror simple_service_lifetime_destroy(my_library_simpleservicelifetime** context); +my_library_ffierror simple_service_lifetime_new_with(my_library_simpleservicelifetime** context, const uint32_t* some_value); +void simple_service_lifetime_method_lt(my_library_simpleservicelifetime* context, my_library_slicebool slice); +void simple_service_lifetime_method_lt2(my_library_simpleservicelifetime* context, my_library_slicebool slice); +const char* simple_service_lifetime_return_string_accept_slice(my_library_simpleservicelifetime* anon0, my_library_sliceu8 anon1); +my_library_ffierror simple_service_lifetime_method_void_ffi_error(my_library_simpleservicelifetime* context); #ifdef __cplusplus } diff --git a/backends/c/tests/output_typedefs/my_header.h.expected b/backends/c/tests/output_typedefs/my_header.h.expected index dd187935..7fb58a66 100644 --- a/backends/c/tests/output_typedefs/my_header.h.expected +++ b/backends/c/tests/output_typedefs/my_header.h.expected @@ -38,8 +38,6 @@ typedef struct my_library_generic3 my_library_generic3; typedef struct my_library_generic4 my_library_generic4; -typedef struct my_library_opaque my_library_opaque; - /// Some struct we want to expose as a class. typedef struct my_library_simple_service my_library_simple_service; @@ -97,6 +95,11 @@ typedef struct my_library_inner float x; } my_library_inner; +typedef struct my_library_local +{ + uint32_t x; +} my_library_local; + #pragma pack(push, 1) typedef struct my_library_packed1 { @@ -118,11 +121,6 @@ typedef struct my_library_phantomu8 uint32_t x; } my_library_phantomu8; -typedef struct my_library_some_foreign_type -{ - uint32_t x; -} my_library_some_foreign_type; - /// Documented struct. typedef struct my_library_struct_documented { @@ -200,6 +198,11 @@ typedef struct my_library_array uint8_t data[16]; } my_library_array; +typedef struct my_library_container +{ + my_library_local foreign; +} my_library_container; + typedef struct my_library_genericu32 { const uint32_t* x; @@ -388,8 +391,6 @@ typedef my_library_tupled (*tupled)(my_library_tupled); typedef my_library_ffi_error (*complex_args_1)(my_library_vec3f32, const my_library_tupled*); -typedef const my_library_opaque* (*complex_args_2)(my_library_some_foreign_type); - typedef uint8_t (*callback)(my_library_fptr_fn_u8_rval_u8, uint8_t); typedef uint32_t (*generic_1a)(my_library_genericu32, my_library_phantomu8); @@ -483,6 +484,8 @@ typedef void (*pattern_callback_3)(my_library_delegate_callback_my_callback_cont typedef uint32_t (*pattern_callback_4)(my_library_my_callback_namespaced, uint32_t); +typedef void (*pattern_surrogates_1)(my_library_local, my_library_container*); + /// Destroys the given instance. /// /// # Safety @@ -511,6 +514,8 @@ typedef uint32_t (*simple_service_method_value)(const my_library_simple_service* /// Multiple lines. typedef void (*simple_service_method_void)(const my_library_simple_service*); +typedef void (*simple_service_method_void2)(const my_library_simple_service*); + typedef uint8_t (*simple_service_method_mut_self)(my_library_simple_service*, my_library_sliceu8); /// Single line. @@ -547,17 +552,17 @@ typedef my_library_ffi_error (*simple_service_method_callback)(my_library_simple /// /// The passed parameter MUST have been created with the corresponding init function; /// passing any other value results in undefined behavior. -typedef my_library_ffi_error (*simple_service_lt_destroy)(my_library_simple_service_lifetime**); +typedef my_library_ffi_error (*simple_service_lifetime_destroy)(my_library_simple_service_lifetime**); -typedef my_library_ffi_error (*simple_service_lt_new_with)(my_library_simple_service_lifetime**, const uint32_t*); +typedef my_library_ffi_error (*simple_service_lifetime_new_with)(my_library_simple_service_lifetime**, const uint32_t*); -typedef void (*simple_service_lt_method_lt)(my_library_simple_service_lifetime*, my_library_slice_bool); +typedef void (*simple_service_lifetime_method_lt)(my_library_simple_service_lifetime*, my_library_slice_bool); -typedef void (*simple_service_lt_method_lt2)(my_library_simple_service_lifetime*, my_library_slice_bool); +typedef void (*simple_service_lifetime_method_lt2)(my_library_simple_service_lifetime*, my_library_slice_bool); -typedef const char* (*simple_service_lt_return_string_accept_slice)(my_library_simple_service_lifetime*, my_library_sliceu8); +typedef const char* (*simple_service_lifetime_return_string_accept_slice)(my_library_simple_service_lifetime*, my_library_sliceu8); -typedef my_library_ffi_error (*simple_service_lt_method_void_ffi_error)(my_library_simple_service_lifetime*); +typedef my_library_ffi_error (*simple_service_lifetime_method_void_ffi_error)(my_library_simple_service_lifetime*); #ifdef __cplusplus diff --git a/backends/cpython/tests/output/reference_project.py.expected b/backends/cpython/tests/output/reference_project.py.expected index ebbeccd9..059e544a 100644 --- a/backends/cpython/tests/output/reference_project.py.expected +++ b/backends/cpython/tests/output/reference_project.py.expected @@ -36,7 +36,6 @@ def init_lib(path): c_lib.ref_mut_option.argtypes = [ctypes.POINTER(ctypes.c_int64)] c_lib.tupled.argtypes = [Tupled] c_lib.complex_args_1.argtypes = [Vec3f32, ctypes.POINTER(Tupled)] - c_lib.complex_args_2.argtypes = [SomeForeignType] c_lib.callback.argtypes = [ctypes.CFUNCTYPE(ctypes.c_uint8, ctypes.c_uint8), ctypes.c_uint8] c_lib.generic_1a.argtypes = [Genericu32, Phantomu8] c_lib.generic_1b.argtypes = [Genericu8, Phantomu8] @@ -83,6 +82,7 @@ def init_lib(path): c_lib.pattern_callback_2.argtypes = [ctypes.CFUNCTYPE(None, ctypes.c_void_p)] c_lib.pattern_callback_3.argtypes = [DelegateCallbackMyCallbackContextual, ctypes.c_uint32] c_lib.pattern_callback_4.argtypes = [ctypes.CFUNCTYPE(ctypes.c_uint32, ctypes.c_uint32), ctypes.c_uint32] + c_lib.pattern_surrogates_1.argtypes = [Local, ctypes.POINTER(Container)] c_lib.simple_service_destroy.argtypes = [ctypes.POINTER(ctypes.c_void_p)] c_lib.simple_service_new_with.argtypes = [ctypes.POINTER(ctypes.c_void_p), ctypes.c_uint32] c_lib.simple_service_new_without.argtypes = [ctypes.POINTER(ctypes.c_void_p)] @@ -91,6 +91,7 @@ def init_lib(path): c_lib.simple_service_method_result.argtypes = [ctypes.c_void_p, ctypes.c_uint32] c_lib.simple_service_method_value.argtypes = [ctypes.c_void_p, ctypes.c_uint32] c_lib.simple_service_method_void.argtypes = [ctypes.c_void_p] + c_lib.simple_service_method_void2.argtypes = [ctypes.c_void_p] c_lib.simple_service_method_mut_self.argtypes = [ctypes.c_void_p, Sliceu8] c_lib.simple_service_method_mut_self_void.argtypes = [ctypes.c_void_p, SliceBool] c_lib.simple_service_method_mut_self_ref.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_uint8), ctypes.POINTER(ctypes.c_uint8)] @@ -103,12 +104,12 @@ def init_lib(path): c_lib.simple_service_return_string.argtypes = [ctypes.c_void_p] c_lib.simple_service_method_void_ffi_error.argtypes = [ctypes.c_void_p] c_lib.simple_service_method_callback.argtypes = [ctypes.c_void_p, ctypes.CFUNCTYPE(ctypes.c_uint32, ctypes.c_uint32)] - c_lib.simple_service_lt_destroy.argtypes = [ctypes.POINTER(ctypes.c_void_p)] - c_lib.simple_service_lt_new_with.argtypes = [ctypes.POINTER(ctypes.c_void_p), ctypes.POINTER(ctypes.c_uint32)] - c_lib.simple_service_lt_method_lt.argtypes = [ctypes.c_void_p, SliceBool] - c_lib.simple_service_lt_method_lt2.argtypes = [ctypes.c_void_p, SliceBool] - c_lib.simple_service_lt_return_string_accept_slice.argtypes = [ctypes.c_void_p, Sliceu8] - c_lib.simple_service_lt_method_void_ffi_error.argtypes = [ctypes.c_void_p] + c_lib.simple_service_lifetime_destroy.argtypes = [ctypes.POINTER(ctypes.c_void_p)] + c_lib.simple_service_lifetime_new_with.argtypes = [ctypes.POINTER(ctypes.c_void_p), ctypes.POINTER(ctypes.c_uint32)] + c_lib.simple_service_lifetime_method_lt.argtypes = [ctypes.c_void_p, SliceBool] + c_lib.simple_service_lifetime_method_lt2.argtypes = [ctypes.c_void_p, SliceBool] + c_lib.simple_service_lifetime_return_string_accept_slice.argtypes = [ctypes.c_void_p, Sliceu8] + c_lib.simple_service_lifetime_method_void_ffi_error.argtypes = [ctypes.c_void_p] c_lib.primitive_bool.restype = ctypes.c_bool c_lib.primitive_u8.restype = ctypes.c_uint8 @@ -134,7 +135,6 @@ def init_lib(path): c_lib.ref_mut_option.restype = ctypes.c_bool c_lib.tupled.restype = Tupled c_lib.complex_args_1.restype = ctypes.c_int - c_lib.complex_args_2.restype = ctypes.c_void_p c_lib.callback.restype = ctypes.c_uint8 c_lib.generic_1a.restype = ctypes.c_uint32 c_lib.generic_1b.restype = ctypes.c_uint8 @@ -192,10 +192,10 @@ def init_lib(path): c_lib.simple_service_return_string.restype = ctypes.POINTER(ctypes.c_char) c_lib.simple_service_method_void_ffi_error.restype = ctypes.c_int c_lib.simple_service_method_callback.restype = ctypes.c_int - c_lib.simple_service_lt_destroy.restype = ctypes.c_int - c_lib.simple_service_lt_new_with.restype = ctypes.c_int - c_lib.simple_service_lt_return_string_accept_slice.restype = ctypes.POINTER(ctypes.c_char) - c_lib.simple_service_lt_method_void_ffi_error.restype = ctypes.c_int + c_lib.simple_service_lifetime_destroy.restype = ctypes.c_int + c_lib.simple_service_lifetime_new_with.restype = ctypes.c_int + c_lib.simple_service_lifetime_return_string_accept_slice.restype = ctypes.POINTER(ctypes.c_char) + c_lib.simple_service_lifetime_method_void_ffi_error.restype = ctypes.c_int c_lib.complex_args_1.errcheck = lambda rval, _fptr, _args: _errcheck(rval, 0) c_lib.panics.errcheck = lambda rval, _fptr, _args: _errcheck(rval, 0) @@ -209,9 +209,9 @@ def init_lib(path): c_lib.simple_service_method_mut_self_no_error.errcheck = lambda rval, _fptr, _args: _errcheck(rval, 0) c_lib.simple_service_method_void_ffi_error.errcheck = lambda rval, _fptr, _args: _errcheck(rval, 0) c_lib.simple_service_method_callback.errcheck = lambda rval, _fptr, _args: _errcheck(rval, 0) - c_lib.simple_service_lt_destroy.errcheck = lambda rval, _fptr, _args: _errcheck(rval, 0) - c_lib.simple_service_lt_new_with.errcheck = lambda rval, _fptr, _args: _errcheck(rval, 0) - c_lib.simple_service_lt_method_void_ffi_error.errcheck = lambda rval, _fptr, _args: _errcheck(rval, 0) + c_lib.simple_service_lifetime_destroy.errcheck = lambda rval, _fptr, _args: _errcheck(rval, 0) + c_lib.simple_service_lifetime_new_with.errcheck = lambda rval, _fptr, _args: _errcheck(rval, 0) + c_lib.simple_service_lifetime_method_void_ffi_error.errcheck = lambda rval, _fptr, _args: _errcheck(rval, 0) def primitive_void(): @@ -295,9 +295,6 @@ def tupled(x: Tupled) -> Tupled: def complex_args_1(a: Vec3f32, b: ctypes.POINTER(Tupled)): return c_lib.complex_args_1(a, b) -def complex_args_2(cmplx: SomeForeignType) -> ctypes.c_void_p: - return c_lib.complex_args_2(cmplx) - def callback(callback, value: int) -> int: if not hasattr(callback, "__ctypes_from_outparam__"): callback = callbacks.fn_u8_rval_u8(callback) @@ -490,6 +487,9 @@ def pattern_callback_4(callback, x: int) -> int: return c_lib.pattern_callback_4(callback, x) +def pattern_surrogates_1(s: Local, c: ctypes.POINTER(Container)): + return c_lib.pattern_surrogates_1(s, c) + U8 = 255 @@ -822,20 +822,16 @@ class Inner(ctypes.Structure): return ctypes.Structure.__set__(self, "x", value) -class Packed1(ctypes.Structure): - _pack_ = 1 +class Local(ctypes.Structure): # These fields represent the underlying C data layout _fields_ = [ - ("x", ctypes.c_uint8), - ("y", ctypes.c_uint16), + ("x", ctypes.c_uint32), ] - def __init__(self, x: int = None, y: int = None): + def __init__(self, x: int = None): if x is not None: self.x = x - if y is not None: - self.y = y @property def x(self) -> int: @@ -845,16 +841,8 @@ class Packed1(ctypes.Structure): def x(self, value: int): return ctypes.Structure.__set__(self, "x", value) - @property - def y(self) -> int: - return ctypes.Structure.__get__(self, "y") - - @y.setter - def y(self, value: int): - return ctypes.Structure.__set__(self, "y", value) - -class Packed2(ctypes.Structure): +class Packed1(ctypes.Structure): _pack_ = 1 # These fields represent the underlying C data layout @@ -886,16 +874,20 @@ class Packed2(ctypes.Structure): return ctypes.Structure.__set__(self, "y", value) -class Phantomu8(ctypes.Structure): +class Packed2(ctypes.Structure): + _pack_ = 1 # These fields represent the underlying C data layout _fields_ = [ - ("x", ctypes.c_uint32), + ("x", ctypes.c_uint8), + ("y", ctypes.c_uint16), ] - def __init__(self, x: int = None): + def __init__(self, x: int = None, y: int = None): if x is not None: self.x = x + if y is not None: + self.y = y @property def x(self) -> int: @@ -905,8 +897,16 @@ class Phantomu8(ctypes.Structure): def x(self, value: int): return ctypes.Structure.__set__(self, "x", value) + @property + def y(self) -> int: + return ctypes.Structure.__get__(self, "y") + + @y.setter + def y(self, value: int): + return ctypes.Structure.__set__(self, "y", value) + -class SomeForeignType(ctypes.Structure): +class Phantomu8(ctypes.Structure): # These fields represent the underlying C data layout _fields_ = [ @@ -1246,6 +1246,26 @@ class Array(ctypes.Structure): return ctypes.Structure.__set__(self, "data", value) +class Container(ctypes.Structure): + + # These fields represent the underlying C data layout + _fields_ = [ + ("foreign", Local), + ] + + def __init__(self, foreign: Local = None): + if foreign is not None: + self.foreign = foreign + + @property + def foreign(self) -> Local: + return ctypes.Structure.__get__(self, "foreign") + + @foreign.setter + def foreign(self, value: Local): + return ctypes.Structure.__set__(self, "foreign", value) + + class Genericu32(ctypes.Structure): # These fields represent the underlying C data layout @@ -1984,6 +2004,10 @@ class SimpleService: Multiple lines.""" return c_lib.simple_service_method_void(self._ctx, ) + def method_void2(self, ): + """""" + return c_lib.simple_service_method_void2(self._ctx, ) + def method_mut_self(self, slice: Sliceu8 | ctypes.Array[ctypes.c_uint8]) -> int: """""" if hasattr(slice, "_length_") and getattr(slice, "_type_", "") == ctypes.c_uint8: @@ -2076,37 +2100,37 @@ class SimpleServiceLifetime: def new_with(some_value: ctypes.POINTER(ctypes.c_uint32)) -> SimpleServiceLifetime: """""" ctx = ctypes.c_void_p() - c_lib.simple_service_lt_new_with(ctx, some_value) + c_lib.simple_service_lifetime_new_with(ctx, some_value) self = SimpleServiceLifetime(SimpleServiceLifetime.__api_lock, ctx) return self def __del__(self): - c_lib.simple_service_lt_destroy(self._ctx, ) + c_lib.simple_service_lifetime_destroy(self._ctx, ) def method_lt(self, slice: SliceBool | ctypes.Array[ctypes.c_uint8]): """""" if hasattr(slice, "_length_") and getattr(slice, "_type_", "") == ctypes.c_uint8: slice = SliceBool(data=ctypes.cast(slice, ctypes.POINTER(ctypes.c_uint8)), len=len(slice)) - return c_lib.simple_service_lt_method_lt(self._ctx, slice) + return c_lib.simple_service_lifetime_method_lt(self._ctx, slice) def method_lt2(self, slice: SliceBool | ctypes.Array[ctypes.c_uint8]): """""" if hasattr(slice, "_length_") and getattr(slice, "_type_", "") == ctypes.c_uint8: slice = SliceBool(data=ctypes.cast(slice, ctypes.POINTER(ctypes.c_uint8)), len=len(slice)) - return c_lib.simple_service_lt_method_lt2(self._ctx, slice) + return c_lib.simple_service_lifetime_method_lt2(self._ctx, slice) def return_string_accept_slice(self, anon1: Sliceu8 | ctypes.Array[ctypes.c_uint8]) -> bytes: """""" if hasattr(anon1, "_length_") and getattr(anon1, "_type_", "") == ctypes.c_uint8: anon1 = Sliceu8(data=ctypes.cast(anon1, ctypes.POINTER(ctypes.c_uint8)), len=len(anon1)) - rval = c_lib.simple_service_lt_return_string_accept_slice(self._ctx, anon1) + rval = c_lib.simple_service_lifetime_return_string_accept_slice(self._ctx, anon1) return ctypes.string_at(rval) def method_void_ffi_error(self, ): """""" - return c_lib.simple_service_lt_method_void_ffi_error(self._ctx, ) + return c_lib.simple_service_lifetime_method_void_ffi_error(self._ctx, ) diff --git a/backends/cpython/tests/output/tests.py b/backends/cpython/tests/output/tests.py index f3a82c30..95245c6c 100644 --- a/backends/cpython/tests/output/tests.py +++ b/backends/cpython/tests/output/tests.py @@ -70,13 +70,6 @@ def test_tuple(self): tupled = r.Tupled(x0=100) self.assertEqual(200, r.tupled(tupled).x0) - def test_complex(self): - vec = r.Vec3f32() - foreign = r.SomeForeignType() - - r.complex_args_1(vec, None) - self.assertEqual(None, r.complex_args_2(foreign)) - def test_callback(self): def my_callback(param): diff --git a/backends/csharp/tests/output/reference_project.md b/backends/csharp/tests/output/reference_project.md index 86a0ffa4..5f8cde49 100644 --- a/backends/csharp/tests/output/reference_project.md +++ b/backends/csharp/tests/output/reference_project.md @@ -29,7 +29,6 @@ Freestanding callables inside the module. - **[ref_mut_option](#ref_mut_option)** - - **[tupled](#tupled)** - - **[complex_args_1](#complex_args_1)** - - - **[complex_args_2](#complex_args_2)** - - **[callback](#callback)** - - **[generic_1a](#generic_1a)** - - **[generic_1b](#generic_1b)** - @@ -76,6 +75,7 @@ Freestanding callables inside the module. - **[pattern_callback_2](#pattern_callback_2)** - - **[pattern_callback_3](#pattern_callback_3)** - - **[pattern_callback_4](#pattern_callback_4)** - + - **[pattern_surrogates_1](#pattern_surrogates_1)** - ### Classes Methods operating on common state. @@ -87,6 +87,7 @@ Methods operating on common state. - **[MethodResult](#SimpleService.MethodResult)** - Methods returning a Result<(), _> are the default and do not - **[MethodValue](#SimpleService.MethodValue)** - - **[MethodVoid](#SimpleService.MethodVoid)** - This method should be documented. + - **[MethodVoid2](#SimpleService.MethodVoid2)** - - **[MethodMutSelf](#SimpleService.MethodMutSelf)** - - **[MethodMutSelfVoid](#SimpleService.MethodMutSelfVoid)** - Single line. - **[MethodMutSelfRef](#SimpleService.MethodMutSelfRef)** - @@ -117,15 +118,16 @@ Composite data used by functions and methods. - **[Aligned2](#Aligned2)** - - **[Array](#Array)** - - **[BooleanAlignment](#BooleanAlignment)** - + - **[Container](#Container)** - - **[DelegateCallbackMyCallbackContextual](#DelegateCallbackMyCallbackContextual)** - - **[ExtraTypef32](#ExtraTypef32)** - - **[Genericu32](#Genericu32)** - - **[Genericu8](#Genericu8)** - - **[Inner](#Inner)** - + - **[Local](#Local)** - - **[Packed1](#Packed1)** - - **[Packed2](#Packed2)** - - **[Phantomu8](#Phantomu8)** - - - **[SomeForeignType](#SomeForeignType)** - - **[StructDocumented](#StructDocumented)** - Documented struct. - **[StructRenamed](#StructRenamed)** - - **[Tupled](#Tupled)** - @@ -266,6 +268,23 @@ public partial struct BooleanAlignment + ### **Container** + + +#### Fields +- **foreign** - +#### Definition +```csharp +public partial struct Container +{ + public Local foreign; +} +``` + +--- + + + ### **DelegateCallbackMyCallbackContextual** @@ -353,18 +372,16 @@ public partial struct Inner - ### **Packed1** + ### **Local** #### Fields - **x** - -- **y** - #### Definition ```csharp -public partial struct Packed1 +public partial struct Local { - public byte x; - public ushort y; + uint x; } ``` @@ -372,7 +389,7 @@ public partial struct Packed1 - ### **Packed2** + ### **Packed1** #### Fields @@ -380,7 +397,7 @@ public partial struct Packed1 - **y** - #### Definition ```csharp -public partial struct Packed2 +public partial struct Packed1 { public byte x; public ushort y; @@ -391,16 +408,18 @@ public partial struct Packed2 - ### **Phantomu8** + ### **Packed2** #### Fields - **x** - +- **y** - #### Definition ```csharp -public partial struct Phantomu8 +public partial struct Packed2 { - public uint x; + public byte x; + public ushort y; } ``` @@ -408,14 +427,14 @@ public partial struct Phantomu8 - ### **SomeForeignType** + ### **Phantomu8** #### Fields - **x** - #### Definition ```csharp -public partial struct SomeForeignType +public partial struct Phantomu8 { public uint x; } @@ -1063,14 +1082,6 @@ public static void complex_args_1_checked(Vec3f32 a, ref Tupled b); --- -### **complex_args_2** -#### Definition -```csharp -public static extern IntPtr complex_args_2(SomeForeignType cmplx); -``` - ---- - ### **callback** #### Definition ```csharp @@ -1485,6 +1496,14 @@ public static extern uint pattern_callback_4(IntPtr callback, uint x); --- +### **pattern_surrogates_1** +#### Definition +```csharp +public static extern void pattern_surrogates_1(Local s, out Container c); +``` + +--- + # Classes ## **SimpleService** Some struct we want to expose as a class. @@ -1563,6 +1582,17 @@ public class SimpleService { --- +### **MethodVoid2** + +#### Definition +```csharp +public class SimpleService { + public void MethodVoid2(); +} +``` + +--- + ### **MethodMutSelf** #### Definition diff --git a/backends/csharp/tests/output_safe/Interop.cs.expected b/backends/csharp/tests/output_safe/Interop.cs.expected index a33c380b..3d20c96c 100644 --- a/backends/csharp/tests/output_safe/Interop.cs.expected +++ b/backends/csharp/tests/output_safe/Interop.cs.expected @@ -18,9 +18,9 @@ namespace My.Company static Interop() { var api_version = Interop.pattern_api_guard(); - if (api_version != 16012698009278542425ul) + if (api_version != 5646035154290656051ul) { - throw new TypeLoadException($"API reports hash {api_version} which differs from hash in bindings (16012698009278542425). You probably forgot to update / copy either the bindings or the library."); + throw new TypeLoadException($"API reports hash {api_version} which differs from hash in bindings (5646035154290656051). You probably forgot to update / copy either the bindings or the library."); } } @@ -121,9 +121,6 @@ namespace My.Company } } - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "complex_args_2")] - public static extern IntPtr complex_args_2(SomeForeignType cmplx); - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "callback")] public static extern byte callback(InteropDelegate_fn_u8_rval_u8 callback, byte value); @@ -404,6 +401,9 @@ namespace My.Company [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pattern_callback_4")] public static extern uint pattern_callback_4(MyCallbackNamespaced callback, uint x); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pattern_surrogates_1")] + public static extern void pattern_surrogates_1(Local s, out Container c); + /// Destroys the given instance. /// /// # Safety @@ -503,6 +503,9 @@ namespace My.Company [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_method_void")] public static extern void simple_service_method_void(IntPtr context); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_method_void2")] + public static extern void simple_service_method_void2(IntPtr context); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_method_mut_self")] public static extern byte simple_service_method_mut_self(IntPtr context, Sliceu8 slice); @@ -665,8 +668,8 @@ namespace My.Company /// /// The passed parameter MUST have been created with the corresponding init function; /// passing any other value results in undefined behavior. - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lt_destroy")] - public static extern FFIError simple_service_lt_destroy(ref IntPtr context); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lifetime_destroy")] + public static extern FFIError simple_service_lifetime_destroy(ref IntPtr context); /// Destroys the given instance. /// @@ -674,37 +677,37 @@ namespace My.Company /// /// The passed parameter MUST have been created with the corresponding init function; /// passing any other value results in undefined behavior. - public static void simple_service_lt_destroy_checked(ref IntPtr context) + public static void simple_service_lifetime_destroy_checked(ref IntPtr context) { - var rval = simple_service_lt_destroy(ref context);; + var rval = simple_service_lifetime_destroy(ref context);; if (rval != FFIError.Ok) { throw new InteropException(rval); } } - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lt_new_with")] - public static extern FFIError simple_service_lt_new_with(ref IntPtr context, ref uint some_value); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lifetime_new_with")] + public static extern FFIError simple_service_lifetime_new_with(ref IntPtr context, ref uint some_value); - public static void simple_service_lt_new_with_checked(ref IntPtr context, ref uint some_value) + public static void simple_service_lifetime_new_with_checked(ref IntPtr context, ref uint some_value) { - var rval = simple_service_lt_new_with(ref context, ref some_value);; + var rval = simple_service_lifetime_new_with(ref context, ref some_value);; if (rval != FFIError.Ok) { throw new InteropException(rval); } } - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lt_method_lt")] - public static extern void simple_service_lt_method_lt(IntPtr context, SliceBool slice); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lifetime_method_lt")] + public static extern void simple_service_lifetime_method_lt(IntPtr context, SliceBool slice); - public static void simple_service_lt_method_lt(IntPtr context, Bool[] slice) + public static void simple_service_lifetime_method_lt(IntPtr context, Bool[] slice) { var slice_pinned = GCHandle.Alloc(slice, GCHandleType.Pinned); var slice_slice = new SliceBool(slice_pinned, (ulong) slice.Length); try { - simple_service_lt_method_lt(context, slice_slice);; + simple_service_lifetime_method_lt(context, slice_slice);; } finally { @@ -712,16 +715,16 @@ namespace My.Company } } - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lt_method_lt2")] - public static extern void simple_service_lt_method_lt2(IntPtr context, SliceBool slice); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lifetime_method_lt2")] + public static extern void simple_service_lifetime_method_lt2(IntPtr context, SliceBool slice); - public static void simple_service_lt_method_lt2(IntPtr context, Bool[] slice) + public static void simple_service_lifetime_method_lt2(IntPtr context, Bool[] slice) { var slice_pinned = GCHandle.Alloc(slice, GCHandleType.Pinned); var slice_slice = new SliceBool(slice_pinned, (ulong) slice.Length); try { - simple_service_lt_method_lt2(context, slice_slice);; + simple_service_lifetime_method_lt2(context, slice_slice);; } finally { @@ -729,16 +732,16 @@ namespace My.Company } } - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lt_return_string_accept_slice")] - public static extern IntPtr simple_service_lt_return_string_accept_slice(IntPtr anon0, Sliceu8 anon1); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lifetime_return_string_accept_slice")] + public static extern IntPtr simple_service_lifetime_return_string_accept_slice(IntPtr anon0, Sliceu8 anon1); - public static string simple_service_lt_return_string_accept_slice(IntPtr anon0, byte[] anon1) + public static string simple_service_lifetime_return_string_accept_slice(IntPtr anon0, byte[] anon1) { var anon1_pinned = GCHandle.Alloc(anon1, GCHandleType.Pinned); var anon1_slice = new Sliceu8(anon1_pinned, (ulong) anon1.Length); try { - var s = simple_service_lt_return_string_accept_slice(anon0, anon1_slice);; + var s = simple_service_lifetime_return_string_accept_slice(anon0, anon1_slice);; return Marshal.PtrToStringAnsi(s); } finally @@ -747,12 +750,12 @@ namespace My.Company } } - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lt_method_void_ffi_error")] - public static extern FFIError simple_service_lt_method_void_ffi_error(IntPtr context); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lifetime_method_void_ffi_error")] + public static extern FFIError simple_service_lifetime_method_void_ffi_error(IntPtr context); - public static void simple_service_lt_method_void_ffi_error_checked(IntPtr context) + public static void simple_service_lifetime_method_void_ffi_error_checked(IntPtr context) { - var rval = simple_service_lt_method_void_ffi_error(context);; + var rval = simple_service_lifetime_method_void_ffi_error(context);; if (rval != FFIError.Ok) { throw new InteropException(rval); @@ -836,6 +839,13 @@ namespace My.Company public ulong datum; } + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public partial struct Container + { + public Local foreign; + } + [Serializable] [StructLayout(LayoutKind.Sequential)] public partial struct DelegateCallbackMyCallbackContextual @@ -872,6 +882,13 @@ namespace My.Company float x; } + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public partial struct Local + { + uint x; + } + [Serializable] [StructLayout(LayoutKind.Sequential, Pack = 1)] public partial struct Packed1 @@ -895,13 +912,6 @@ namespace My.Company public uint x; } - [Serializable] - [StructLayout(LayoutKind.Sequential)] - public partial struct SomeForeignType - { - public uint x; - } - /// Documented struct. [Serializable] [StructLayout(LayoutKind.Sequential)] @@ -1264,6 +1274,11 @@ namespace My.Company Interop.simple_service_method_void(_context); } + public void MethodVoid2() + { + Interop.simple_service_method_void2(_context); + } + public byte MethodMutSelf(Sliceu8 slice) { return Interop.simple_service_method_mut_self(_context, slice); @@ -1391,7 +1406,7 @@ namespace My.Company public static SimpleServiceLifetime NewWith(ref uint some_value) { var self = new SimpleServiceLifetime(); - var rval = Interop.simple_service_lt_new_with(ref self._context, ref some_value); + var rval = Interop.simple_service_lifetime_new_with(ref self._context, ref some_value); if (rval != FFIError.Ok) { throw new InteropException(rval); @@ -1401,7 +1416,7 @@ namespace My.Company public void Dispose() { - var rval = Interop.simple_service_lt_destroy(ref _context); + var rval = Interop.simple_service_lifetime_destroy(ref _context); if (rval != FFIError.Ok) { throw new InteropException(rval); @@ -1410,38 +1425,38 @@ namespace My.Company public void MethodLt(SliceBool slice) { - Interop.simple_service_lt_method_lt(_context, slice); + Interop.simple_service_lifetime_method_lt(_context, slice); } public void MethodLt(Bool[] slice) { - Interop.simple_service_lt_method_lt(_context, slice); + Interop.simple_service_lifetime_method_lt(_context, slice); } public void MethodLt2(SliceBool slice) { - Interop.simple_service_lt_method_lt2(_context, slice); + Interop.simple_service_lifetime_method_lt2(_context, slice); } public void MethodLt2(Bool[] slice) { - Interop.simple_service_lt_method_lt2(_context, slice); + Interop.simple_service_lifetime_method_lt2(_context, slice); } public string ReturnStringAcceptSlice(Sliceu8 anon1) { - var s = Interop.simple_service_lt_return_string_accept_slice(_context, anon1); + var s = Interop.simple_service_lifetime_return_string_accept_slice(_context, anon1); return Marshal.PtrToStringAnsi(s); } public string ReturnStringAcceptSlice(byte[] anon1) { - return Interop.simple_service_lt_return_string_accept_slice(_context, anon1); + return Interop.simple_service_lifetime_return_string_accept_slice(_context, anon1); } public void MethodVoidFfiError() { - var rval = Interop.simple_service_lt_method_void_ffi_error(_context); + var rval = Interop.simple_service_lifetime_method_void_ffi_error(_context); if (rval != FFIError.Ok) { throw new InteropException(rval); diff --git a/backends/csharp/tests/output_unity/Assets/Interop.cs.expected b/backends/csharp/tests/output_unity/Assets/Interop.cs.expected index 05aa6f52..31302b1e 100644 --- a/backends/csharp/tests/output_unity/Assets/Interop.cs.expected +++ b/backends/csharp/tests/output_unity/Assets/Interop.cs.expected @@ -23,9 +23,9 @@ namespace My.Company static Interop() { var api_version = Interop.pattern_api_guard(); - if (api_version != 16012698009278542425ul) + if (api_version != 5646035154290656051ul) { - throw new TypeLoadException($"API reports hash {api_version} which differs from hash in bindings (16012698009278542425). You probably forgot to update / copy either the bindings or the library."); + throw new TypeLoadException($"API reports hash {api_version} which differs from hash in bindings (5646035154290656051). You probably forgot to update / copy either the bindings or the library."); } } @@ -126,9 +126,6 @@ namespace My.Company } } - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "complex_args_2")] - public static extern IntPtr complex_args_2(SomeForeignType cmplx); - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "callback")] public static extern byte callback(InteropDelegate_fn_u8_rval_u8 callback, byte value); @@ -497,6 +494,9 @@ namespace My.Company public static extern uint pattern_callback_4(IntPtr callback, uint x); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pattern_surrogates_1")] + public static extern void pattern_surrogates_1(Local s, out Container c); + /// Destroys the given instance. /// /// # Safety @@ -596,6 +596,9 @@ namespace My.Company [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_method_void")] public static extern void simple_service_method_void(IntPtr context); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_method_void2")] + public static extern void simple_service_method_void2(IntPtr context); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_method_mut_self")] public static extern byte simple_service_method_mut_self(IntPtr context, Sliceu8 slice); @@ -809,8 +812,8 @@ namespace My.Company /// /// The passed parameter MUST have been created with the corresponding init function; /// passing any other value results in undefined behavior. - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lt_destroy")] - public static extern FFIError simple_service_lt_destroy(ref IntPtr context); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lifetime_destroy")] + public static extern FFIError simple_service_lifetime_destroy(ref IntPtr context); /// Destroys the given instance. /// @@ -818,104 +821,104 @@ namespace My.Company /// /// The passed parameter MUST have been created with the corresponding init function; /// passing any other value results in undefined behavior. - public static void simple_service_lt_destroy_checked(ref IntPtr context) + public static void simple_service_lifetime_destroy_checked(ref IntPtr context) { - var rval = simple_service_lt_destroy(ref context);; + var rval = simple_service_lifetime_destroy(ref context);; if (rval != FFIError.Ok) { throw new InteropException(rval); } } - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lt_new_with")] - public static extern FFIError simple_service_lt_new_with(ref IntPtr context, ref uint some_value); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lifetime_new_with")] + public static extern FFIError simple_service_lifetime_new_with(ref IntPtr context, ref uint some_value); - public static void simple_service_lt_new_with_checked(ref IntPtr context, ref uint some_value) + public static void simple_service_lifetime_new_with_checked(ref IntPtr context, ref uint some_value) { - var rval = simple_service_lt_new_with(ref context, ref some_value);; + var rval = simple_service_lifetime_new_with(ref context, ref some_value);; if (rval != FFIError.Ok) { throw new InteropException(rval); } } - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lt_method_lt")] - public static extern void simple_service_lt_method_lt(IntPtr context, SliceBool slice); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lifetime_method_lt")] + public static extern void simple_service_lifetime_method_lt(IntPtr context, SliceBool slice); - public static void simple_service_lt_method_lt(IntPtr context, Bool[] slice) + public static void simple_service_lifetime_method_lt(IntPtr context, Bool[] slice) { unsafe { fixed (void* ptr_slice = slice) { var slice_slice = new SliceBool(new IntPtr(ptr_slice), (ulong) slice.Length); - simple_service_lt_method_lt(context, slice_slice);; + simple_service_lifetime_method_lt(context, slice_slice);; } } } #if UNITY_2018_1_OR_NEWER - public static void simple_service_lt_method_lt(IntPtr context, NativeArray slice) + public static void simple_service_lifetime_method_lt(IntPtr context, NativeArray slice) { var slice_slice = new SliceBool(slice); - simple_service_lt_method_lt(context, slice_slice);; + simple_service_lifetime_method_lt(context, slice_slice);; } #endif - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lt_method_lt2")] - public static extern void simple_service_lt_method_lt2(IntPtr context, SliceBool slice); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lifetime_method_lt2")] + public static extern void simple_service_lifetime_method_lt2(IntPtr context, SliceBool slice); - public static void simple_service_lt_method_lt2(IntPtr context, Bool[] slice) + public static void simple_service_lifetime_method_lt2(IntPtr context, Bool[] slice) { unsafe { fixed (void* ptr_slice = slice) { var slice_slice = new SliceBool(new IntPtr(ptr_slice), (ulong) slice.Length); - simple_service_lt_method_lt2(context, slice_slice);; + simple_service_lifetime_method_lt2(context, slice_slice);; } } } #if UNITY_2018_1_OR_NEWER - public static void simple_service_lt_method_lt2(IntPtr context, NativeArray slice) + public static void simple_service_lifetime_method_lt2(IntPtr context, NativeArray slice) { var slice_slice = new SliceBool(slice); - simple_service_lt_method_lt2(context, slice_slice);; + simple_service_lifetime_method_lt2(context, slice_slice);; } #endif - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lt_return_string_accept_slice")] - public static extern IntPtr simple_service_lt_return_string_accept_slice(IntPtr anon0, Sliceu8 anon1); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lifetime_return_string_accept_slice")] + public static extern IntPtr simple_service_lifetime_return_string_accept_slice(IntPtr anon0, Sliceu8 anon1); - public static string simple_service_lt_return_string_accept_slice(IntPtr anon0, byte[] anon1) + public static string simple_service_lifetime_return_string_accept_slice(IntPtr anon0, byte[] anon1) { unsafe { fixed (void* ptr_anon1 = anon1) { var anon1_slice = new Sliceu8(new IntPtr(ptr_anon1), (ulong) anon1.Length); - var s = simple_service_lt_return_string_accept_slice(anon0, anon1_slice);; + var s = simple_service_lifetime_return_string_accept_slice(anon0, anon1_slice);; return Marshal.PtrToStringAnsi(s); } } } #if UNITY_2018_1_OR_NEWER - public static string simple_service_lt_return_string_accept_slice(IntPtr anon0, NativeArray anon1) + public static string simple_service_lifetime_return_string_accept_slice(IntPtr anon0, NativeArray anon1) { var anon1_slice = new Sliceu8(anon1); - var s = simple_service_lt_return_string_accept_slice(anon0, anon1_slice);; + var s = simple_service_lifetime_return_string_accept_slice(anon0, anon1_slice);; return Marshal.PtrToStringAnsi(s); } #endif - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lt_method_void_ffi_error")] - public static extern FFIError simple_service_lt_method_void_ffi_error(IntPtr context); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lifetime_method_void_ffi_error")] + public static extern FFIError simple_service_lifetime_method_void_ffi_error(IntPtr context); - public static void simple_service_lt_method_void_ffi_error_checked(IntPtr context) + public static void simple_service_lifetime_method_void_ffi_error_checked(IntPtr context) { - var rval = simple_service_lt_method_void_ffi_error(context);; + var rval = simple_service_lifetime_method_void_ffi_error(context);; if (rval != FFIError.Ok) { throw new InteropException(rval); @@ -999,6 +1002,13 @@ namespace My.Company public ulong datum; } + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public partial struct Container + { + public Local foreign; + } + [Serializable] [StructLayout(LayoutKind.Sequential)] public partial struct DelegateCallbackMyCallbackContextual @@ -1044,6 +1054,13 @@ namespace My.Company float x; } + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public partial struct Local + { + uint x; + } + [Serializable] [StructLayout(LayoutKind.Sequential, Pack = 1)] public partial struct Packed1 @@ -1067,13 +1084,6 @@ namespace My.Company public uint x; } - [Serializable] - [StructLayout(LayoutKind.Sequential)] - public partial struct SomeForeignType - { - public uint x; - } - /// Documented struct. [Serializable] [StructLayout(LayoutKind.Sequential)] @@ -1504,6 +1514,11 @@ namespace My.Company Interop.simple_service_method_void(_context); } + public void MethodVoid2() + { + Interop.simple_service_method_void2(_context); + } + public byte MethodMutSelf(Sliceu8 slice) { return Interop.simple_service_method_mut_self(_context, slice); @@ -1681,7 +1696,7 @@ namespace My.Company public static SimpleServiceLifetime NewWith(ref uint some_value) { var self = new SimpleServiceLifetime(); - var rval = Interop.simple_service_lt_new_with(ref self._context, ref some_value); + var rval = Interop.simple_service_lifetime_new_with(ref self._context, ref some_value); if (rval != FFIError.Ok) { throw new InteropException(rval); @@ -1691,7 +1706,7 @@ namespace My.Company public void Dispose() { - var rval = Interop.simple_service_lt_destroy(ref _context); + var rval = Interop.simple_service_lifetime_destroy(ref _context); if (rval != FFIError.Ok) { throw new InteropException(rval); @@ -1700,59 +1715,59 @@ namespace My.Company public void MethodLt(SliceBool slice) { - Interop.simple_service_lt_method_lt(_context, slice); + Interop.simple_service_lifetime_method_lt(_context, slice); } public void MethodLt(Bool[] slice) { - Interop.simple_service_lt_method_lt(_context, slice); + Interop.simple_service_lifetime_method_lt(_context, slice); } #if UNITY_2018_1_OR_NEWER public void MethodLt(NativeArray slice) { - Interop.simple_service_lt_method_lt(_context, slice); + Interop.simple_service_lifetime_method_lt(_context, slice); } #endif public void MethodLt2(SliceBool slice) { - Interop.simple_service_lt_method_lt2(_context, slice); + Interop.simple_service_lifetime_method_lt2(_context, slice); } public void MethodLt2(Bool[] slice) { - Interop.simple_service_lt_method_lt2(_context, slice); + Interop.simple_service_lifetime_method_lt2(_context, slice); } #if UNITY_2018_1_OR_NEWER public void MethodLt2(NativeArray slice) { - Interop.simple_service_lt_method_lt2(_context, slice); + Interop.simple_service_lifetime_method_lt2(_context, slice); } #endif public string ReturnStringAcceptSlice(Sliceu8 anon1) { - var s = Interop.simple_service_lt_return_string_accept_slice(_context, anon1); + var s = Interop.simple_service_lifetime_return_string_accept_slice(_context, anon1); return Marshal.PtrToStringAnsi(s); } public string ReturnStringAcceptSlice(byte[] anon1) { - return Interop.simple_service_lt_return_string_accept_slice(_context, anon1); + return Interop.simple_service_lifetime_return_string_accept_slice(_context, anon1); } #if UNITY_2018_1_OR_NEWER public string ReturnStringAcceptSlice(NativeArray anon1) { - return Interop.simple_service_lt_return_string_accept_slice(_context, anon1); + return Interop.simple_service_lifetime_return_string_accept_slice(_context, anon1); } #endif public void MethodVoidFfiError() { - var rval = Interop.simple_service_lt_method_void_ffi_error(_context); + var rval = Interop.simple_service_lifetime_method_void_ffi_error(_context); if (rval != FFIError.Ok) { throw new InteropException(rval); diff --git a/backends/csharp/tests/output_unsafe/Interop.cs.expected b/backends/csharp/tests/output_unsafe/Interop.cs.expected index 7e77f964..2b699cbb 100644 --- a/backends/csharp/tests/output_unsafe/Interop.cs.expected +++ b/backends/csharp/tests/output_unsafe/Interop.cs.expected @@ -23,9 +23,9 @@ namespace My.Company static Interop() { var api_version = Interop.pattern_api_guard(); - if (api_version != 16012698009278542425ul) + if (api_version != 5646035154290656051ul) { - throw new TypeLoadException($"API reports hash {api_version} which differs from hash in bindings (16012698009278542425). You probably forgot to update / copy either the bindings or the library."); + throw new TypeLoadException($"API reports hash {api_version} which differs from hash in bindings (5646035154290656051). You probably forgot to update / copy either the bindings or the library."); } } @@ -126,9 +126,6 @@ namespace My.Company } } - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "complex_args_2")] - public static extern IntPtr complex_args_2(SomeForeignType cmplx); - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "callback")] public static extern byte callback(InteropDelegate_fn_u8_rval_u8 callback, byte value); @@ -497,6 +494,9 @@ namespace My.Company public static extern uint pattern_callback_4(IntPtr callback, uint x); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "pattern_surrogates_1")] + public static extern void pattern_surrogates_1(Local s, out Container c); + /// Destroys the given instance. /// /// # Safety @@ -596,6 +596,9 @@ namespace My.Company [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_method_void")] public static extern void simple_service_method_void(IntPtr context); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_method_void2")] + public static extern void simple_service_method_void2(IntPtr context); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_method_mut_self")] public static extern byte simple_service_method_mut_self(IntPtr context, Sliceu8 slice); @@ -809,8 +812,8 @@ namespace My.Company /// /// The passed parameter MUST have been created with the corresponding init function; /// passing any other value results in undefined behavior. - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lt_destroy")] - public static extern FFIError simple_service_lt_destroy(ref IntPtr context); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lifetime_destroy")] + public static extern FFIError simple_service_lifetime_destroy(ref IntPtr context); /// Destroys the given instance. /// @@ -818,104 +821,104 @@ namespace My.Company /// /// The passed parameter MUST have been created with the corresponding init function; /// passing any other value results in undefined behavior. - public static void simple_service_lt_destroy_checked(ref IntPtr context) + public static void simple_service_lifetime_destroy_checked(ref IntPtr context) { - var rval = simple_service_lt_destroy(ref context);; + var rval = simple_service_lifetime_destroy(ref context);; if (rval != FFIError.Ok) { throw new InteropException(rval); } } - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lt_new_with")] - public static extern FFIError simple_service_lt_new_with(ref IntPtr context, ref uint some_value); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lifetime_new_with")] + public static extern FFIError simple_service_lifetime_new_with(ref IntPtr context, ref uint some_value); - public static void simple_service_lt_new_with_checked(ref IntPtr context, ref uint some_value) + public static void simple_service_lifetime_new_with_checked(ref IntPtr context, ref uint some_value) { - var rval = simple_service_lt_new_with(ref context, ref some_value);; + var rval = simple_service_lifetime_new_with(ref context, ref some_value);; if (rval != FFIError.Ok) { throw new InteropException(rval); } } - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lt_method_lt")] - public static extern void simple_service_lt_method_lt(IntPtr context, SliceBool slice); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lifetime_method_lt")] + public static extern void simple_service_lifetime_method_lt(IntPtr context, SliceBool slice); - public static void simple_service_lt_method_lt(IntPtr context, System.ReadOnlySpan slice) + public static void simple_service_lifetime_method_lt(IntPtr context, System.ReadOnlySpan slice) { unsafe { fixed (void* ptr_slice = slice) { var slice_slice = new SliceBool(new IntPtr(ptr_slice), (ulong) slice.Length); - simple_service_lt_method_lt(context, slice_slice);; + simple_service_lifetime_method_lt(context, slice_slice);; } } } #if UNITY_2018_1_OR_NEWER - public static void simple_service_lt_method_lt(IntPtr context, NativeArray slice) + public static void simple_service_lifetime_method_lt(IntPtr context, NativeArray slice) { var slice_slice = new SliceBool(slice); - simple_service_lt_method_lt(context, slice_slice);; + simple_service_lifetime_method_lt(context, slice_slice);; } #endif - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lt_method_lt2")] - public static extern void simple_service_lt_method_lt2(IntPtr context, SliceBool slice); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lifetime_method_lt2")] + public static extern void simple_service_lifetime_method_lt2(IntPtr context, SliceBool slice); - public static void simple_service_lt_method_lt2(IntPtr context, System.ReadOnlySpan slice) + public static void simple_service_lifetime_method_lt2(IntPtr context, System.ReadOnlySpan slice) { unsafe { fixed (void* ptr_slice = slice) { var slice_slice = new SliceBool(new IntPtr(ptr_slice), (ulong) slice.Length); - simple_service_lt_method_lt2(context, slice_slice);; + simple_service_lifetime_method_lt2(context, slice_slice);; } } } #if UNITY_2018_1_OR_NEWER - public static void simple_service_lt_method_lt2(IntPtr context, NativeArray slice) + public static void simple_service_lifetime_method_lt2(IntPtr context, NativeArray slice) { var slice_slice = new SliceBool(slice); - simple_service_lt_method_lt2(context, slice_slice);; + simple_service_lifetime_method_lt2(context, slice_slice);; } #endif - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lt_return_string_accept_slice")] - public static extern IntPtr simple_service_lt_return_string_accept_slice(IntPtr anon0, Sliceu8 anon1); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lifetime_return_string_accept_slice")] + public static extern IntPtr simple_service_lifetime_return_string_accept_slice(IntPtr anon0, Sliceu8 anon1); - public static string simple_service_lt_return_string_accept_slice(IntPtr anon0, System.ReadOnlySpan anon1) + public static string simple_service_lifetime_return_string_accept_slice(IntPtr anon0, System.ReadOnlySpan anon1) { unsafe { fixed (void* ptr_anon1 = anon1) { var anon1_slice = new Sliceu8(new IntPtr(ptr_anon1), (ulong) anon1.Length); - var s = simple_service_lt_return_string_accept_slice(anon0, anon1_slice);; + var s = simple_service_lifetime_return_string_accept_slice(anon0, anon1_slice);; return Marshal.PtrToStringAnsi(s); } } } #if UNITY_2018_1_OR_NEWER - public static string simple_service_lt_return_string_accept_slice(IntPtr anon0, NativeArray anon1) + public static string simple_service_lifetime_return_string_accept_slice(IntPtr anon0, NativeArray anon1) { var anon1_slice = new Sliceu8(anon1); - var s = simple_service_lt_return_string_accept_slice(anon0, anon1_slice);; + var s = simple_service_lifetime_return_string_accept_slice(anon0, anon1_slice);; return Marshal.PtrToStringAnsi(s); } #endif - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lt_method_void_ffi_error")] - public static extern FFIError simple_service_lt_method_void_ffi_error(IntPtr context); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "simple_service_lifetime_method_void_ffi_error")] + public static extern FFIError simple_service_lifetime_method_void_ffi_error(IntPtr context); - public static void simple_service_lt_method_void_ffi_error_checked(IntPtr context) + public static void simple_service_lifetime_method_void_ffi_error_checked(IntPtr context) { - var rval = simple_service_lt_method_void_ffi_error(context);; + var rval = simple_service_lifetime_method_void_ffi_error(context);; if (rval != FFIError.Ok) { throw new InteropException(rval); @@ -999,6 +1002,13 @@ namespace My.Company public ulong datum; } + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public partial struct Container + { + public Local foreign; + } + [Serializable] [StructLayout(LayoutKind.Sequential)] public partial struct DelegateCallbackMyCallbackContextual @@ -1044,6 +1054,13 @@ namespace My.Company float x; } + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public partial struct Local + { + uint x; + } + [Serializable] [StructLayout(LayoutKind.Sequential, Pack = 1)] public partial struct Packed1 @@ -1067,13 +1084,6 @@ namespace My.Company public uint x; } - [Serializable] - [StructLayout(LayoutKind.Sequential)] - public partial struct SomeForeignType - { - public uint x; - } - /// Documented struct. [Serializable] [StructLayout(LayoutKind.Sequential)] @@ -1504,6 +1514,11 @@ namespace My.Company Interop.simple_service_method_void(_context); } + public void MethodVoid2() + { + Interop.simple_service_method_void2(_context); + } + public byte MethodMutSelf(Sliceu8 slice) { return Interop.simple_service_method_mut_self(_context, slice); @@ -1681,7 +1696,7 @@ namespace My.Company public static SimpleServiceLifetime NewWith(ref uint some_value) { var self = new SimpleServiceLifetime(); - var rval = Interop.simple_service_lt_new_with(ref self._context, ref some_value); + var rval = Interop.simple_service_lifetime_new_with(ref self._context, ref some_value); if (rval != FFIError.Ok) { throw new InteropException(rval); @@ -1691,7 +1706,7 @@ namespace My.Company public void Dispose() { - var rval = Interop.simple_service_lt_destroy(ref _context); + var rval = Interop.simple_service_lifetime_destroy(ref _context); if (rval != FFIError.Ok) { throw new InteropException(rval); @@ -1700,59 +1715,59 @@ namespace My.Company public void MethodLt(SliceBool slice) { - Interop.simple_service_lt_method_lt(_context, slice); + Interop.simple_service_lifetime_method_lt(_context, slice); } public void MethodLt(System.ReadOnlySpan slice) { - Interop.simple_service_lt_method_lt(_context, slice); + Interop.simple_service_lifetime_method_lt(_context, slice); } #if UNITY_2018_1_OR_NEWER public void MethodLt(NativeArray slice) { - Interop.simple_service_lt_method_lt(_context, slice); + Interop.simple_service_lifetime_method_lt(_context, slice); } #endif public void MethodLt2(SliceBool slice) { - Interop.simple_service_lt_method_lt2(_context, slice); + Interop.simple_service_lifetime_method_lt2(_context, slice); } public void MethodLt2(System.ReadOnlySpan slice) { - Interop.simple_service_lt_method_lt2(_context, slice); + Interop.simple_service_lifetime_method_lt2(_context, slice); } #if UNITY_2018_1_OR_NEWER public void MethodLt2(NativeArray slice) { - Interop.simple_service_lt_method_lt2(_context, slice); + Interop.simple_service_lifetime_method_lt2(_context, slice); } #endif public string ReturnStringAcceptSlice(Sliceu8 anon1) { - var s = Interop.simple_service_lt_return_string_accept_slice(_context, anon1); + var s = Interop.simple_service_lifetime_return_string_accept_slice(_context, anon1); return Marshal.PtrToStringAnsi(s); } public string ReturnStringAcceptSlice(System.ReadOnlySpan anon1) { - return Interop.simple_service_lt_return_string_accept_slice(_context, anon1); + return Interop.simple_service_lifetime_return_string_accept_slice(_context, anon1); } #if UNITY_2018_1_OR_NEWER public string ReturnStringAcceptSlice(NativeArray anon1) { - return Interop.simple_service_lt_return_string_accept_slice(_context, anon1); + return Interop.simple_service_lifetime_return_string_accept_slice(_context, anon1); } #endif public void MethodVoidFfiError() { - var rval = Interop.simple_service_lt_method_void_ffi_error(_context); + var rval = Interop.simple_service_lifetime_method_void_ffi_error(_context); if (rval != FFIError.Ok) { throw new InteropException(rval); diff --git a/core/README.md b/core/README.md index f8ef0d3f..838863db 100644 --- a/core/README.md +++ b/core/README.md @@ -75,6 +75,17 @@ If you want to ... - **understand what's possible**, see the [**reference project**](https://github.com/ralfbiedert/interoptopus/tree/master/reference_project/src), - **support a new language**, [**copy the C backend**](https://github.com/ralfbiedert/interoptopus/tree/master/backends/c). +For a "production-ready" project you generally want to follow these 3 steps: + +- Write a Rust crate, such as `my_library` and solve your problem in a Rust-idiomatic way. +- Add a crate `my_library_ffi`, which holds interop types and C-style definitions for your library similar to the examples above. + - For example, for a `my_library::Container` you probably want to a FFI sibling `my_library_ffi::Container`. + - While this might seem like extra work (it is) it gives you fine-grained access to what you expose. Once your project reaches a certain size, this will be invaluable. + - You'll probably want to add some conversion methods between your types, and FFI exposed methods to allow your interop users + to interact with these types in a controlled fashion. +- Add a crate `my_library_interop`. Inside there reference the backends for which you want to create interop definitions + (either in some unit tests, a `main.rs` or `build.rs`), and invoke the interop generation when needed; again, compare examples above. + ### Supported Rust Constructs @@ -125,6 +136,7 @@ Gated behind **feature flags**, these enable: ### Changelog +- **v0.15** - Updated to syn2, bug fixes. - **v0.14** - Better inventory UX. - **v0.13** - Python backend uses `ctypes` now. - **v0.12** - Better compat using `#[ffi_service_method]`.