在Swift中使用嵌入数据模拟操作码的惯用方法

时间:2016-09-21 19:26:29

标签: swift enums swift3

Swift枚举似乎非常适合在Swift3中建模字节操作码。例如:

enum MathOpCode:UInt8 {
    case Add = 0
    case Subtract = 1
    case Multiply = 2
    case Divide = 3
}

let a = 42
let b = 13
let someByte = 2

if let opcode = OpCode(rawValue: someByte) {
    switch opcode {
    case .Add:
        return a + b
    case .Subtract:
        return a - b
    case .Multiply:
        return a * b
    case .Divide:
        return a / b
    }
}

这对于编写二进制协议非常有用。枚举很好地捕获了逻辑操作码,然后开关读得很好。对我而言,OpCodes包含少量数据的地方。 IOW,我要说我添加了一个名为AddSmallConstant的OpCode,它代表所有匹配0b01nnnnnn的操作码,其中前两位必须是01,但是底部的6位是嵌入常数0-63。我可以在我的枚举中添加64个案例......

enum MathOpCode:UInt8 {
    ...
    case AddConstant0 = 0b01000000
    case AddConstant1 = 0b01000001
    ...
    case AddConstant63 = 0b01111111
}

这并不能很好地扩展。为了获得嵌入的值,我必须使用rawValue和masking操作来获取它。我不能有一个看起来像

的switch语句
case MathOpCode.AddConstant0...MathOpCode.AddConstant63

匹配整个范围,因为枚举案例无法转换为范围。替代方案是在整个地方使用rawValue

switch opCode.rawValue {
case MathOpCode.Add.rawValue:
    ...
case MathOpCode.Subtract.rawValue:
    ...
case (MathOpCode.AddConstant0.rawValue)...(MathOpCode.AddConstant63.rawValue):
    ...
}

现在enum看起来像是行李,最好只定义一堆常量让我的文件顶部。我错过了一个更好的模式,我可以在Swift中使用它来表达这些类型的关系和模式吗?

1 个答案:

答案 0 :(得分:1)

如您所知,Swift有一个名为关联值的奇特功能,但不幸的是,您无法控制具有关联值的枚举的原始位表示。

如果您想控制MathOpCode类型的原始位代表,则可能需要创建RawRepresentable类型。

例如,你可以这样写:

struct MathOpCode: RawRepresentable {
    var rawValue: UInt8
    init(rawValue: UInt8) {self.rawValue = rawValue}

    static let add = MathOpCode(rawValue: 0)
    static let subtract = MathOpCode(rawValue: 1)
    static let multiply = MathOpCode(rawValue: 2)
    static let divide = MathOpCode(rawValue: 3)

    static let addConstant = MathOpCode(rawValue: 0b0100_0000)
    static func add(constant value: UInt8) -> MathOpCode {
        guard value < 64 else {fatalError("constant out of bounds")}
        return MathOpCode(rawValue: self.addConstant.rawValue + value)
    }

    var isAddConstant: Bool {return self.rawValue & 0b1100_0000 == MathOpCode.addConstant.rawValue}
    var constant: UInt8 {return self.rawValue & 0b0011_1111}
}
//Prepare `matches` operator for `switch`.
func ~= (lhs: MathOpCode, rhs: MathOpCode) -> Bool {
    return lhs == MathOpCode.addConstant && rhs.isAddConstant
     || lhs == rhs
}

您可以将其用作:

let opCode = MathOpCode.add(constant: 22)

switch opCode {
case MathOpCode.add:
    print("add")
case MathOpCode.subtract:
    print("subtract")
case MathOpCode.multiply:
    print("multiply")
case MathOpCode.divide:
    print("divide")
case MathOpCode.addConstant:
    print("addConstant", opCode.constant)
default:
    print("invalid opCode")
}
//->addConstant 22