将位索引数组转换为 OptionSet

Converting array of bit indexes to OptionSet

我正在尝试编写一个辅助函数,它将位索引数组转换为符合 OptionSet 的 class。

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 上添加额外的约束吗?

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

您可能需要更多设置才能让您的 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)

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

但是每个无符号整数类型都可以从 and 转换为 为 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)) {
            [=11=] | 1 << UIntMax()
        }
        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) { [=14=] | 1 <<  })
    }
}

无需中间转换为“最大”整数类型。