从 Swift 调用 C 可变参数函数
Call C variadic function from Swift
我正在尝试使用以下签名从 Swift 调用 C 函数(外部 API):
extern void k(int, const char*, ...)
我这样调用 C 函数:
func send(query: String, args: CVarArg...) {
let query2 = UnsafeMutablePointer(mutating: query.cString(using: String.defaultCStringEncoding))
let result = k(0, query2, args)
}
但是出现错误'k' is unavailable: Variadic function is unavailable
。
我不确定这是否可行,因为 C API 不接受 va_list
(http://swiftdoc.org/v3.1/protocol/CVarArg/)。如果需要,我不介意在 C 中编写包装函数。
您不能调用从中获取变量参数列表的 C 函数
Swift。您需要一个带有 va_list
参数的变体
(类似于 printf
和 vprintf
):
int kv(int n, const char *s, va_list args) {
// ...
}
原函数k
可以转发到kv
:
int k(int n, const char* s, ...) {
va_list args;
va_start(args, s);
int result = kv(n, s, args);
va_end(args);
return result;
}
现在可以从 Swift 调用 kv
:
func send(query: String, args: CVarArg...) {
let result = withVaList(args) {
kv(0, query, [=12=])
}
}
(您可以将 Swift String
直接传递给采用
const char *
参数,编译器插入必要的代码到
创建一个临时表示为以 null 结尾的 C 字符串。)
为每个可能的参数数量创建一个包装器:
void k1(int x, const char *param1) {
k(x, param1);
}
void k2(int x, const char *param1, const char *param2) {
k(x, param1, param2);
}
void k3(int x, const char *param1, const char *param2, const char *param3) {
k(x, param1, param2, param3);
}
这可能是您唯一的选择,但您可以使其更通用...
我们可以创建一个采用 va_list
的函数,对参数进行计数并调用特定的 k1
、k2
等变体,以提供 "nicer" API 对于 Swift:
void genericK(int x, va_list params) {
const char * paramArray[10];
int numParams = 0;
NSString* arg = nil;
while ((arg = va_arg(params, NSString *))) {
paramArray[numParams++] = [arg cStringUsingEncoding:[NSString defaultCStringEncoding]];
}
switch (numParams) {
case 1:
k(x, paramArray[0], NULL);
break;
case 2:
k(x, paramArray[0], paramArray[1], NULL);
break;
case 3:
k(x, paramArray[0], paramArray[1], paramArray[2], NULL);
break;
default:
assert(false);
}
}
然后您可以在 Swift 中创建另一个包装器,它再次采用可变数量的参数:
func k(_ x: Int32, _ params: String...) {
withVaList(params) {
genericK(x, [=12=])
}
}
k(0, "testA", "testB", "testC")
解决方案有一个问题 - 您将数量或参数限制为固定数量。不过这应该不是什么大问题...
我正在尝试使用以下签名从 Swift 调用 C 函数(外部 API):
extern void k(int, const char*, ...)
我这样调用 C 函数:
func send(query: String, args: CVarArg...) {
let query2 = UnsafeMutablePointer(mutating: query.cString(using: String.defaultCStringEncoding))
let result = k(0, query2, args)
}
但是出现错误'k' is unavailable: Variadic function is unavailable
。
我不确定这是否可行,因为 C API 不接受 va_list
(http://swiftdoc.org/v3.1/protocol/CVarArg/)。如果需要,我不介意在 C 中编写包装函数。
您不能调用从中获取变量参数列表的 C 函数
Swift。您需要一个带有 va_list
参数的变体
(类似于 printf
和 vprintf
):
int kv(int n, const char *s, va_list args) {
// ...
}
原函数k
可以转发到kv
:
int k(int n, const char* s, ...) {
va_list args;
va_start(args, s);
int result = kv(n, s, args);
va_end(args);
return result;
}
现在可以从 Swift 调用 kv
:
func send(query: String, args: CVarArg...) {
let result = withVaList(args) {
kv(0, query, [=12=])
}
}
(您可以将 Swift String
直接传递给采用
const char *
参数,编译器插入必要的代码到
创建一个临时表示为以 null 结尾的 C 字符串。)
为每个可能的参数数量创建一个包装器:
void k1(int x, const char *param1) {
k(x, param1);
}
void k2(int x, const char *param1, const char *param2) {
k(x, param1, param2);
}
void k3(int x, const char *param1, const char *param2, const char *param3) {
k(x, param1, param2, param3);
}
这可能是您唯一的选择,但您可以使其更通用...
我们可以创建一个采用 va_list
的函数,对参数进行计数并调用特定的 k1
、k2
等变体,以提供 "nicer" API 对于 Swift:
void genericK(int x, va_list params) {
const char * paramArray[10];
int numParams = 0;
NSString* arg = nil;
while ((arg = va_arg(params, NSString *))) {
paramArray[numParams++] = [arg cStringUsingEncoding:[NSString defaultCStringEncoding]];
}
switch (numParams) {
case 1:
k(x, paramArray[0], NULL);
break;
case 2:
k(x, paramArray[0], paramArray[1], NULL);
break;
case 3:
k(x, paramArray[0], paramArray[1], paramArray[2], NULL);
break;
default:
assert(false);
}
}
然后您可以在 Swift 中创建另一个包装器,它再次采用可变数量的参数:
func k(_ x: Int32, _ params: String...) {
withVaList(params) {
genericK(x, [=12=])
}
}
k(0, "testA", "testB", "testC")
解决方案有一个问题 - 您将数量或参数限制为固定数量。不过这应该不是什么大问题...