Accessing attributes stored in a generic list

Hi Everyone,

I am still pretty new to Swift and have been trying to implement a node graph, and have hit a wall.

I am trying to store a list of ports, that could be made up of different generic types. I have tried to implement it using Generics and protocols. Where I get stuck is how to I convert the anyObject or Any back into a Plug

Thanks for any assistance

Regards,

Simon

//———————————————————————————————

protocol genericPlug {
    associatedtype dataType
    var value:dataType? {set get}
}

class Plug<T>: genericPlug {
    typealias dataType = T
    var value:dataType?
    var name:String

    init (name:String, value:T){
        [self.name](http://self.name) = name
        self.value = value
    }
}

var plugs: [AnyObject] = []
// I have also tried with [genericPlug]

var plug_1 = Plug(name:"input", value:0)
var plug_2 = Plug(name:"output", value:0)

plugs.append(plug_1)
plugs.append(plug_2)

var plug_3 = Plug(name:"input2", value:16.0)
plugs.append(plug_3)

var doublePlg = Plug(name:"a_plug", value:[2.1, 3.0])
var intListPlg = Plug(name:"array_plug", value:[2, 1, 34])

plugs.append(doublePlg)
plugs.append(intListPlg)

intListPlg.value
doublePlg.value

plugs[3].value[0] + plugs[2].value

So it's going to be hard to do exactly what you're trying to do here, because by putting all your plugs into an array, you will necessarily lose information about what each plug's particular dataType is. And that means when you get to this line:

plugs[3].value[0] + plugs[2].value

the compiler has no way of knowing what type plugs[3].value[0] and plugs[2].value are. For instance, for all the compiler knows, plugs[2].value is could be a String and plugs[3].value[0] could be a UInt32.

So the quick-and-dirty answer to this is, if you want to preserve type information in a group of objects, you could use a tuple:

let plugs = (
    plug1,
    plug2,
    plug3,
    doublePlg,
    intListPlg
)

plugs.3.value[0] + plugs.2.value

This will allow the compiler to know at build time exactly what the value of each of the plugs are.

If the list has to be dynamic, you could also cast the plugs as they come out of your array:

var plugs: [Any] = []
...
// check if each plug is the type you expect at runtime
if let plug3 = plugs[3] as? Plug<[Double]>, let plug2 = plugs[2] as? Plug<Double> {
     plug3.value[0] + plug2.value
}

But converting things to Any and then casting them back to specific types has a bit of code-smell in Swift.

A better solution would probably be that, if you have something consistent you have to do with each of these plugs, put that inside a protocol without an associated type constraint, and then you can use an array of [GenericPlug] without issues.

1 Like

Thank you for your help Spencer, I never knew you could do that with a tuple and the compiler would allow for mixed types.

What I am trying to achieve is having a Node class that stores an array of input and output plugs/ports. Every node has an execute function which can perform some math on the inputs values of the plugs and then set an output plugs value, with the result.

The array of plugs would need to be dynamic as custom nodes can be created.

Would it be possible to have this type of array?

below is a very rough description of what I am trying to achieve. was writen on the fly, so it will not compile, but shows what I am trying to achieve.

Different nodes will have a variety of different inputs and outputs, and its up to the dev to specify in the execute how the data is used and returned.

class Node{
   var in_plugs:[Plug] = []
   var out_plugs:[Plug] = []

  func addPlug(plg:Plug){
     plugs.append(plg)
  }

  func execute(){
    var _sumValue = 0
    for plg in self.plugs {
      _sumValue += plg.value
    }
  out_plugs[0].value = _sumValue
  }
}

Yeah so if this is what you're trying to do, the easiest way would be to have a protocol with a consistent type for value and then each of the conforming types would implement the conversion. So for example something like:

protocol Plug {
    var value: Double { get, set }
}

struct IntPlug {
    var _value: Int
    var value: Double {
        get { return Double(_value) }
        set { _value = Int(newValue) }
    }
}

struct DoublePlug {
    var value: Double
}

struct DoubleArrayPlyg {
    var _values: [Double]
    var value: Double {
        get { return _values.reduce(0, +) }
        set { _values = [newValue] }
    }
}

There are other ways you could do it, but this might be a fairly simple solution.

Thanks Spencer.

That will definitely help me with the start.

What would you suggest as the not so simple solution?
As there will be custom types that I would like to store in a plug.
Example: Matrix3x3, Matrix4x4, Vector3, Vector4, Color and a variety of others.