且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

基本类型的通用协议

更新时间:2022-10-14 20:03:48

You're very close, but you don't need a fixed-length tuple. That's what's causing your headaches. Just use an array instead.

@infix func % (values:CVarArg[], format:String) -> String {
  return NSString(format:format, arguments:getVaList(values))
}

[M_PI, 6] % "%.3f->%d"
==> "3.142->6"

[M_PI, M_PI_2] % "%.3f %.3f"
==> "3.142 1.571"

Of course this is highly type-unsafe because it's an unchecked printf as you say.

BTW, this even works with mixed-type stuff, and with non-literals:

let x = 1
let y = 1.5
let z = "yes"

[x, y, z] % "%d, %.2f, %@"
==> "1, 1.50, yes"

I don't know if that part is going to be fragile, however. Mixed-type literals are promoted to NSArray, which seems a dangerous thing to do automatically, so they may change it. But NSArray is acceptable as a CVarArg[].

Note that not all types can be converted this way. Characters currently cannot for instance. You can overcome this by extending them to do so:

extension Character : CVarArg {
  func encode() -> Word[] {
    var result = Word[]()
    let s = String(self)
    for c in s.unicodeScalars {
      result.append(Word(c.value))
    }
    return result
  }
}

let c:Character = "c"

["I", c, 2*3] % "%@, %lc, %d"
==> "I, c, 6"

I'm wondering if there's an easier way to write encode(), but I'm not sure yet. Hopefully a Character encoding will be provided by Swift in the future. But the lesson here is that arbitrary types could be given an encode and be formatted.

class Car {
  let make = "Ford"
}

extension Car : CVarArg {
  func encode() -> Word[] {
    return NSString(string:self.make).encode()
  }
}

let car = Car()

[car] % "%@"

The lesson here is that you can turn arbitrary things into a CVarArg or into any protocol, via extensions.