I'm trying to write a helper function which will convert an array of bit indexes to a class conforming to OptionSet.
func getOptionSet<T: OptionSet>(bitIndexes: [Int64]) -> T {
var result: Int64 = 0
for index in bitIndexes {
result |= 1 << index
}
return T(rawValue: result) // error
}
This fails to compile:
Cannot invoke initializer for type 'T' with an argument list of type '(rawValue: Int64)'
I've also tried using RawValue:
func getOptionSet<T: OptionSet>(bitIndexes: [T.RawValue]) {
var result = T.RawValue() // error
This doesn't work as well:
Cannot invoke value of type 'T.RawValue.Type' with argument list '()'
Can this be done? Do I need to add additional constraints on T?
I know it's possible to rewrite this function to use a concrete type, but I want to keep it generic if possible.
The problem in your code is that Int64
and T.RawValue
are
unrelated and can be different types.
But every unsigned integer type can be converted from and
to UIntMax
, so the problem can be solved by restricting RawValue
to UnsignedInteger
.
Using @OOPer's idea to define a custom initializer this would be:
extension OptionSet where RawValue: UnsignedInteger {
init(bitIndexes: [Int]) {
var result: UIntMax = 0
for index in bitIndexes {
result |= 1 << UIntMax(index)
}
self.init(rawValue: RawValue(result))
}
}
which can also be written as
extension OptionSet where RawValue: UnsignedInteger {
init(bitIndexes: [Int]) {
let result = bitIndexes.reduce(UIntMax(0)) {
$0 | 1 << UIntMax($1)
}
self.init(rawValue: RawValue(result))
}
}
All option set types that I have seen so far have an unsigned integer
type as raw value, but note that the same would also work with
SignedInteger
and IntMax
.
Example:
struct TestSet: OptionSet {
let rawValue: UInt16
init(rawValue: UInt16) {
self.rawValue = rawValue
}
}
let ts = TestSet(bitIndexes: [1, 4])
print(ts) // TestSet(rawValue: 18)
Compare also How do you enumerate OptionSetType in Swift? for the reverse task.
Update: As of Swift 4 the UnsignedInteger
protocol has a
public static func << <RHS>(lhs: Self, rhs: RHS) -> Self where RHS : BinaryInteger
method, so that the above code can be simplified to
extension OptionSet where RawValue: UnsignedInteger {
init(bitIndexes: [Int]) {
self.init(rawValue: bitIndexes.reduce(0) { $0 | 1 << $1 })
}
}
without intermediate conversion to a “maximal” integer type.