I have a problem of getting the size of a variable declared as Any.Type
See the following playground code:
I have this function:
func genericSizeMe<T> (_ : T.Type) -> Int
{
return MemoryLayout<T>.size
}
I run it like this:
let size1 = genericSizeMe(UInt32.self) // 4
let size2 = genericSizeMe(UInt16.self) // 2
var type1: UInt32.Type = UInt32.self
let size3 = genericSizeMe(type1) // 4
var type2: UInt16.Type = UInt16.self
let size4 = genericSizeMe(type2) // 2
var type3: Any.Type = UInt32.self
let size5 = genericSizeMe(type3) //ERROR
This gives the error:
/*
Playground execution failed:
error: MyPlayground.playground:14:13: error: cannot invoke 'genericSizeMe' with an argument list of type '(Any.Type)'
let size5 = genericSizeMe(type3)
^
MyPlayground.playground:14:13: note: expected an argument list of type '(T.Type)'
let size5 = genericSizeMe(type3)
^
*/
How (if possible) would one solve this? What I want to achieve is to have an array of types, and get the size of each type. Like this:
[UInt32.self, UInt8.self]
And loop over the array and print the byte size needed to allocate each type.
If easier, I can also accept to actually make an instance of each type before getting the size.
The problem that you're running into with your generic function is that when a generic placeholder T
is a protocol type P
, T.Type
is not P.Type
; it's P.Protocol
.
You cannot therefore pass an Any.Type
to a T.Type
parameter (although Any
isn't technically a protocol, it's a special built-in type; it has the semantics of a protocol in most cases). You need to pass a metatype that represents a concrete type.
Therefore one solution is to build a type-erasing wrapper for metatypes, as shown in Check whether Swift object is an instance of a given metatype:
struct AnyType {
let base: Any.Type
private let _memorySize: () -> Int
private let _memoryStride: () -> Int
private let _memoryAlignment: () -> Int
var memorySize: Int { return _memorySize() }
var memoryStride: Int { return _memoryStride() }
var memoryAlignment: Int { return _memoryAlignment() }
/// Creates a new AnyType wrapper from a given metatype.
/// The passed metatype's value **must** match its static value,
/// i.e `T.self == base` must be `true`.
init<T>(_ base: T.Type) {
precondition(T.self == base, """
The static value \(T.self) and dynamic value \(base) of the \
passed metatype do not match
"""
)
self.base = T.self
self._memorySize = { MemoryLayout<T>.size }
self._memoryStride = { MemoryLayout<T>.stride }
self._memoryAlignment = { MemoryLayout<T>.alignment }
}
}
You can then use it like so:
struct S {
var i: Int
var b: Bool
}
let types = [AnyType(UInt32.self), AnyType(UInt8.self), AnyType(S.self)]
for type in types {
print("Size of \(type.base): \(type.memorySize)")
print("Stride of \(type.base): \(type.memoryStride)")
print("Alignment of \(type.base): \(type.memoryAlignment)")
print()
}
// Size of UInt32: 4
// Stride of UInt32: 4
// Alignment of UInt32: 4
//
// Size of UInt8: 1
// Stride of UInt8: 1
// Alignment of UInt8: 1
//
// Size of S: 9
// Stride of S: 16
// Alignment of S: 8
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With