Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Size of Any.Type in swift

Tags:

swift

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.

like image 255
ullstrm Avatar asked Nov 07 '22 12:11

ullstrm


1 Answers

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
like image 57
Hamish Avatar answered Nov 15 '22 08:11

Hamish