Reputation: 22252
(feel free to retitle question as appropriate)
I'm working with a lot of BLE data, and for debugging purposes, I've found it easy to extend UInt8
with a HEX
computed variable:
extension UInt8 {
var HEX:String {
return String(format: "%02X", self)
}
}
// 190.HEX --> "BE"
I found myself wanting a lowercase variant. And then I wanted it for UInt32
and UInt16
as well. Since the only thing that changes is the number of digits to print, I thought I could do this with a protocol of sorts (at least for education purposes).
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
}
}
Then comes the part where I want to take advantage of this and provide default implementations of the HEX
and hex
methods:
extension HexPrintable {
var HEX:String {
return String(format: "%0\(self.hexDigitCount)X", self)
}
var hex:String {
return String(format: "%0\(self.hexDigitCount)x", self)
}
}
I get a compiler error Argument type 'Self' does not conform to expected type 'CVarArgType'
.
I think I understand this. It's saying that as a protocol, it can't guarantee that adopting types will be of a type (CVarArgType
) that could be used in the String initializer like that. So I thought I could use a where
clause for the first time. I modified my protocol extension to look like:
extension HexPrintable where Self == CVarArgType { ...
Which leads to a Same-type requirement makes generic parameter 'Self' non-generic
. At which point my amateur type theorist understanding overflowed. What is the magic to make my two extension methods on different UInt sizes work?
Upvotes: 1
Views: 593
Reputation: 539685
The correct syntax would be
extension HexPrintable where Self : CVarArgType { ... }
Alternatively, make your HexPrintable
protocol inherit from CVarArgType
:
protocol HexPrintable : CVarArgType {
var hexDigitCount:Int { get }
}
Note that you can implement the same functionality
with a single extension on IntegerType
, using sizeofValue()
to determine the output width:
extension IntegerType where Self : CVarArgType {
var HEX : String {
let size = sizeofValue(self)
return String(format: "%0\(2*size)X", self)
}
}
But there is another problem: The %X
format expects a Int32
argument
(corresponding to the C int
type). Both your and my above code will
not produce correct results for values exceeding the range of a 32-bit
integer.
Various possible solutions to that problem are given e.g. in How to create a generic integer-to-hex function for all Integer types? (and I just added another one).
Upvotes: 1