Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

passing variadic arguments to 'print' doesn't produce desired output

Tags:

swift

I'm writing a LogUtil class, which simply pass arguments to 'print' functions with extra information like "filename, line NO. " etc. see below codes.

However, usually when we use 'print' function to output like this:

  print("param1", "param2")

it outputs:

  param1 param2

however, use my LogUtil to log,

 LogUtil.d("tag", "param1", "param2"), 

it outputs:

D - /LogUtil.swift(44): [["tag", "param1", "param2"]]

I want to output the original 'print' style:

D -/LogUtil.swift(44): tag param1 param2

how to fix this?Thanks!

class func p(level: LogLevel, items: Any...) {
    //todo eric thread id
    //todo eric fileName is too long
    //todo eric loglevel control
    //todo eric outputs to file


    let filePath = __FILE__
    let lineNo = __LINE__
    print("\(getLogLevelDesc(level)) - \(filePath)(\(lineNo)):", items)

}

class func d(items: Any...) {
    return p(LogLevel.DEBUG, items: items)
}

class func i(items: Any...) {
    return p(LogLevel.INFO, items: items)
}

class func w(items: Any...) {
    return p(LogLevel.WARN, items: items)
}

class func e(items: Any...) {
    return p(LogLevel.ERROR, items: items)
}

class func fatal(items: Any...) {
    return p(LogLevel.FATAL, items: items)
}
like image 621
ericxu1983 Avatar asked Apr 29 '16 02:04

ericxu1983


People also ask

Is printf variadic function?

Variadic functions are functions (e.g. printf) which take a variable number of arguments.

Why do we use variadic parameters in Swift?

A variadic parameter accepts zero or more values of a specified type. You use a variadic parameter to specify that the parameter can be passed a varying number of input values when the function is called.

Can variadic methods take any number of parameters?

A variadic function allows you to accept any arbitrary number of arguments in a function.


1 Answers

First you call LogUtil.d("tag", "param1", "param2"). Since the items parameter is declared as variadic Any..., the local variable items inside d has type [Any], and has the value ["tag", "param1", "param2"].

Then d calls p, passing its own items as the first argument for p's items parameter. Since p's items parameter is also variadic, d could pass more arguments for p's items. That is, d could call p(LogLevel.DEBUG, items: items, "more", "words"). It doesn't, but it could.

Since p's items parameter is variadic Any..., the local variable items inside p is also of type [Any], and its value is [["tag", "param1", "param2"]]. It is an array of three elements inside an array of one element.

What you need to do is make p, or some new function, take a non-variadic items parameter, and call that from d and all your other variadic functions.

Also, __FILE__ and __LINE___ are deprecated. If you've upgraded to Xcode 7.3, you should use the new #file and #line special forms instead.

But you still have a problem: #file and #line will return the file and line where they are used inside function p. That's probably not what you want. You probably want the file and line where the program calls d or i or whatever.

To do that, you pass the file and line arguments to d (and the other functions), with default values of #file and #line. The defaults will be expanded at the call site, so you'll get the file and line number of the call to d or i or whatever.

So ultimately you want one function that knows how to take an [Any] (with only one level of array wrapping) and turn it into a space-separated string. Here's how you do that:

struct LogUtil {

    enum LogLevel: String {
        case Debug
        case Info
        case Warning
        case Error
        case Fatal
    }

    // The low-level function that all the others call.
    private static func log(level level: LogLevel, file: String, line: Int, items: [Any]) {
        let itemString = items.map { String($0) }.joinWithSeparator(" ")
        print("\(level) - \(file)(\(line)): \(itemString)")
    }

Note that log(level:file:line:items:) is not variadic, so it won't add another array wrapper around its items.

Then you define all your user-visible functions to call the log function:

    static func p(level: LogLevel, items: Any..., file: String = #file, line: Int = #line) {
        log(level: level, file: file, line: line, items: items)
    }

    static func d(items: Any..., file: String = #file, line: Int = #line) {
        log(level: .Debug, file: file, line: line, items: items)
    }

    static func i(items: Any..., file: String = #file, line: Int = #line) {
        log(level: .Info, file: file, line: line, items: items)
    }

    // other level-specific functions here...

}

When you call LogUtil.d("tag", "foo", "bar"), the output looks like you want:

Debug - /var/folders/kn/<snip>/playground282.swift(33): tag foo bar
like image 186
rob mayoff Avatar answered Oct 14 '22 14:10

rob mayoff