问题
Ahoy Everyone,
I have been working on a Node system for the last few nights, and have hit a bit of a road block (again XD)
Below is a large chunk of code that handles Plugs ( which can be made up of different data types), and Nodes which hold the plugs, and can do something with the data from the plugs. It was copied out of a playground, so should "work" if pasted into one.
I am currently stuck at the execution function in the AddNode, which would access the data from two plugs, add them together and then set that data to the output plug. The Node should be able to handle being instantiated with different types, So it uses Generics.
You can see a bunch of commented out lines as I have been trying different scenarios. I thought I would try and store the Generic Type, and then could use that to cast the value and then add them together, but it seems that does not work.
I am still very new to the world of Swift, this is my trial by fire and learn project. If you notice any bad/smelly code, or something that could be done better please do mention it.
Please ignore some of the comment ramblings, as I code I hit scenarios and wonder if they could be done better, or have a better solution.
protocol PlugValue {
init()
}
extension Int: PlugValue { }
extension Float: PlugValue { }
extension Double: PlugValue { }
extension SIMD3: PlugValue where Scalar == Int32 { }
//extension Array: PlugValue {}
extension Array: PlugValue where Element: PlugValue {}
class Plug<Value: PlugValue> {
public var value: Value
public var name : String
init(_ value: Value) {
self.value = value
self.name = "un-named"
}
init(_ value:Value, name:String){
self.name = name
self.value = value
}
}
protocol AnyPlug:class {
var anyValue: PlugValue { get set}
var name: String { get set }
}
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 {
get {
value
}
set(v) {
value = v as! Value
}
}
//var anyValue: PlugValue { value }
}
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)
}
}
// MARK:-
class Node{
var plugs: [AnyPlug]
var name: String
init(_ name:String) {
self.name = name
plugs = []
}
func addPlug<genPlug:AnyPlug>(_ plug: genPlug){
// checks the plug name doesn't exist in the list, unique names
self.validatePlug(plug)
// Adds plug only if it doesn't exist in the list already
var found = false
for inPlg in self.plugs {
if plug === inPlg {
found = true
}
}
if !found{
self.plugs.append(plug)
}
}
func getPlug(name:String) -> [AnyPlug]{
var matchs: [AnyPlug] = []
for plg in self.plugs {
if plg.name == name { matchs.append(plg) }
}
return matchs
}
public func setPlugName<genPlug:AnyPlug>(plug: genPlug, name:String) -> String{
var newName = name
// flag to store if a name collision is found, start true, to initialise the first loop
var nameCollision = true
while nameCollision {
nameCollision = false
for existing_plg in self.plugs{
// if the names match, and makes sure one is not comparing the plug to itself.
if existing_plg.name == newName && plug !== existing_plg {
nameCollision = true
newName = nameIncrement(name: newName)
}
}
}
plug.name = newName
return newName
}
/// Checks that the plug is valid. At the moment this means checking no name collisions
private func validatePlug<genPlug:AnyPlug>(_ plug: genPlug){
_ = self.setPlugName(plug: plug, name: plug.name)
}
}
class AddNode<numericType:PlugValue> : Node{
var acceptedType:numericType.Type
var out:Plug<numericType>?
public override required init(_ name:String="AddNode"){
self.acceptedType = numericType.self
super.init(name)
self.initPlugs()
}
func initPlugs() {
let default_value:numericType
if numericType.self == Double.self{
default_value = 0.0 as! numericType
}
else
if numericType.self == Float.self{
default_value = Float(0.0) as! numericType
}
else { default_value = 0 as! numericType}
// Input plugs
let plg_in_1 = Plug(default_value, name:"value_1" )
let plg_in_2 = Plug(default_value, name:"value_2" )
// Output plugs
let plg_out_1 = Plug(default_value, name:"result")
plugs.append(plg_in_1)
plugs.append(plg_in_2)
self.out = plg_out_1
}
func execute() {
var sum_value:numericType? = nil
for plg in self.plugs{
let plgVal = plg as! Plug<numericType>
if sum_value == nil{
sum_value = plgVal.value
}
else{
let tValue = plgVal[numericType.self]
numericType() + acceptedType.init(1.0)
0.0
//numericType.Type
numericType.self
let ttValue: numericType = plgVal()
numericType.self
acceptedType.self
tValue
ttValue
plg[numericType]
//tValue + 1.0
//sum_value = sum_value + plgVal.value
//sum_value =+ 2.0
//sum_value! + tValue
}
}
self.out!.value = sum_value!
}
}
/**
Performs an incrementation on a name
- Parameter name: The string that will have a number appended or incremented on the end of it.
- Returns: An incremented string
# Example:
~~~
"name" = "name_1"
"name_1" = "name_2"
*/
public func nameIncrement(name:String) -> String {
// check the name has a suffix '_#'
// if no suffix is found add one, if one is found
var new_name:String = name
if name.contains("_"){
var string_bits = name.split(separator: "_")
// the last suffix was not a number, so we can just add _1 to the end
if Int(string_bits.last!) == nil { new_name.append("_1") }
else
{
var index = Int(string_bits.last!) ?? 0
index += 1
string_bits.remove(at: string_bits.endIndex-1)
string_bits.insert(Substring(String(index)), at:string_bits.endIndex)
new_name = string_bits.joined(separator: "_")
}
}
else {
// name has no "_", so we append a 1 to the end of the name
new_name.append("_1")
}
return new_name
}
// MARK: IMPLEMENTATION USAGE
var newNode = Node("Dynamic Node")
var plg_1 = Plug(1, name:"index")
var plg_2 = Plug([2.2, 45.0, 90.1], name:"angles")
var plg_3 = Plug([0.0, 0.0, 0.0], name:"angles") // Name conflict
// Add plug to Node
newNode.addPlug(plg_1)
newNode.addPlug(plg_2)
newNode.addPlug(plg_3)
var tempPlug_1 = newNode.plugs[0]
var tempPlug_2 = newNode.plugs[2]
// How would I cast the AnyNode back into a Plug<Type>
// Is there a more straight forward way to do this, which doesn't require the creation of a new variable, and knowing the type
// Would it be possible to store the type of plug in the protocol and leverage that to cast back into a plug
// eg. var tp = tempPlug_1 as! Plug<Int>
// is there a way to hold a type in a variable so you can use it later to cast the generic type
/*
var magicType = Int // have it as a variable in the protocol?
var tp = tempPlug_1 as! Plug<magicType>
*/
var tp = tempPlug_1 as! Plug<Int>
tp === plg_1 // is true, so they are pointers to the same location, even though its a new variable and been cast
tp.value = 5
var sds = newNode.plugs[0][Int]
newNode.plugs[0].anyValue = 10
var sds2 = newNode.plugs[0]
plg_1.value = 20 // best case scenario
// testing purposes to change type easier for testing
typealias caster = Double
let addNode = AddNode<caster>("newNode")
addNode.name = "Add Node"
addNode.plugs[0].anyValue = caster(2.0)
addNode.plugs[1].anyValue = caster(4.0)
addNode.execute()
addNode.out
// instantiate a variable from the node type // investigation purposes
var testType = addNode.acceptedType.init(2)
testType + 2
Thanks again for taking a look,
Regards, Simon
来源:https://stackoverflow.com/questions/61675702/arithmetic-with-generics-and-protocols