Невозможно реализовать расширение протокола для соответствующего типа

(не стесняйтесь переименовывать вопрос соответствующим образом)

Я работаю с большим количеством данных BLE, и в целях отладки мне было легко расширить UInt8 с помощью вычисляемой переменной HEX:

extension UInt8 {
    var HEX:String {
        return String(format: "%02X", self)
    }
}

// 190.HEX --> "BE"

Я обнаружил, что хочу строчный вариант. А потом захотелось и для UInt32 и UInt16. Поскольку единственное, что меняется, — это количество цифр для печати, я подумал, что могу сделать это с помощью своего рода протокола (по крайней мере, в образовательных целях).

protocol HexPrintable {
    var hexDigitCount:Int { get }
}

extension UInt8:HexPrintable {
    var hexDigitCount:Int {
        return 2
    }
}

extension UInt16:HexPrintable {
    var hexDigitCount:Int {
        return 4
    }
}

extension UInt32:HexPrintable {
    var hexDigitCount:Int {
        return 8
    }
}

Затем следует часть, где я хочу воспользоваться этим и предоставить реализацию методов HEX и hex по умолчанию:

extension HexPrintable {
    var HEX:String {
        return String(format: "%0\(self.hexDigitCount)X", self)
    }

    var hex:String {
        return String(format: "%0\(self.hexDigitCount)x", self)
    }
}

Я получаю ошибку компилятора Argument type 'Self' does not conform to expected type 'CVarArgType'.

Думаю, я понимаю это. В нем говорится, что в качестве протокола он не может гарантировать, что принятые типы будут иметь тип (CVarArgType), который можно было бы использовать в инициализаторе String таким образом. Поэтому я подумал, что могу использовать предложение where в первый раз. Я изменил расширение своего протокола, чтобы оно выглядело так:

extension HexPrintable where Self == CVarArgType { ...

Что приводит к Same-type requirement makes generic parameter 'Self' non-generic. В этот момент мое теоретическое понимание шрифта-любителя переполнилось. Что за волшебство, чтобы заставить работать мои два метода расширения для разных размеров UInt?


person Travis Griggs    schedule 22.10.2015    source источник


Ответы (1)


Правильный синтаксис будет

extension HexPrintable where Self : CVarArgType { ... }

В качестве альтернативы сделайте так, чтобы ваш протокол HexPrintable наследовался от CVarArgType:

protocol HexPrintable : CVarArgType {
    var hexDigitCount:Int { get }
}

Обратите внимание, что вы можете реализовать ту же функциональность с одним расширением на IntegerType, используя sizeofValue() для определения ширины вывода:

extension IntegerType where Self : CVarArgType {
    var HEX : String {
        let size = sizeofValue(self)
        return String(format: "%0\(2*size)X", self)
    }
}

Но есть еще одна проблема: формат %X предполагает наличие аргумента Int32 (соответствующего типу C int). И ваш, и мой приведенный выше код не будут давать правильных результатов для значений, превышающих диапазон 32-битного целого числа.

Приводятся различные возможные решения этой проблемы, например. в Как создать универсальный функция преобразования целых чисел в шестнадцатеричные для всех типов Integer? (и я только что добавил еще одну).

person Martin R    schedule 22.10.2015
comment
Прохладный. Это, безусловно, проще, чем то, что я пытался. Было ли то, что я пытался сделать, технически возможным? Я хотел бы понять ошибки, которые я видел лучше... - person Travis Griggs; 22.10.2015
comment
Ааа... так это был всего лишь бит ==. Является ли == в предложении where действительно допустимым синтаксисом, который означает что-то еще? - person Travis Griggs; 22.10.2015
comment
@TravisGriggs: обычно == используется для равенства типов, а : — для соответствия протоколу. Но это кажется более сложным, например. extension SequenceType where Self.Generator.Element == Int {} компилируется, а extension Array where Self.Element == Int {} не компилируется :) — В любом случае, есть проблема как с вашим, так и с моим кодом, я обновлю ответ через мгновение. - person Martin R; 22.10.2015