Arithmeti c с обобщениями и протоколами - PullRequest
1 голос
/ 08 мая 2020

Привет всем,

Я работал над системой Node последние несколько ночей и наткнулся на небольшой блокпост (опять же XD)

Ниже большой кусок кода, который обрабатывает заглушки (которые могут состоять из различных типов данных) и узлов, которые содержат заглушки и могут что-то делать с данными из заглушек. Он был скопирован с игровой площадки, поэтому должен "работать", если вставить в него.

В настоящее время я застрял на функции Execution в AddNode , что могло бы получить доступ к данным из двух разъемов, сложить их вместе, а затем установить эти данные на выходной разъем. Узел должен иметь возможность обрабатывать создание экземпляров с различными типами, поэтому он использует Generics.

Вы можете увидеть кучу закомментированных строк, поскольку я пробовал другой сценарий ios. Я думал, что попробую сохранить Generi c Type, а затем смогу использовать это для преобразования значения и затем сложить их вместе, но похоже, что это не работает.

Я все еще очень новичок в мир Swift, это мой проект «Испытай огонь и учись». Если вы заметили какой-либо плохой / вонючий код или что-то, что можно было бы сделать лучше, пожалуйста, упомяните об этом. сделали лучше, или у вас есть лучшее решение.


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

Еще раз спасибо за внимание,

С уважением, Саймон

...