从Swift调用C variadic函数

时间:2017-05-29 16:23:46

标签: swift variadic-functions

我正在尝试使用以下签名从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_listhttp://swiftdoc.org/v3.1/protocol/CVarArg/)。如果需要,我不介意在C中编写包装函数。

2 个答案:

答案 0 :(得分:2)

你不能从C语言中调用变量参数列表 迅速。您需要一个采用va_list参数的变体 (类似于printfvprintf):

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,计算参数并调用特定的k1k2等变体,为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")

解决方案有一个问题 - 您将数字或参数限制为固定数字。这应该不是一个大问题......