Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift equivalent of #define to retrieve list of variable arguments

Tags:

macros

swift

So I have a function in C that works as

#define PRINTF(format) { va_list list; va_start(list, format); vprintf(format, list);}

void foo(const char* format, ...) {
   PRINTF(format)
}

I was wondering if I were to make a Swift equivalent of the above code, how wouldI represent the ... as? The only ways that I know how to use variable arguments is with the use of CVarArgType, or with the use of an array of arguments.

However, since this C snippet uses a macro, I'm finding it hard to port this code to Swift without changing the parameters of the function I make instead of a macro. Could I get any insight or help as to how I would go about doing something like this?

I need help to write the #define macro equivalent function in Swift, and am having trouble with coming up with an equivalent to va_list list, va_start calls.

like image 261
Kunal Avatar asked Jan 30 '26 17:01

Kunal


1 Answers

Well, my original answer absolutely overcomplicates things...

We do need to take a String for the format, and a CVarArgType... for the list of arguments (which as treated as a [CVarArgType] within the function). I complicated it by trying to use Objective-C's NSString. If we stick with Swift's String, it actually has an initializer which takes the arguments we're passing.

The simplest way to write this function in Swift looks like this:

func foo(format: String, args: CVarArgType...) {
    println(String(format: format, arguments: args))
}

Swift does not allow for the #define macro, to my knowledge. However, we can convert that C macro to a regular Swift function.

func foo(format: String, args: CVarArgType...) {
    println(NSString(format: format, arguments: getVaList(args)))
}

CVarArgType is a Swift protocol which accepts the types we normally pass into the String for format strings.

The ... makes the function accept any number of arguments for this last parameters. Within the function, this will have the type [CVarArgType] (an array).

Meanwhile, NSString's initializer (and similarly, NSLog and other similar spots) are looking for a CVaListPointer as the last argument, so we can use Swift's built-in function, getVaList, which converts a [CVarArgType] to a CVaListPointer.

This function can be called as such:

foo("%@, %@... %i", "hello", "world", 123)

Which will print:

hello, world... 123

And as @AirspeedVelocity points out, it's generally recommended to use withVaList rather than getVaList. We can do so something like this:

func foo(format: String, args: CVarArgType...) {
    func buildLogString(argList: CVaListPointer) -> String {
        return NSString(format: format, arguments: argList) as String
    }
    println(withVaList(args, buildLogString))
}

For applications beyond this specific example, it's important to note that we can use the ... to accept a list of arguments of any type, and within the function, we get an array of that type.

For example:

func bar(myVariadicListOfStuff: MyStuffClass...) {
    // myVariadicListOfStuff is a [MyStuffClass]
    for thing in myVariadicListOfStuff {
        // this is a MyStuffClass
    }
}
like image 150
nhgrif Avatar answered Feb 01 '26 07:02

nhgrif