我正在尝试使用以下签名从Swift调用C函数(extern 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中编写包装函数。
答案 0 :(得分:2)
你不能从C语言中调用变量参数列表
迅速。您需要一个采用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, $0)
}
}
(你可以直接将Swift String
传递给C函数
const char *
参数,编译器插入必要的代码
创建一个临时表示形式,作为以null结尾的C字符串。)
答案 1 :(得分:1)
为每个可能数量的参数创建一个包装器:
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
等变体,为Swift提供“更好”的API:
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, $0)
}
}
k(0, "testA", "testB", "testC")
解决方案有一个问题 - 您将数字或参数限制为固定数字。这应该不是一个大问题......