NumberConvertibleの紹介

>   この記事は[https://academy.realm.io/jp/posts/richard-fox-casting-swift-2/](https://academy.realm.io/jp/posts/richard-fox-casting-swift-2/)を元にしています。
> Swiftもバージョンアップされて当時と状況も変わってきたので、いつくかアップデートしてます。



Swiftは厳格な静的型付け言語です。

そのため


```swift
let num1: Int = 1 
let num2: Double = 2.0
let num3: CGFloat = num1 + num2
```

のような計算は行えません。

しかし、ビット演算や金額処理などを除いてモバイルアプリの数値計算にここまで厳格な数値への型付けは必要でしょうか？

そのため今回は演算子のオーバーロードとプロトコルを活用して、Swiftの型安全性を少し犠牲に簡易的な演算手法を紹介します。



最初に思いつくのが、全ての型同士の演算に対して演算子を定義してしまうと言う手法です。しかしこれでは必要な演算子の数は膨大になり、コンパイラが型を決定するのにかかる時間も長くなってしまいます。

そのため`protocol`を使って一般化し、様々な以下のように型同士で演算ができるようにします。



## 実装

まずは以下のような`protocol`を定義します。

```swift
protocol Dividable {
    static func / (lhs: Self, rhs: Self) -> Self
}
protocol NumberConvertible: Numeric, Dividable {
    init (_ value: Int)
    init (_ value: Float)
    init (_ value: Double)
    init (_ value: CGFloat)
}
```

`Numeric`には除算が定義されてないため、`Dividable`を定義するととで、除算に対応しています。



そして、各数値型を`NumberConvertible`に対応させます。

```swift
extension Double  : NumberConvertible {}
extension Float   : NumberConvertible {}
extension Int     : NumberConvertible {}
extension CGFloat : NumberConvertible{
    init(_ value: CGFloat){
        self = value
    }
}
```

`CGFloat`には`CGFloat`からのイニシャライザは提供されていないので、その部分だけ定義します。



変換用のメソッドとして`convert()`を定義します。

```swift
extension NumberConvertible {
	func convert<T: NumberConvertible>() -> T {
        switch self {
        case let x as CGFloat: return T(x)
        case let x as Float: return T(x)
        case let x as Double: return T(x)
        case let x as Int: return T(x)
        default:
            fatalError("NumberConvertible convert cast failed!")
        }
    }
}
```



最後に各演算子を定義してあげます。

```swift
func + <T: NumberConvertible, U: NumberConvertible, V: NumberConvertible>(rhs: T, lhs: U) -> V {
    let v: V = lhs.convert()
    let w: V = rhs.convert()
    return v + w
}
func - <T: NumberConvertible, U: NumberConvertible, V: NumberConvertible>(rhs: T, lhs: U) -> V {
    let v: V = lhs.convert()
    let w: V = rhs.convert()
    return v - w
}
func * <T: NumberConvertible, U: NumberConvertible, V: NumberConvertible>(rhs: T, lhs: U) -> V {
    let v: V = lhs.convert()
    let w: V = rhs.convert()
    return v * w
}
func / <T: NumberConvertible, U: NumberConvertible, V: NumberConvertible>(rhs: T, lhs: U) -> V {
    let v: V = lhs.convert()
    let w: V = rhs.convert()
    return v / w
}
```

代入演算子も

```swift
func += <T: NumberConvertible, U: NumberConvertible>(rhs: inout T, lhs: U)  {
    rhs = rhs.convert() + lhs.convert()
}
func -= <T: NumberConvertible, U: NumberConvertible>(rhs: inout T, lhs: U)  {
    rhs = rhs.convert() - lhs.convert()
}
func *= <T: NumberConvertible, U: NumberConvertible>(rhs: inout T, lhs: U)  {
    rhs = rhs.convert() * lhs.convert()
}
func /= <T: NumberConvertible, U: NumberConvertible>(rhs: inout T, lhs: U)  {
    rhs = rhs.convert() / lhs.convert()
}
```

最後にOptional型に対して `??`  を定義し

簡易変換用に独自演算子 `~~` を定義します。

```swift
func ?? <T: NumberConvertible, U: NumberConvertible, V:NumberConvertible>(lhs: T?, rhs: U) -> V {
    let v: V? = lhs?.convert()
    return v == nil ? rhs.convert() : v!
}

prefix operator ~~
prefix func ~~<T: NumberConvertible, U: NumberConvertible>(lhs: T) -> U {
    return lhs.convert()
}
```

以上で完成です。



## 使い方

```swift
let num1: Int = 2
let num2: Double = 3.14
let num3: CGFloat = num1 + num2
```

```swift
let view = UIView()
let width: Int = 320
let height: Double = 100.5

view.frame.size.width = ~~width
view.frame.size.height = ~~height
```



速度の調整などをしたものは
[https://github.com/ObuchiYuki/NumberConvertible/blob/master/NumberConvertible.swift](https://github.com/ObuchiYuki/NumberConvertible/blob/master/NumberConvertible.swift
)にあります。