如何从 Swift FFI 传递参数到函数?
How to pass out parameter to function from Swift FFI?
假设我有一个用 Rust 定义的函数,它看起来像这样:
#[no_mangle]
pub unsafe extern "C" fn do_something(
my_value: *mut MyStruct,
some_param: c_uint,
content: *mut *mut u8,
length: *mut c_uint,
capacity: *mut c_uint,
) -> *mut MyStruct {
// Do something and obtain an `ffi_result`...
let heap_data = ffi_result.as_mut_ptr();
// These values are passed as "out" parameters.
*length = ffi_result.len() as c_uint;
*capacity = ffi_result.capacity() as c_uint;
*content = heap_data;
// We intentionally "leak" this data to the heap.
// The caller is responsible for cleaning it up by calling another function.
std::mem::forget(ffi_result);
std::boxed::Box::into_raw(value_of_type_my_struct)
}
它接受一个指向结构的指针、一个简单的整数参数、几个 out
稍后可用于创建数组的参数,它 returns 一个指向结构的指针。
现在我将 rust 库编译成目标 aarch64-apple-ios
的静态库。我设置了一个 XCode 项目,按照 here 的解释将静态库添加为依赖项,并在其中导入以下头文件
“Objective-C Bridging Header”
#ifndef libmy_project_h
#define libmy_project_h
#include <stdint.h>
struct myStruct;
struct myStruct *do_something(struct myStruct *state, int someParam, char **content, int *length, int *capacity);
#endif
到目前为止,一切似乎都运行良好,我已经成功地将此过程用于一大堆其他功能。但是在这种特殊情况下,我不知道如何从 swift 调用这个函数。我需要从 swift 调用该函数并将 content
、length
和 capacity
作为 out 参数 传递,以便我以后可以使用在 Swift 中创建数组的指针,例如 .
这是我到目前为止尝试过的:
var content = UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>(UnsafeMutablePointer(bitPattern: 0))
var length = UnsafeMutablePointer<Int32>(bitPattern: 0)
var capacity = UnsafeMutablePointer<Int32>(bitPattern: 0)
let my_struct = do_something(my_struct, Int32(some_param), content, length, capacity)
let buffer = UnsafeRawBufferPointer(start: content?.pointee, count: Int(length!.pointee))
var data = Array(repeating: UInt8(0), count: Int(length!.pointee))
data.withUnsafeMutableBytes { arrayPtr in
arrayPtr.copyBytes(from: buffer)
}
但是现在当我执行这个 swift 片段时,我得到一个 EXC_BAD_ACCESS
错误,我认为这是因为我手动创建的指针不属于 space 的地址我的应用程序。如何创建可用作 out
参数的指针?
P.S。这里的参考是 C# 中的相同互操作代码:
[DllImport("my_dll")]
private static extern IntPtr do_something(IntPtr state, uint someParam, out IntPtr content, out uint length, out uint capacity);
可以这样调用:
IntPtr contentPointer;
uint length, capacity;
IntPtr my_struct = do_something(state, myParam, out contentPointer, out length, out capacity);
byte[] rawContent = new byte[length];
Marshal.Copy(contentPointer, rawContent, 0, (int)length);
// Free the data owned by rust with another FFI call:
free_do_something_result(contentPointer, length, capacity);
var length = UnsafeMutablePointer<Int32>(bitPattern: 0)
您需要为您的out-parameters传递存储。这是定义一个空指针。当 Rust 尝试将结果写入地址 0 时,它会崩溃,因为您无权在那里写入。
不是创建两层指针,而是创建一个你想要的类型的值,然后传递那个值的地址(&
);这将自动添加额外的指针层。
// Create storage
var content: UnsafeMutablePointer<CChar>? // Could be NULL, so Optional
var length: Int32 = 0
var capacity: Int32 = 0
// Pass as references
do_something(&content, &length, &capacity)
// Copy the data
let data = Array(UnsafeRawBufferPointer(start: content, count: Int(length)))
content
这里还是一个指针,因为更新的东西是一个指针。你不是在为内容提供存储,Rust 是。但是你做需要为指针提供存储(这就是它所做的)。
我无法编译你的代码,因为它遗漏了很多(MCVE 在这里会好得多),所以我无法测试这是否完全符合你的意思,但它应该是关闭。
在您的示例中,您正在泄漏内存,但由于您的 C# 调用 free_do_something_result
(我假设它会清理它),我假设您实际上在 Swift.
假设我有一个用 Rust 定义的函数,它看起来像这样:
#[no_mangle]
pub unsafe extern "C" fn do_something(
my_value: *mut MyStruct,
some_param: c_uint,
content: *mut *mut u8,
length: *mut c_uint,
capacity: *mut c_uint,
) -> *mut MyStruct {
// Do something and obtain an `ffi_result`...
let heap_data = ffi_result.as_mut_ptr();
// These values are passed as "out" parameters.
*length = ffi_result.len() as c_uint;
*capacity = ffi_result.capacity() as c_uint;
*content = heap_data;
// We intentionally "leak" this data to the heap.
// The caller is responsible for cleaning it up by calling another function.
std::mem::forget(ffi_result);
std::boxed::Box::into_raw(value_of_type_my_struct)
}
它接受一个指向结构的指针、一个简单的整数参数、几个 out
稍后可用于创建数组的参数,它 returns 一个指向结构的指针。
现在我将 rust 库编译成目标 aarch64-apple-ios
的静态库。我设置了一个 XCode 项目,按照 here 的解释将静态库添加为依赖项,并在其中导入以下头文件
#ifndef libmy_project_h
#define libmy_project_h
#include <stdint.h>
struct myStruct;
struct myStruct *do_something(struct myStruct *state, int someParam, char **content, int *length, int *capacity);
#endif
到目前为止,一切似乎都运行良好,我已经成功地将此过程用于一大堆其他功能。但是在这种特殊情况下,我不知道如何从 swift 调用这个函数。我需要从 swift 调用该函数并将 content
、length
和 capacity
作为 out 参数 传递,以便我以后可以使用在 Swift 中创建数组的指针,例如
这是我到目前为止尝试过的:
var content = UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>(UnsafeMutablePointer(bitPattern: 0))
var length = UnsafeMutablePointer<Int32>(bitPattern: 0)
var capacity = UnsafeMutablePointer<Int32>(bitPattern: 0)
let my_struct = do_something(my_struct, Int32(some_param), content, length, capacity)
let buffer = UnsafeRawBufferPointer(start: content?.pointee, count: Int(length!.pointee))
var data = Array(repeating: UInt8(0), count: Int(length!.pointee))
data.withUnsafeMutableBytes { arrayPtr in
arrayPtr.copyBytes(from: buffer)
}
但是现在当我执行这个 swift 片段时,我得到一个 EXC_BAD_ACCESS
错误,我认为这是因为我手动创建的指针不属于 space 的地址我的应用程序。如何创建可用作 out
参数的指针?
P.S。这里的参考是 C# 中的相同互操作代码:
[DllImport("my_dll")]
private static extern IntPtr do_something(IntPtr state, uint someParam, out IntPtr content, out uint length, out uint capacity);
可以这样调用:
IntPtr contentPointer;
uint length, capacity;
IntPtr my_struct = do_something(state, myParam, out contentPointer, out length, out capacity);
byte[] rawContent = new byte[length];
Marshal.Copy(contentPointer, rawContent, 0, (int)length);
// Free the data owned by rust with another FFI call:
free_do_something_result(contentPointer, length, capacity);
var length = UnsafeMutablePointer<Int32>(bitPattern: 0)
您需要为您的out-parameters传递存储。这是定义一个空指针。当 Rust 尝试将结果写入地址 0 时,它会崩溃,因为您无权在那里写入。
不是创建两层指针,而是创建一个你想要的类型的值,然后传递那个值的地址(&
);这将自动添加额外的指针层。
// Create storage
var content: UnsafeMutablePointer<CChar>? // Could be NULL, so Optional
var length: Int32 = 0
var capacity: Int32 = 0
// Pass as references
do_something(&content, &length, &capacity)
// Copy the data
let data = Array(UnsafeRawBufferPointer(start: content, count: Int(length)))
content
这里还是一个指针,因为更新的东西是一个指针。你不是在为内容提供存储,Rust 是。但是你做需要为指针提供存储(这就是它所做的)。
我无法编译你的代码,因为它遗漏了很多(MCVE 在这里会好得多),所以我无法测试这是否完全符合你的意思,但它应该是关闭。
在您的示例中,您正在泄漏内存,但由于您的 C# 调用 free_do_something_result
(我假设它会清理它),我假设您实际上在 Swift.