Swift 执行图:协议 + 泛型僵局

问题描述 投票:0回答:1

我正在尝试创建一个由节点组成的通用非循环执行图。每个节点有 0 个或多个输入和输出。每个输入和输出都针对特定对象类型(Double、UIImage、String 等)进行类型化。我希望只能以一对一的关系(无复用)连接具有匹配对象类型的输入和输出。一切都正常,直到我尝试创建 Connection 对象,该对象表示不同节点上的特定输入和输出之间存在连接。以下是我的类型的简化版本:

import UIKit

protocol NodeOutput {
    associatedtype Entity
    
    var type: Entity.Type { get }
    var value: Entity { get }
}

protocol NodeInput {
    associatedtype Entity
        
    var type: Entity.Type { get }
    func set(value: Entity)
}

protocol Node {
    var inputs: [any NodeInput] { get }
    var outputs: [any NodeOutput] { get }
}

struct GenericConnection<Input: NodeInput, Output: NodeOutput> where Input.Entity == Output.Entity {
    
    public let input: Input
    public let output: Output
    
    /// Create a new connection object.
    public init(input: Input, output: Output) throws {
        self.input = input
        self.output = output
        
        input.set(value: output.value)
    }
    
    public func activate() throws {
        input.set(value: output.value)
    }
}

// Actual Implementations

class SimpleNodeInput<Entity>: NodeInput {
    
    public var type: Entity.Type { Entity.self }
    public private(set) var value: Entity
    
    public init(value: Entity) {
        self.value = value
    }
    
    public func set(value: Entity) {
        self.value = value
    }
}

class SimpleNodeOutput<Entity>: NodeOutput {
    public var type: Entity.Type { Entity.self }
    public var value: Entity
    
    public init(value: Entity) {
        self.value = value
    }
}

class ConstantColorNode: Node {
    let inputs: [any NodeInput] = []
    private(set) var outputs: [any NodeOutput] = []
    
    public private(set) var color: UIColor {
        didSet {
            _colorOutput.value = color
        }
    }
    
    private var _colorOutput: SimpleNodeOutput<UIColor>
    var colorOutput: any NodeOutput { _colorOutput as any NodeOutput }
    
    public init(color: UIColor) {
        self.color = color
        _colorOutput = SimpleNodeOutput(value: color)
        self.outputs = [colorOutput]
    }
    
    func set(color: UIColor) {
        self.color = color
    }
}

class FinalOutputNode<T>: Node {
    private(set) var inputs: [any NodeInput] = []
    let outputs: [any NodeOutput] = []
    
    var finalInput: any NodeInput
    
    init(initialValue: T) {
        
        finalInput = SimpleNodeInput(value: initialValue)
        self.inputs = [finalInput]
    }
}

let colorNode = ConstantColorNode(color: .red)
let outputNode = FinalOutputNode<UIColor>(initialValue: .blue)
let genericConnection = try GenericConnection(input: outputNode.finalInput, output: colorNode.colorOutput)

所以... Connection 类型有一个编译器错误,即

set
不能在类型
any NodeInput
上使用。 GenericConnection 类型不会出现编译器错误,直到您尝试实例化它,然后您会收到错误:
any NodeInput
不符合
NodeInput
any NodeOutput
不符合
NodeOutput

我真的不确定如何对需要限制为具有相同泛型类型的节点之间的连接进行建模。作为记录,我有多个输入和输出节点类型,它们可以执行诸如发出常量值或查询委托的类型和值之类的操作。只是不确定如何正确地做到这一点,或者是否可以优雅地表达而不需要制作所有数据

Any
然后进行运行时类型检查(这看起来超级恶心)。

swift generics graph protocols
1个回答
0
投票

您可以使用主要关联类型

protocol NodeOutput<Entity> {
    associatedtype Entity
    
    var type: Entity.Type { get }
    var value: Entity { get }
}

protocol NodeInput<Entity> {
    associatedtype Entity
    
    var type: Entity.Type { get }
    func set(value: Entity)
}

protocol Node<Entity> {
    associatedtype Entity
    var inputs: [any NodeInput<Entity>] { get }
    var outputs: [any NodeOutput<Entity>] { get }
}

struct GenericConnection<T> {
    
    public let input: any NodeInput<T>
    public let output: any NodeOutput<T>
    
        /// Create a new connection object.
    public init(input: any NodeInput<T>, output: any NodeOutput<T>) throws {
        self.input = input
        self.output = output
        
        input.set(value: output.value)
    }
    
    public func activate() throws {
        input.set(value: output.value)
    }
}

    // Actual Implementations

class SimpleNodeInput<Entity>: NodeInput {
    
    public var type: Entity.Type { Entity.self }
    public private(set) var value: Entity
    
    public init(value: Entity) {
        self.value = value
    }
    
    public func set(value: Entity) {
        self.value = value
    }
}

class SimpleNodeOutput<Entity>: NodeOutput {
    public var type: Entity.Type { Entity.self }
    public var value: Entity
    
    public init(value: Entity) {
        self.value = value
    }
}

class ConstantColorNode: Node {
    let inputs: [any NodeInput<UIColor>] = []
    private(set) var outputs: [any NodeOutput<UIColor>] = []
    
    public private(set) var color: UIColor {
        didSet {
            _colorOutput.value = color
        }
    }
    
    private var _colorOutput: SimpleNodeOutput<UIColor>
    var colorOutput: any NodeOutput<UIColor> { _colorOutput as any NodeOutput<UIColor> }
    
    public init(color: UIColor) {
        self.color = color
        _colorOutput = SimpleNodeOutput(value: color)
        self.outputs = [colorOutput]
    }
    
    func set(color: UIColor) {
        self.color = color
    }
}

class FinalOutputNode<T>: Node {
    private(set) var inputs: [any NodeInput<T>] = []
    let outputs: [any NodeOutput<T>] = []
    
    var finalInput: any NodeInput<T>
    
    init(initialValue: T) {
        
        finalInput = SimpleNodeInput(value: initialValue)
        self.inputs = [finalInput]
    }
}

struct ConnectionBundle {
    public let input: any NodeInput
    public let output: any NodeOutput
    
    init(input: any NodeInput, output: any NodeOutput) {
        self.input = input
        self.output = output
    }
}


let colorNode = ConstantColorNode(color: .red)
let outputNode = FinalOutputNode<UIColor>(initialValue: .blue)
let genericConnection = try GenericConnection(input: outputNode.finalInput, output: colorNode.colorOutput)
© www.soinside.com 2019 - 2024. All rights reserved.