简体   繁体   中英

Storing Generic Objects in a Heterogeneous Array and retrieving object parameter as the correct type

Ahoy everyone,

I have recently been trying to implement a Node based graph system that passes data between nodes using plugs. Similar to many of the 3D applications like houdini and maya.

I have written a similar system before using Python, and wanted to try this with Swift as my first learning excersise. Boy did I jump into the deep end on this one.

I am stuck now with Swifts Arrays, as I would like to store a list of Generic plugs. Each plug can have its own value type float, int, color, string, Vector Matrix.

I have read up about Type Erasers and opaque types, but still cant seem to get my values our of a list in a way that I can perform some arithmetic on them.

All and any help that might put me in the direction would be greatly appreciated: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
    }
}

Thanks everyone

I managed to find a solution that worked for my needs.

I am still looking at finding a better way to handle getting the data and their correct type.

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))

Use a generic something to extract what you need. Your options are methods and subscripts.

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

With the array of protocols, do you have to down cast them into their Plug when retrieving them every time?

Essentially. This is true of all heterogenous sequences. Here are your options:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM