M. Koot
M. Koot

Reputation: 1176

How to convert generic numbers to Double in swift?

I've made a class with generic type T, where T should only be a numeric value (CGFloat, Double, Int, etc.) and should be able to be converted to Double. I've tried several types, like Numeric and Equatable, but those still cast the net too wide. When converting to Double the type should conform to either BinaryInteger or BinaryFloatingPoint but I've yet to find a way to let a generic conform to either one of two types.

What I want:

class SomeClass<T:SomeNumberTypeThatAllowsConversionToDouble> {
    var number: T
    var doubleNumber: Double
    init(number: T) {
        self.number = number
        self.doubleNumber = Double(number) //or some other function that converts from T to Double
    }
}

What I don't want is two separate extensions to SomeClass where one handles the BinaryInteger case and one the BinaryFloatingPoint case, since that would defeat the purpose of why I'm using generics in the first place.

Upvotes: 3

Views: 570

Answers (1)

Charlie Cai
Charlie Cai

Reputation: 301

What you would like achieve is constraint T to conform to a fixed group of protocol.

It is very similar to what we have in Typescript T:number|point|size ( union type mentioned by @Dávid Pásztor )

below is my workaround in Swift.It is not that neat and maybe we can have better solution.

protocol DoubleConvertible { }

extension Int:DoubleConvertible { }
extension CGFloat:DoubleConvertible { }
extension Double:DoubleConvertible {
    static func convert(_ number:DoubleConvertible) -> Double {
        if let intNumber = number as? Int {
            return Double(intNumber)
        } else if let cgFloatNumber = number as? CGFloat {
            return Double(cgFloatNumber)
        } else if let doubleNumber = number as? Double{
            return Double(doubleNumber)
        } else {
            assertionFailure()
        }
    }
}

import UIKit

class SomeClass<T> where T:DoubleConvertible {
    var number: T
    var doubleNumber: Double
    init(number: T) {
        self.number = number
        self.doubleNumber = Double.convert(number)
    }
}

print(SomeClass<Int>(number: 10).doubleNumber)
print(SomeClass<Double>(number: 10.0).doubleNumber)
print(SomeClass<CGFloat>(number: CGFloat(integerLiteral: 10)).doubleNumber)

//10.0
//10.0
//10.0

print(SomeClass<CGPoint>(number: 10).doubleNumber)
//Type 'CGPoint' does not conform to protocol 'DoubleConvertible'

Upvotes: 3

Related Questions