Home > Software design >  Calling variadic C function from Swift with pointers
Calling variadic C function from Swift with pointers

Time:12-11

I'm trying to figure out how to call variadic C functions that write to pointers from Swift, such as vsscanf, but I don't understand how to actually construct the list of pointers to Swift variables.

I figure that if I have a string, I can get an UnsafePointer<CChar> and call vsscanf on it, but... how do I tell it where to actually write the data? How do I construct the CVaListPointer to pass to 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? */)
}

Basically, doing the same thing as 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;
}

CodePudding user response:

From the documentation on CVarArgs:

To create a wrapper for the c_api function, write a function that takes CVarArg arguments, and then call the imported C function using the withVaList(_:_:) function.

Swift only imports C variadic functions that use a va_list for their arguments. C functions that use the ... syntax for variadic arguments are not imported, and therefore can’t be called using CVarArg arguments.

Your wrapper function could look like:

func vsscanfSwiftWrapper(
    buffer: UnsafePointer<CChar>,
    format: UnsafePointer<CChar>,
    _ arguments: CVarArg...
) -> CInt {
    withVaList(arguments) { vaList in
        vsscanf(buffer, format, vaList)
    }
}

CodePudding user response:

Like this:

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)

outputs

5
3.14
  • Related