🍣
Swiftで色々な型の可変長引数でinoutパラメータにする方法
swiftでGo言語のfmt.Scan、fmt.Fscan、fmt.Sscanみたいなのを書いてみました。
そのとき可変長引数、型はバラバラで、なおかつinoutパラメータのように動く関数を書くのに苦労したので、その共有です。swiftの勉強として書いたものなので、標準的な解決方法は別にあるかもしれません。解決方法
関数の定義は次の通りです。
public func sscan(content: String, _ a :Any...)throws -> Int
使う側はUnsafeMutablePointer<T>
をAny
にキャストして渡します。
func p<T>(_ x: UnsafeMutablePointer<T>) -> Any {
return x
}
var (s, b, c) = ("", 0, 0)
let n = try sscan(content: "hello 2345 111", p(&s), p(&b), p(&c))
型はswitchで振り分けます。
func doScan(_ args: [Any])throws -> Int {
var n = 0
for arg in args {
buf = []
switch arg {
case let v as UnsafeMutablePointer<Int>:
v.pointee = try scanInt()
case let v as UnsafeMutablePointer<String>:
v.pointee = try convertString()
default:
throw NSError(domain: "unsupport type", code: -1, userInfo: nil)
}
n+=1
}
return n
}
とりあえずこれで動きますが、できれば関数p
みたいなの消したいです。
考えたこと
まず次のような感じに書くと'inout' must not be used on variadic parameters
というエラーが出ます。
func sscan(_ str: String, _ a: inout Any...)throws -> Int
可変長引数にinout
は使えないんですよね。
次に、こんな感じにすれば良いかなとも思いますが、これだとInt
とかString
は混ぜられないですよね。
func sscan<T>(_ str: String, _ a: inout [T])throws -> Int
そしたら実質inout
みたいに変更できて、どんな型でも混ぜられるUnsafeMutableRawPointer
みたいなのどうかと思ったのですが、これだと引数で渡した変数の型が全く分からなくなります。
func sscan(_ str: String, _ a: UnsafeMutableRawPointer...)throws -> Int
ということで、型情報がありinout
無しで変数変えるとなるとUnsafeMutablePointer<T>
を使うことになり、いろいろな型を混ぜるとなるとAny
で渡すのかなということになりました。
Discussion