Reputation: 2329
I'm trying to build a generic type MyStruct<T>
that can use either Float
or Double
for internal storage. In the initializer, I'm passing an argument of type T (which I intend to be either Float
or Double
). That initializer calls some trig functions such as sin()
and cos()
. Both of those functions are overloaded in the system libraries to provide Float
and Double
versions.
var f:Float=1.2
var d:Double=1.2
sin(f) //0.9320391
sin(d) //0.9320390859672263
Trouble is, I can't use them in my generic struct. A stripped down case would look like this:
struct MyStruct<T> {
var v:T
init(x:T){v=sin(x)}
}
Because:
Playground execution failed: Untitled Page.xcplaygroundpage:9:17: error: cannot invoke 'sin' with an argument list of type '(T)' init(x:T){v=sin(x)}
Untitled Page.xcplaygroundpage:9:17: note: overloads for 'sin' exist with these partially matching parameter lists: (Float), (Double) init(x:T){v=sin(x)}
It seems like there should be a way to make this work, but it feels a lot like this situation. Where the comments suggest there is no way to require the existence of a global function.
I can force the situation by using a construct such as:
init(x:T){v=T(sin(Double(x)))}
and putting a constraint on T that it can be constructed from a Double
, but that seems to defeat the purpose of making the Float
version of the struct, which is to reduce computation effort when this is used in a critical loop of code.
It feels like this would have been easier if sin()
had been defined in the library as a generic function rather than an overloaded function:
func sin<T:FloatingPointType> (x:T) -> T
but it is what it is.
Is there a way for me to build a library that is Float/Double agnostic on top of the standard libraries without adding a lot of overhead?
Upvotes: 4
Views: 3816
Reputation: 80801
Unfortunately, there's no easy way to do this the way sin()
is currently implemented in Swift. It would have to be treated as an operator (like how Equatable
and ==
works) in order to allow you to add it as a protocol requirement.
@matt's solution is a nice quick fix, but if you want something more permanent, you may want to consider creating a protocol and then extending the floating point types in order to allow you to overload the sin()
function with a generic version.
protocol FloatingPointMathType : FloatingPointType {
var _sinValue : Self { get }
}
extension Float : FloatingPointMathType {
var _sinValue : Float {return sin(self)}
}
extension Double : FloatingPointMathType {
var _sinValue : Double {return sin(self)}
}
extension CGFloat : FloatingPointMathType {
var _sinValue : CGFloat {return sin(self)}
}
func sin<T:FloatingPointMathType>(x:T) -> T {return x._sinValue}
(Feel free to add more math functions)
We're having to use a 'shadow' calculated property here to make up for the fact that we can't simply use sin()
as a protocol requirement. It's not ideal – but probably about as good as you're going to get.
You can now go ahead and use sin()
as a generic function:
struct MyStruct<T:FloatingPointMathType> {
var v : T
init(x:T) {
v = sin(x)
}
}
print(MyStruct(x: Float(3.0)).v) // 0.14112
print(MyStruct(x: Double(3.0)).v) // 0.141120008059867
print(MyStruct(x: CGFloat(3.0)).v) // 0.141120008059867
Upvotes: 5
Reputation: 535138
Might try something like this:
struct MyStruct<T:FloatingPointType> {
var v:T
init(x:T){
switch x {
case is Float:
v = sin(x as! Float) as! T
case is Double:
v = sin(x as! Double) as! T
case is CGFloat:
v = sin(x as! CGFloat) as! T
default:v = 0.0 as! T
}
}
}
Seems to work:
let s = MyStruct(x:Float(3))
s.v // 0.14112
let s2 = MyStruct(x:Double(3))
s2.v // 0.1411200080598672
let s3 = MyStruct(x:CGFloat(3))
s3.v // 0.1411200080598672
Upvotes: 0