[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