[swift-users] Still can't derive from a generic class

Joe Groff jgroff at apple.com
Thu Aug 31 10:50:38 CDT 2017


> On Aug 30, 2017, at 11:17 AM, Joanna Carter via swift-users <swift-users at swift.org> wrote:
> 
> Hi Kenny
> 
>> Just curious, and because I have a distinct lack of imagination: can you share a concrete case of this pattern?

This is likely due to the runtime not handling cycles properly in type metadata initialization—a subclass T needs its base class metadata for BaseObject<T>, which in turn needs the metadata for T to instantiate the generic type. This is something we plan to fix next year since it requires runtime ABI changes. For the particular pattern you've described:

> class BaseObject<rootType>
> {
>  private let properties: [PartialKeyPath<rootType> : AnyProperty]
> 
>  init(properties: [PartialKeyPath<rootType> : AnyProperty])
>  {
>    self.properties = properties
>  }
> 
>  func value<valueType>(for keyPath: KeyPath<rootType, valueType>) -> valueType?
>  {
>    guard let property = properties[keyPath] else
>    {
>      return nil
>    }
> 
>    return property.getValue()
>  }
> 
>  func set<valueType>(value: valueType?, for keyPath: KeyPath<rootType, valueType>)
>  {
>    guard let property = properties[keyPath] else
>    {
>      return
>    }
> 
>    property.set(value: value)
>  }
> }
> 
> class Test : BaseObject<Test>
> {
>  var name: String?
>  {
>    get
>    {
>      return value(for: \.name)!
>    }
>    set
>    {
>      set(value: newValue, for: \.name)
>    }
>  }
> 
>  var number: Int?
>  {
>    get
>    {
>      return value(for: \.number)!
>    }
>    set
>    {
>      set(value: newValue, for: \.number)
>    }
>  }
> 
>  init()
>  {
>    super.init(properties: [\Test.name : Property<String>(), \Test.number : Property<Int>()])
>  }
> }
> 
> Without the generic rootType parameter, all the getter and setter methods need an explicit mention of the derived class for the keypath :
> 
> class Test : BaseObject
> {
>  var name: String?
>  {
>    get
>    {
>      return value(for: \Test.name)!
>    }
>    set
>    {
>      set(value: newValue, for: \Test.name)
>    }
>  }
> 
>  …
> }

Would a protocol-based approach suffice? `Self` in a protocol extension would give you access to the concrete type in a similar way:

protocol Base: AnyObject {
  var properties:  [PartialKeyPath<Self> : AnyProperty] { get }
}

extension Base {
  func value<T>(for keyPath: KeyPath<Self, T>) -> T?
  {
    guard let property = properties[keyPath] else
    {
      return nil
    }

    return property.getValue()
  }

  /*etc.*/
}

class Test: Base {
  let properties = [\Test.name: Property<String>()]

  var name: String {
    get { return value(for: \.name) }
    set { set(value: newValue, for: \.name) }
  }
}

-Joe


More information about the swift-users mailing list