arraysswiftswift5typecasting-operator

Assigning variable to a type in a function, how can I have the type itself be an input that will describe the type of a variable output? : Swift 5


I've made a file reading struct containing data, offsets and reading methods, (somehow fread() was confusing me).

I've gotten it working, and now want to implement a method into it that will take n x m matrix terms and output a matrix of values. (Similar to fread(FileID, [ n m ], "(datatype)") in matlab).

Here is my idea

// should be able to handle all sorts of datatypes and output n by m matrix.
    mutating func matRead( dim : [[Int]], dtype : Int ){
        if dim.count != 2{
            fatalError("Dimensions dont match \"n by m\" in matRead")
        }
        // make sure to preallocate with zeros.
        
        var mat_Out : typecast[dtype][2].self = []
        
    }

I have a typecasting dictionary that looks so

// typecast dictionary key : [ array_type, byte, element_type ]
let typecast : [Int16:[Any]] = [ 1: [ [UInt8].self  , 1, "uint8"],
                              2: [ [UInt16].self , 2, "uint16"],  // use .self to reference data type itself.
                              3: [ [UInt32].self , 4, "uint32"],
                              4: [ [Int8].self   , 1, "int8"  ],
                              5: [ [Int16].self  , 2, "int16" ],
                              6: [ [Int32].self  , 4, "int32" ],
                              7: [ [Float32].self, 4, "float32"],
                              8: [ [Float64].self, 8, "float64"],
                              12:[ [Int32].self  , 4, "int32" ]   ]

Heres the question: line 8 of the matRead() function doesn't work. Swift doesn't understand that I am trying to use the typecasting dictionary to assign to a new array type the output matrix. I've also tried "as" var mat_Out : Any = [] as typecast[dtype][0], also the same error (Swift compiler thinks I meant to put the brackets outside).

Alternatively I could go the long way and typecast manually based on the string values (already had to do this : see below fread struct code), but if there is another way it would be immensely time saving.


FileReading struct below ( redundant use of checking counts, but I haven't cleaned it up)

struct fRead {
    var off : Int = 0
    let data : Data
    
    mutating func resetOffsetToZero() {
        off = 0
    }
    
    mutating func setOffset( _ to : Int ) {
        off.self = to
    }
    mutating func moveOffset( _ by : Int) {
        off.self += by
    }
    func getOffset() -> Int {
        return off
    }
    // reading without specified number of elements defaults to 1.
    // these mutating funcs vary only in their output type and byte offset size.
    mutating func int32Read(count : Int = 1) -> [Int32] {
        var int32out : [Int32] = []
        if count > 1 {
            for i in 0...count-1 {
                int32out.append( Int32( data.subdata(in: off + i * 4..<(off + (i + 1) * 4) ).withUnsafeBytes{ $0.load(as: Int32.self )} ))
            }
            off += count * 4
        }
        else if count == 1 {
            int32out =  [ Int32( data.subdata(in: off..<(off+4) ).withUnsafeBytes{ $0.load(as: Int32.self )} )]
            off += 4
        }
        else if count == 0 {
            return []
        } else if count < 0{
            print("Warning, fReadint32Read( count : Int = 1) called with a negative count, returning empty array.")
        }
        return int32out
    }
    
    mutating func int16Read(count : Int = 1) -> [Int16] {
        var int16out : [Int16] = []
        if count > 1 {
            for i in 0...count-1 {
                int16out.append( data.subdata(in: off + i * 2..<(off + (i + 1) * 2) ).withUnsafeBytes{ $0.load(as: Int16.self )} )
            }
            off += count * 2
        }
        else if count == 1 {
            int16out =  [ data.subdata(in: off..<(off+2) ).withUnsafeBytes{ $0.load(as: Int16.self )} ]
            off += 2
        }
        else if count == 0 {
            return []
        } else if count < 0 {
            print("Warning, fRead.int16Read( count : Int = 1) called with a negative count, returning empty array.")
        }
        return int16out
    }
    
    mutating func float64Read(count : Int = 1) -> [Float64] {
        var float64out : [Float64] = []
        if count > 1 {
            for i in 0...count-1 {
                float64out.append( data.subdata(in: off + i * 8..<(off + (i + 1) * 8) ).withUnsafeBytes{ $0.load(as: Float64.self )} )
            }
            off += count * 8
        }
        else if count == 1 {
            float64out =  [ data.subdata(in: off..<(off+8) ).withUnsafeBytes{ $0.load(as: Float64.self )} ]
            off += 8
        }
        else if count == 0 {
            return []
        } else if count < 0 {
            print("Warning, fRead.int16Read( count : Int = 1) called with a negative count, returning empty array.")
        }
        return float64out
    }
    
    mutating func uint32Read(count : Int = 1) -> [UInt32] {
        var uint32out : [UInt32] = []
        if count > 1 {
            for i in 0...count-1 {
                uint32out.append( data.subdata(in: off + i * 4..<(off + (i + 1) * 4) ).withUnsafeBytes{ $0.load(as: UInt32.self )} )
            }
            off += count * 4
        }
        else if count == 1 {
            uint32out =  [ data.subdata(in: off..<(off+4) ).withUnsafeBytes{ $0.load(as: UInt32.self )} ]
            off += 4
        }
        else if count == 0 {
            return []
        } else if count < 0 {
            print("Warning, fRead.int16Read( count : Int = 1) called with a negative count, returning empty array.")
        }
        return uint32out
    }
    
    mutating func int64Read(count : Int = 1) -> [Int64] {
        var int64out : [Int64] = []
        if count > 1 {
            for i in 0...count-1 {
                int64out.append( Int64( data.subdata(in: off + i * 4..<(off + (i + 1) * 8) ).withUnsafeBytes{ $0.load(as: Int64.self )} ))
            }
            off += count * 8
        }
        else if count == 1 {
            int64out =  [ Int64( data.subdata(in: off..<(off+8) ).withUnsafeBytes{ $0.load(as: Int64.self )} )]
            off += 8
        }
        else if count == 0 {
            return []
        } else if count < 0{
            print("Warning, fReadint64Read( count : Int = 1) called with a negative count, returning empty array.")
        }
        return int64out
    }
    // should be able to handle all sorts of datatypes and output n by m matrix.
    mutating func matRead( dim : [[Int]], dtype : Int ){
        if dim.count != 2{
            fatalError("Dimensions dont match \"n by m\" in matRead")
        }
        // make sure to preallocate with zeros.
        
        var mat_Out : Any = [] as typecast[dtype][0]
        
    }
}

// I only discovered afterwards that 0...0 range works! So all the testing for  ==1 , == 0 wasn't necessary

Solution

  • Wouldn't a generic solution be the best way forward here? Below is a generic version when the elements are integer so a similar function is needed for floats (BinaryFloatingPoint).

    mutating func matRead<T: BinaryInteger>(count: Int = 1) -> [T] {
        var intOut : [T] = []
        let width = T().bitWidth / 8
        if count > 1 {
            for i in 0...count-1 {
                intOut.append( T( data.subdata(in: off + i * 4..<(off + (i + 1) * width) ).withUnsafeBytes{ $0.load(as: T.self )} ))
            }
            off += count * width
        }
        else if count == 1 {
            intOut =  [ T( data.subdata(in: off..<(off+width) ).withUnsafeBytes{ $0.load(as: T.self )} )]
            off += width
        }
        else if count == 0 {
            return []
        } else if count < 0{
            print("Warning, fReadint64Read( count : Int = 1) called with a negative count, returning empty array.")
        }
        return intOut
    }
    

    Note that there is a "bug" in the code above, I wasn't sure what to replace 4 with in i * 4..<(off + (i + 1) since you have used that number so inconsistently between your functions. I guess it should be width / 2 but I leave it to you to update that part.