将通用对象存储在异构数组中,并将对象参数检索为正确的类型。

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

大家好

我最近一直在尝试实现一个基于节点的图形系统,使用插头在节点之间传递数据。类似于很多3D应用,比如houdini和maya。

我之前用Python写过一个类似的系统,所以想用Swift作为我的第一个学习练习。天哪,我在这个问题上跳进了深渊。

我现在被Swift的数组卡住了,因为我想存储一个Generic plugs的列表.每个plug可以有自己的值类型float, int, color, string, Vector Matrix。

我已经阅读了关于类型擦除器和不透明类型的文章,但似乎仍然不能让我的值以一种可以对它们进行运算的方式从列表中获得。

所有的和任何帮助,可能会把我的方向将是非常感激的:D。

import Foundation
import MetalKit

protocol genericPlug {
    associatedtype T
    func GetValue() -> T
}


class Plug<T>:genericPlug{
    var _value:T?
    var value:T {
        get{GetValue()}
        set(val){
            value = val
        }
    }

    func GetValue() -> T{
        return _value!
    }

    init(_ newValue:T){
        _value=newValue
    }
}

class Node{
    var plugs:[genericPlug] = []
    init(){
        var p1 = Plug<Int>(0)
        var p2 = Plug(vector2(1.2, 3.1))
        var p3 = Plug([0.0, 3.1, 0.6, 1])

        plugs.append(p1)
        plugs.append(p2)
        plugs.append(p3)
    }

    func execute(){
        // will access the plugs in the array and perform some sort of calculations on them.
        plugs[0].value + 1      // should equal 1
        plugs[1].value.x + 0.8  // should have x=2.0 y=3.1
        plugs[2].value[1] - 0.1 // should equal 3.0
    }
}

谢谢大家

swift generics heterogeneous-array
1个回答
0
投票

使用一个通用的东西来提取你需要的东西。你的选择是方法和子程序。

protocol PlugValue {
  init()
}

extension Int: PlugValue { }
extension Float: PlugValue { }
extension Double: PlugValue { }
extension SIMD3: PlugValue where Scalar == Int32 { }
struct Plug<Value: PlugValue> {
  var value: Value

  init(_ value: Value) {
    self.value = value
  }
}
protocol AnyPlug {
  var anyValue: PlugValue { get }
}

extension AnyPlug {
  subscript<Value: PlugValue>(type: Value.Type = Value.self) -> Value {
    anyValue as? Value ?? .init()
  }

  func callAsFunction<Value: PlugValue>(_ type: Value.Type = Value.self) -> Value {
    anyValue as? Value ?? .init()
  }
}

extension Plug: AnyPlug {
  var anyValue: PlugValue { value }
}
let plugs: [AnyPlug] = [
  Plug(1),
  Plug(2.3 as Float),
  Plug(4.5),
  Plug([6, 7, 8] as SIMD3)
]

plugs[0][Int.self] // 1
plugs[1][Double.self] // 0
plugs[1][] as Float // 2.3
let double: Double = plugs[2]() // 4.5
plugs[3](SIMD3.self).y // 7

随着协议的数组,你是否每次检索它们时都要把它们下投到它们的Plug里?

基本上是这样的。所有的异构序列都是如此。这里是你的选择。

extension Array: PlugValue where Element: PlugValue { }

let plug: AnyPlug = Plug([0.1, 1.1, 2.1])
(plug as? Plug<[Double]>)?.value[1]
(plug.anyValue as? [Double])?[1]
extension Plug {
  enum Error: Swift.Error {
    case typeMismatch
  }
}

extension AnyPlug {
  func callAsFunction<Value: PlugValue, Return>(_ closure: (Value) -> Return) throws {
    guard let value = anyValue as? Value
    else { throw Plug<Value>.Error.typeMismatch }

    closure(value)
  }
}

try plug { (doubles: [Double]) in doubles[1] } // 1.1
try plug { ($0 as [Double])[1] } // 1.1
try plug { $0 as Int } // <Swift.Int>.Error.typeMismatch

0
投票

我找到了一个适合我需要的解决方案。

我还在寻找更好的方法来处理获取数据和它们的正确类型。

import Foundation
import MetalKit

// Creating the PlugType Enum
enum PlugType{
    case Integer(Int?)
    case Float_(Float?)
    case Double_(Double?)
    case Vector3(simd_int3)

    // default types
    static func IntegerType() -> PlugType{ return PlugType.Integer(nil)}
    static func FloatType() -> PlugType{ return PlugType.Float_(nil)}
    static func DoubleType() -> PlugType{ return PlugType.Double_(nil)}
}

// Implements a way to retrieve the correct value type
extension PlugType{
    var IntegerValue: Int{
        switch self{
            case .Integer(let value):
                return value ?? 0
            default:
                return 0
        }
    }

    var FloatValue: Float{
        switch self
        {
        case .Float_(let value):
            return value ?? 0
        default:
            return 0
        }
    }

    var DoubleValue: Double{
        switch self
        {
        case .Double_(let value):
            return value ?? 0
        default:
            return 0
        }
    }
}

// Get the string representation of the PlugType
extension PlugType {
    var typeName: String{
        switch self {
        case .Integer: return "Integer"
        case .Float_: return "Float"
        case .Double_: return "Double"
        case .Vector3: return "Vector3"
        }
    }

    var swiftType: Any.Type {
        switch self {
        case .Integer: return Int.self
        case .Float_: return Float.self
        case .Double_: return Double.self
        case .Vector3: return simd_int3.self
        }
    }
}

class Plug{
    var _value:PlugType?
    var type:String? { get{ return _value?.typeName } }

    init(_ newValue:PlugType){
        _value = newValue
    }

    func geee<T>(_ input:T) -> T{
        switch type {
            case "Integer":
                return getVal(_value!.IntegerValue) as! T
            case "Double":
                return getVal(_value!.DoubleValue) as! T
            default:
            return 0 as! T
        }

    }

    func getVal(_ val:Int) -> Int {
        return val
    }
    func getVal(_ val:Float) -> Float {
        return val
    }
    func getVal(_ val:Double) -> Double {
        return val
    }
}

var plugs:[Plug] = []
var p1 = Plug(PlugType.Integer(2))
© www.soinside.com 2019 - 2024. All rights reserved.