[英]Calling variadic C function from Swift with pointers
我試圖弄清楚如何調用從 Swift 寫入指針的可變參數 C 函數,例如vsscanf
,但我不明白如何實際構造指向 Swift 變量的指針列表。
我認為如果我有一個字符串,我可以獲得一個UnsafePointer<CChar>
並對其調用vsscanf
,但是......我如何告訴它實際寫入數據的位置? 如何構造CVaListPointer
以傳遞給vsscanf
?
var a: Int
var b: Float
"(5, 3.14)".withCString{buffer in
let r = vsscanf(buffer, "(%d, %f)", /* how do I put a and b here? */)
}
基本上,做與此處相同的事情 (C):
#include <stdio.h>
#include <stdarg.h>
int parse(const char *buffer, char *format, ...)
{
va_list args;
va_start(args, format);
int result = vsscanf(buffer, format, args);
va_end(args);
return result;
}
int main(int argc, char const *argv[])
{
int a;
float b;
char s[] = "(5, 3.14)";
int r = parse(s, "(%d, %f)", &a, &b);
printf("a: %d, b: %f\n", a, b);
// "a: 5, b: 3.140000"
return 0;
}
更新:多虧了到目前為止的答案,我覺得我更好地理解了它是如何工作的,但我仍在努力解決的問題是以更簡單的方式填充CVaListPointer
。 下面是從字符串中解析 Double 三元組的示例:
func parseVec3(s: String) -> (Double, Double, Double)? {
var x: Double = 0
var y: Double = 0
var z: Double = 0
let r = s.withCString { buffer in
withUnsafeMutablePointer(to: &x) { ptrx in
withUnsafeMutablePointer(to: &y) { ptry in
withUnsafeMutablePointer(to: &z) { ptrz in
withVaList([ptrx, ptry, ptrz]) { va in
return vsscanf(buffer, "(%lf %lf %lf)", va)
}
}
}
}
}
return r == 3 ? (x, y, z) : nil
}
if let v = parseVec3(s: "(1 2 3)") {
print(v)
}
現在,這確實有效。 但我的問題是我正在解析一個文件,其中大部分行(成千上萬行)是六組數字的三元組。 withUnsafeMutablePointer
的高聳結構看起來非常荒謬。 我確定我可以使用更多 Swift 原生方法(或只是正則表達式)來解析它,但我希望只使用vsscanf
,因為在 C 中解析這個文件非常簡單:
int main(int argc, char const *argv[])
{
char s[] = "(1 2 3) (5 9 1) (0 5 8)";
Vec3 a, b, c = {0, 0, 0};
sscanf(s,
"(%f %f %f) (%f %f %f) (%f %f %f)",
&(a.x), &(a.y), &(a.z),
&(b.x), &(b.y), &(b.z),
&(c.x), &(c.y), &(c.z)
);
printf("a: (x: %f, y: %f, z: %f)\n", a.x, a.y, a.z);
printf("b: (x: %f, y: %f, z: %f)\n", b.x, b.y, b.z);
printf("c: (x: %f, y: %f, z: %f)\n", c.x, c.y, c.z);
return 0;
}
如上所述使用 Swift 中的withUnsafeMutablePointer
方法執行此操作將導致11 with<Whatever>
范圍,這只是解析的浮點數的一半......
我想我應該可以做這樣的事情,但我不知道如何獲得指向其他結構成員的指針偏移量:
func parseVec3_3(s: String) -> Vector3? {
var output = Vector3(x: 0, y: 0, z: 0)
var r: CInt = 0
s.withCString { buffer in
withUnsafeMutablePointer(to: &output) { ptr in
withVaList([ptr, /* offset pointer here */]) { va in
r = vsscanf(buffer, "(%lf %lf %lf)", va)
}
}
}
return r == 3 ? output : nil
}
像這樣:
var a: Int = 0
var b: Float = 0
withUnsafePointer(to: &a) { pointerToA in
withUnsafePointer(to: &b) { pointerToB in
withVaList([pointerToA, pointerToB]) { va_list in
"(5, 3.14)".withCString { buffer in
let r = vsscanf(buffer, "(%d, %f)", va_list)
}
}
}
}
print(a)
print(b)
產出
5
3.14
更新
OP編輯的答案包括關於Vector3(x: 0, y: 0, z: 0)
的問題應該變成:
public struct Vector3: Equatable {
public var x: CGFloat
public var y: CGFloat
public var z: CGFloat
public init(x: CGFloat, y: CGFloat, z: CGFloat) {
self.x = x
self.y = y
self.z = z
}
}
func parseVec3_3(s: String) -> Vector3? {
var output = Vector3(x: 0, y: 0, z: 0)
var r: CInt = 0
s.withCString { buffer in
withUnsafePointer(to: &output) { (outputPointer: UnsafePointer<Vector3>) in
outputPointer.withMemoryRebound(to: CGFloat.self, capacity: 3) {
withVaList([OpaquePointer($0), OpaquePointer($0.advanced(by: 1)), OpaquePointer($0.advanced(by: 2))]) { va in
r = vsscanf(buffer, "(%lf %lf %lf)", va)
}
}
}
}
return r == 3 ? output : nil
}
if let vector: Vector3 = parseVec3_3(s: "(1.0 2.0 3.0)") {
print(vector)
}
產出
Vector3(x: 1.0, y: 2.0, z: 3.0)
來自CVarArgs 的文檔:
要為
c_api
函數創建包裝器,請編寫一個接受CVarArg
參數的函數,然后使用withVaList(_:_:)
函數調用導入的 C 函數。Swift 只導入使用
va_list
作為參數的 C 可變參數函數。 使用可變參數語法的 C 函數不會被導入,因此不能使用...
參數CVarArg
。
您的包裝函數可能如下所示:
func vsscanfSwiftWrapper(
buffer: UnsafePointer<CChar>,
format: UnsafePointer<CChar>,
_ arguments: CVarArg...
) -> CInt {
withVaList(arguments) { vaList in
vsscanf(buffer, format, vaList)
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.