I am trying to call a C function (an extern API) from Swift with the following signature:
extern void k(int, const char*, ...)
I am calling the C function like this:
func send(query: String, args: CVarArg...) {
let query2 = UnsafeMutablePointer(mutating: query.cString(using: String.defaultCStringEncoding))
let result = k(0, query2, args)
}
But getting the error 'k' is unavailable: Variadic function is unavailable.
I am unsure if this is possible since the C API doesn't accept a va_list (http://swiftdoc.org/v3.1/protocol/CVarArg/). I do not mind having to write a wrapper function in C if need be.
You cannot call a C function taking a variable argument list from
Swift. You'll need a variant taking a va_list parameter
(similar to printf and vprintf):
int kv(int n, const char *s, va_list args) {
// ...
}
The original function k can forward to 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;
}
Now kv can be called from Swift:
func send(query: String, args: CVarArg...) {
let result = withVaList(args) {
kv(0, query, $0)
}
}
(You can pass a Swift String directly to a C function taking a
const char * parameter, the compiler inserts the necessary code to
create a temporary representation as null-terminated C string.)
Create a wrapper for every possible number of arguments:
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);
}
This is likely your only option but you can make it more generic...
We can create a function taking va_list, count the arguments and call the specific k1, k2, etc. variant, to provide a "nicer" API for 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);
}
}
Then you can create another wrapper in Swift, that takes variable number of arguments again:
func k(_ x: Int32, _ params: String...) {
withVaList(params) {
genericK(x, $0)
}
}
k(0, "testA", "testB", "testC")
The solution has one problem - you are limiting the number or arguments to a fixed number. That shouldn't be a big problem though...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With