将位索引数组转换为OptionSet

时间:2016-07-15 13:03:51

标签: swift generics bit-fields swift3 bitvector

我试图编写一个帮助函数,它将一个位索引数组转换为一个符合OptionSet的类。

func getOptionSet<T: OptionSet>(bitIndexes: [Int64]) -> T {
    var result: Int64 = 0
    for index in bitIndexes {
        result |= 1 << index
    }
    return T(rawValue: result) // error
}

无法编译:

Cannot invoke initializer for type 'T' with an argument list of type '(rawValue: Int64)'

我也尝试过使用RawValue:

func getOptionSet<T: OptionSet>(bitIndexes: [T.RawValue]) {
    var result = T.RawValue()  // error

这也不起作用:

Cannot invoke value of type 'T.RawValue.Type' with argument list '()'

可以这样做吗?我是否需要在T上添加其他约束?

我知道可以重写此函数以使用具体类型,但我希望尽可能保持通用。

2 个答案:

答案 0 :(得分:4)

代码中的问题是Int64T.RawValue 不相关的,可以是不同的类型。

但是每个无符号整数类型都可以从和转换 到UIntMax,因此可以通过将RawValue限制为UnsignedInteger来解决问题。

使用@ OOPer的想法来定义自定义初始化程序,这将是:

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))
    }
}

也可以写成

extension OptionSet where RawValue: UnsignedInteger {
    init(bitIndexes: [Int]) {
        let result = bitIndexes.reduce(UIntMax(0)) {
            $0 | 1 << UIntMax($1)
        }
        self.init(rawValue: RawValue(result))
    }
}

到目前为止,我看到的所有选项集类型都有一个无符号整数 键入原始值,但请注意,同样也可以使用 SignedIntegerIntMax

示例:

struct TestSet: OptionSet {
    let rawValue: UInt16
    init(rawValue: UInt16) {
        self.rawValue = rawValue
    }
}

let ts = TestSet(bitIndexes: [1, 4])
print(ts) // TestSet(rawValue: 18)

还要比较How do you enumerate OptionSetType in Swift?的反向任务。

更新: Swift 4 开始,UnsignedInteger协议有

public static func << <RHS>(lhs: Self, rhs: RHS) -> Self where RHS : BinaryInteger

方法,以便上面的代码可以简化为

extension OptionSet where RawValue: UnsignedInteger {
    init(bitIndexes: [Int]) {
        self.init(rawValue: bitIndexes.reduce(0) { $0 | 1 << $1 })
    }
}

没有中间转换为“最大”整数类型。

答案 1 :(得分:3)

您可能需要更多设置才能使getOptionSet正常工作:

protocol OptionBitShiftable: IntegerLiteralConvertible {
    func << (lhs: Self, rhs: Self) -> Self
    func |= (lhs: inout Self, rhs: Self)
}
extension Int64: OptionBitShiftable {}
extension UInt64: OptionBitShiftable {}
//...

有了上述内容,您可以像这样写getOptionSet

func getOptionSet<T: OptionSet where T.RawValue: OptionBitShiftable>(bitIndexes: [T.RawValue]) -> T {
    var result: T.RawValue = 0
    for index in bitIndexes {
        result |= 1 << index
    }
    return T(rawValue: result)
}

用法:

struct MyOptionSet: OptionSet {
    var rawValue: Int64
    init(rawValue: Int64) {
        self.rawValue = rawValue
    }
}
let myOption: MyOptionSet = getOptionSet(bitIndexes: [1,2,3,5,7])
print(myOption.rawValue) //->174(=2+4+8+32+128)

或者,您可以像这样定义一个初始值设定项:

extension OptionSet where RawValue: OptionBitShiftable {
    init(bitIndexes: [RawValue]) {
        var result: RawValue = 0
        for index in bitIndexes {
            result |= 1 << index
        }
        self.init(rawValue: result)
    }
}

您可以将其用作:

let alsoMyOption = MyOptionSet(bitIndexes: [4, 6])
print(alsoMyOption.rawValue) //->80(=16+64)