> On Sep 18, 2016, at 6:21 AM, Palfi, Andras via swift-users 
> <swift-users@swift.org> wrote:
> 
> Workaround:
> Use 'Thunks'. These are type eraser structs, which implements the same 
> protocol. For each method they referencing (capturing) the original methods 
> and properties of the object. So 'thunks' are technically proxy objects. 
> 
> struct DirtyThunk<SomeElementType> : SomeProtocol {
> 
>    private let _someMethod : (SomeElementType) -> Void
> 
>    init<D : SomeProtocol>(delegate : D) where D.ElementType==SomeElementType {
>        _someMethod = delegate.someMethod
>    }
> 
>    func someMethod(withElement: SomeElementType) {
>        _someMethod(withElement)
>    }
> }
> 
> the subscribe method will be modified:
> func subscribe<D : SomeProtocol>(delegate : D) where 
> D.ElementType==SomeElementType { // would be better without generics as this 
> generates as many methods as many different types used to call it
>        let thunk = DirtyThunk(delegate)
>        delegates.append(thunk)
>    }
> 
> This solution works - however we can never retrieve the original object any 
> more as it is not referenced. The implementation of the “thunks” are also 
> painful a bit. The methods are captured only by the name of the methods 
> without the parameters so leads the problem if different methods have the 
> same prefix.
> 
> I tried to solve using ‘Any’ to reference the delegates but then cannot cast 
> to a proper type to call them.
> 
> 
> Do one know any better solution?

There's a more complex way to write a "type-erased wrapper" (the preferred 
Swift term for a thunk). The trick is that you use a base class which is *not* 
generic on the actual type, plus a derived class which *is* generic on the 
actual type, to hold the actual instance. For example:

        private class SomeProtocolBox<SomeElementType> {
                private init() {}
                
                func someMethod(withElement: SomeElementType) { fatalError() }
        }
        
        private class ConcreteSomeProtocolBox<SomeProtocolType: SomeProtocol>: 
SomeProtocolBox<SomeProtocolType.ElementType> {
                var value: SomeProtocolType
                
                init(_ value: SomeProtocolType) {
                        self.value = value
                }
                
                override func someMethod(withElement: SomeElementType) {
                        value.someMethod(withElement: withElement)
                }
        }
        
        public struct AnySomeProtocol<SomeElementType>: SomeProtocol {
                public typealias ElementType = SomeElementType
                
                private var box: SomeProtocolBox<SomeElementType>
                
                public init<T: SomeProtocol>(_ value: T) where T.ElementType == 
SomeElementType {
                        box = ConcreteSomeProtocolBox(value)
                }
                
                func someMethod(withElement: SomeElementType) {
                        box.someMethod(withElement: withElement)
                }
        }

With this in place, it's not difficult to support recovering the original 
instance as an `Any`:

        extension SomeProtocolBox {
                var base: Any { fatalError() }
        }
        extension ConcreteSomeProtocolBox {
                override var base: Any { return value }
        }
        extension AnySomeProtocol {
                var base: Any { return box.base }
        }

It's also more efficient than the closure-based solution, since it doesn't need 
a separate closure (and context) for each method it indirects.

There's some hope that eventually all of this will be unnecessary and you'll be 
able to use protocols with associated types in more places, possibly with some 
special casting syntax to make sure you're passing the proper type. No idea if 
or when that will actually be implemented, though.

-- 
Brent Royal-Gordon
Architechies

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Reply via email to