如何扩展float3或任何其他内置类型以符合Codable协议?

Las*_*ser 4 initialization scenekit swift swift4 codable

在尝试使用基本JSONEncoder序列化float3对象数组时,发现float3不符合Codable协议,因此无法完成.

我尝试按照下面的编码和解码自定义类型的建议编写一个基本扩展,但是会'self' used before all stored properties are initialized在init中为每个赋值行呈现错误.我假设这是因为编译器不确定Float.self是否在float3初始化之前定义,但我不知道如何解决这个问题.

此外,init的结尾说Return from initializer without initializing all stored properties我假设除了x,y和z之外还有float3属性,但我想知道是否有办法默认/忽略这些,和/或如何找到完整除了挖掘simd中的所有float3扩展之外的属性列表.

如果您对如何做到这一点有任何想法,那么分享它们将非常感激.谢谢!

import SceneKit

extension float3: Codable {
    public init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        x = try values.decode(Float.self, forKey: .x)
        y = try values.decode(Float.self, forKey: .y)
        z = try values.decode(Float.self, forKey: .z)
    }

    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(x, forKey: .x)
        try container.encode(y, forKey: .y)
        try container.encode(z, forKey: .z)
    }

    enum CodingKeys: String, CodingKey {
        case x
        case y
        case z
    }
}
Run Code Online (Sandbox Code Playgroud)

这是simd文件中的(我认为完整的)float3定义:

/// A vector of three `Float`.  This corresponds to the C and
/// Obj-C type `vector_float3` and the C++ type `simd::float3`.
public struct float3 {

    public var x: Float

    public var y: Float

    public var z: Float

    /// Initialize to the zero vector.
    public init()

    /// Initialize a vector with the specified elements.
    public init(_ x: Float, _ y: Float, _ z: Float)

    /// Initialize a vector with the specified elements.
    public init(x: Float, y: Float, z: Float)

    /// Initialize to a vector with all elements equal to `scalar`.
    public init(_ scalar: Float)

    /// Initialize to a vector with elements taken from `array`.
    ///
    /// - Precondition: `array` must have exactly three elements.
    public init(_ array: [Float])

    /// Access individual elements of the vector via subscript.
    public subscript(index: Int) -> Float
}

extension float3 : Equatable {

    /// True iff every element of lhs is equal to the corresponding element of
    /// rhs.
    public static func ==(lhs: float3, rhs: float3) -> Bool
}

extension float3 : CustomDebugStringConvertible {

    /// Debug string representation
    public var debugDescription: String { get }
}

extension float3 : ExpressibleByArrayLiteral {

    /// Initialize using `arrayLiteral`.
    ///
    /// - Precondition: the array literal must exactly three
    ///   elements.
    public init(arrayLiteral elements: Float...)
}

extension float3 : Collection {

    /// The position of the first element in a nonempty collection.
    ///
    /// If the collection is empty, `startIndex` is equal to `endIndex`.
    public var startIndex: Int { get }

    /// The collection's "past the end" position---that is, the position one
    /// greater than the last valid subscript argument.
    ///
    /// When you need a range that includes the last element of a collection, use
    /// the half-open range operator (`..<`) with `endIndex`. The `..<` operator
    /// creates a range that doesn't include the upper bound, so it's always
    /// safe to use with `endIndex`. For example:
    ///
    ///     let numbers = [10, 20, 30, 40, 50]
    ///     if let index = numbers.index(of: 30) {
    ///         print(numbers[index ..< numbers.endIndex])
    ///     }
    ///     // Prints "[30, 40, 50]"
    ///
    /// If the collection is empty, `endIndex` is equal to `startIndex`.
    public var endIndex: Int { get }

    /// Returns the position immediately after the given index.
    ///
    /// The successor of an index must be well defined. For an index `i` into a
    /// collection `c`, calling `c.index(after: i)` returns the same index every
    /// time.
    ///
    /// - Parameter i: A valid index of the collection. `i` must be less than
    ///   `endIndex`.
    /// - Returns: The index value immediately after `i`.
    public func index(after i: Int) -> Int
}

extension float3 {

    /// Vector (elementwise) sum of `lhs` and `rhs`.
    public static func +(lhs: float3, rhs: float3) -> float3

    /// Vector (elementwise) difference of `lhs` and `rhs`.
    public static func -(lhs: float3, rhs: float3) -> float3

    /// Negation of `rhs`.
    prefix public static func -(rhs: float3) -> float3

    /// Elementwise product of `lhs` and `rhs` (A.k.a. the Hadamard or Schur
    /// vector product).
    public static func *(lhs: float3, rhs: float3) -> float3

    /// Scalar-Vector product.
    public static func *(lhs: Float, rhs: float3) -> float3

    /// Scalar-Vector product.
    public static func *(lhs: float3, rhs: Float) -> float3

    /// Elementwise quotient of `lhs` and `rhs`.
    public static func /(lhs: float3, rhs: float3) -> float3

    /// Divide vector by scalar.
    public static func /(lhs: float3, rhs: Float) -> float3

    /// Add `rhs` to `lhs`.
    public static func +=(lhs: inout float3, rhs: float3)

    /// Subtract `rhs` from `lhs`.
    public static func -=(lhs: inout float3, rhs: float3)

    /// Multiply `lhs` by `rhs` (elementwise).
    public static func *=(lhs: inout float3, rhs: float3)

    /// Divide `lhs` by `rhs` (elementwise).
    public static func /=(lhs: inout float3, rhs: float3)

    /// Scales `lhs` by `rhs`.
    public static func *=(lhs: inout float3, rhs: Float)

    /// Scales `lhs` by `1/rhs`.
    public static func /=(lhs: inout float3, rhs: Float)
}
Run Code Online (Sandbox Code Playgroud)

Dáv*_*tor 9

您可以解决编译器错误,而不是尝试直接将解码值分配给您的类型的字段,将解码的值存储在局部变量中,然后调用指定的初始值float3.

作为罗布在提到他的答案,这个问题的原因有做x,y并且z要计算性能,而不是存储的,所以他们不能在初始化过程中被直接写入.

extension float3: Codable {
    public init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        let x = try values.decode(Float.self, forKey: .x)
        let y = try values.decode(Float.self, forKey: .y)
        let z = try values.decode(Float.self, forKey: .z)
        self.init(x, y, z)
    }

    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(x, forKey: .x)
        try container.encode(y, forKey: .y)
        try container.encode(z, forKey: .z)
    }

    private enum CodingKeys: String, CodingKey {
        case x,y,z
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以使用以下代码在Playground中测试编码和解码:

let vector = float3(3, 2.4, 1)
do {
    let encodedVector = try JSONEncoder().encode(vector)
    let jsonVector = String(data: encodedVector, encoding: .utf8) //"{"x":3,"y":2.4000000953674316,"z":1}"
    let decodedVector = try JSONDecoder().decode(float3.self, from: encodedVector) //float3(3.0, 2.4, 1.0)
} catch {
    print(error)
}
Run Code Online (Sandbox Code Playgroud)

如果您更喜欢更简洁的代码,该init(from decoder:)方法可以缩短为:

public init(from decoder: Decoder) throws {
    let values = try decoder.container(keyedBy: CodingKeys.self)
    try self.init(values.decode(Float.self, forKey: .x), values.decode(Float.self, forKey: .y), values.decode(Float.self, forKey: .z))
}
Run Code Online (Sandbox Code Playgroud)


Rob*_*ier 5

D\xc3\xa1vid 关于如何解决该问题是正确的,但值得理解为什么它是一个问题(它实际上不是指定的与便利初始化器;这只适用于类)。

\n\n

如果我们创建了自己的版本float3,您的代码可以与扩展一起正常工作:

\n\n
struct float3 {\n    var x: Float\n    var y: Float\n    var z: Float\n}\n\nextension float3: Codable {\n    init(from decoder: Decoder) throws {\n        let values = try decoder.container(keyedBy: CodingKeys.self)\n        x = try values.decode(Float.self, forKey: .x)\n        y = try values.decode(Float.self, forKey: .y)\n        z = try values.decode(Float.self, forKey: .z)\n    }\n    // ...\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

所以这看起来很奇怪。为什么我们的类型实现simd与类型的工作方式不同simd?因为simd没有x存储属性(或yz)。这些都是计算属性。

\n\n

真正的接口定义在simd.swift.gyb. 如果您研究该代码,您将看到所有 SIMD 向量类型都使用通用_vector存储:

\n\n
public var _vector: Builtin.${llvm_vectype}\n
Run Code Online (Sandbox Code Playgroud)\n\n

component然后为每个字母 ( is )定义计算属性[\'x\',\'y\',\'z\',\'w\']

\n\n
% for i in xrange(count):\n  public var ${component[i]} : ${scalar} {\n    @_transparent\n    get {\n      let elt = Builtin.${extractelement}(_vector,\n        (${i} as Int32)._value)\n\n      return ${scalar}(_bits: elt)\n    }\n    @_transparent\n    set {\n      _vector = Builtin.${insertelement}(_vector,\n        newValue._value,\n        (${i} as Int32)._value)\n    }\n  }\n% end\n
Run Code Online (Sandbox Code Playgroud)\n\n

因此,如果我们构建自己的float3(没有花哨的内置函数),它会是这样的:

\n\n
struct float3 {\n    private var vector: [Float]\n    var x: Float { get { return vector[0] } set { vector[0] = newValue } }\n    var y: Float { get { return vector[1] } set { vector[1] = newValue } }\n    var z: Float { get { return vector[2] } set { vector[2] = newValue } }\n    init(x: Float, y: Float, z: Float) {\n        vector = [x, y, z]\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果您针对此编写扩展,您将得到相同的错误。

\n