C union type in Swift?
Asked Answered
B

7

21

How can I declare and use a C union type in Swift?

I tried:

var value: union {
      var output: CLongLong
      var input: [CInt]
    }

but it does not work...

UPDATED: I want to use union to split a 8 bytes number to 2 x 4 bytes number.

Battled answered 21/6, 2015 at 7:55 Comment(6)
There is no direct support of unions in Swift, but you can use enumeration to accomplish your requirement.Cresida
Unions are disfavoured by just about every C and C++ coding standard know to man because they are so frequently used to write non-portable code. If you have a choice in the matter, never use a union in C, and in all cases unpack whatever data is contained within with bit-wise operators.Tonisha
@marko, I would have to disagree with your premise. For instance, when receiving a message, where the message can have any of several different formats, it is almost always implemented as a union of those formats, typically where the first, non header, field indicates which message type is contained in the rest of the message, where the rest of the message is defined as the union of all the message types.Circumnavigate
This is precisely the scenario which I was thinking of. by using an union here we're trying to overlay the a storage layout onto a stream of bytes - for instance, received from the network, or another process. The portability hazards are myriad because of alignment, structure packing and endianness varying from one architecture to another.Tonisha
@Circumnavigate There are better options to handle such situations. In OOP languages you would use inheritance, every message type represented by a different class, in C you can use pseudo-inheritance.Evasive
A data type containing bit fields isn't inherently unsafe, but it's implementation at least the way C does using union is. In Ada, you can declare types sized in bits and compose them into a larger type while preventing unsafe operations on them.Zoi
K
19

As the Apple Swift document , Enumerations can do similar thing and more.

Alternatively, enumeration members can specify associated values of any type to be stored along with each different member value, much as unions or variants do in other languages. You can define a common set of related members as part of one enumeration, each of which has a different set of values of appropriate types associated with it.

1) If you just want to split a 8 bytes number to 2 x 4 bytes numbers, as you might have known, the Bitwise Operation of Swift could help. Just like

let bigNum: UInt64 = 0x000000700000008 //
let rightNum = (bigNum & 0xFFFFFFFF) // output 8
let leftNum = (bigNum >> 32)  // output 7

2) If you want to simulate the union behaviour like C language, I tried a way like this. Although it works, it looks terrible.

enum Number {
    case a(Int)
    case b(Double)

    var a:Int{
        switch(self)
        {
        case .a(let intval): return intval
        case .b(let doubleValue): return Int(doubleValue)
        }
    }

    var b:Double{
        switch(self)
        {
        case .a(let intval): return Double(intval)
        case .b(let doubleValue): return doubleValue
        }
    }
}
let num = Number.b(5.078)

println(num.a)  // output 5
println(num.b)  // output 5.078
Katabasis answered 21/6, 2015 at 11:41 Comment(2)
(2) is not like a C union at all, it's just converting between double and int. A C union would have both vars share the same memory location, so storing the 8 bytes Double(5.078) takes up and reading them back as an Int would not return 5, but 0x40144FDF3B645A1D (4,617,403,338,154,334,749, yikes!)Quentinquercetin
Semantically, it IS similar to union. Unions mean sharing a spot in memory. A swift enum does this, except that it adds on a byte for the enum value.Colure
C
3

Well, there is no direct support of unions, in Swift, but we can use enumeration for our purpose.

Ex-

enum Algebra {
  case Addition(Double, Double)
  case Substraction(Double, Double)
  case Square(Double)

  var result : Double {
    switch(self)
    {
    case .Addition(let a, let b): return a + b
    case .Substraction(let a, let b):  return a - b
    case .Square(let a):  return a * a
    }
  }
}

let solution = Algebra.Addition(5, 3)
println(solution.result) //Output: 8.0
Cresida answered 21/6, 2015 at 9:11 Comment(3)
Seems to be not the same purpose, I want split a 8 bytes number to 2 x 4 bytes numbersBattled
That's an unsafe operation which is intentionally not supported in Swift. Now obviously what you want to do is trivial to achieve with shift and bitwise and operations. A Swift enumeration will never let you re-interpret data in a different way. Fully intentional.Tunicle
That's incorrect. You can use a raw pointer to write a value as one type, then read the bits back as a different type.Apophthegm
J
3

Swift discourages "unsafe" programming patterns like union, however, there is a workaround. It's a bit ugly, but here goes (using Xcode 7.2)...

struct VariantA {
    var oneField: Int // 8 bytes

    struct VariantB {
        var firstField: UInt32 // first 4 bytes
        var secondField: UInt32 // second 4 bytes
    }

    var variantB: UnsafeMutablePointer<VariantB>  {
        mutating get {
            func addressOf<U>(something:UnsafePointer<U>)
                -> UnsafeMutablePointer<VariantB> {
                return UnsafeMutablePointer<VariantB>(something)
            }
            return addressOf(&oneField)
        }
    }
}

Now a quick check of sizeof(VariantA) will show the whole structure still takes up only eight bytes (which is one 64-bit integer). If we now create an instance like this var a = VariantA(oneField: 1234567890987654321) then we can query the components like this a.oneField which will return the initial value 1,234,567,890,987,654,321 and also a.variantB.memory.firstField will return 2,976,652,465 and a.variantB.memory.secondField will return 287,445,236.

We can change one of the components like this a.variantB.memory.firstField++ and then observe that this changes the original value of a.oneField to 1,234,567,890,987,654,322 as expected.

The ugly parts for me are the occurrences of the words "unsafe", "pointer" and .memory. as well as that addressOf helper function which is only there to overcome a compiler error in Xcode 7.2!

Perhaps working with lower-level structures that require byte-level manipulation like this should not be done in a high level language like Swift at all. Have you considered writing this part of your project in a .c file? With a suitable bridging header you can still do the majority of your project in Swift.

Jambalaya answered 29/12, 2015 at 16:20 Comment(0)
T
2

Here I define MyUnion which has two member named f and g. As you see, union is unsafe, and enum with associated value is preferred, though it's a bit different from union.

// Simulate union of the C programming language
// f and g share memory
struct MyUnion {
    fileprivate var ivar: UInt32 = 0 // Shared memory, private is better.

    var f: Float {
        get { return Float.init(bitPattern: ivar) }
        set { ivar = newValue.bitPattern }
    }
    init(f: Float) { self.f = f }

    var g: Int32 {
        get { return Int32(bitPattern: ivar) }
        set { ivar = UInt32(bitPattern: newValue) }
    }
    init(g: Int32) { self.g = g }
}

var u = MyUnion(f: 13.12)
u.f    // 13.12
u.g    // 1095887749
u.ivar // 1095887749

u.f = -99.123
u.f    // -99.123
u.g    // -1027195142
u.ivar // 3267772154

u = MyUnion(g: -1)
u.f    // nan
u.g    // -1
u.ivar // 4294967295
Trass answered 4/1, 2018 at 12:12 Comment(0)
G
2

Well, you can make a C union in a struct in ObjC code, and when imported in swift it will behave like it's supposed to. Source: https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/using_imported_c_structs_and_unions_in_swift

"Swift imports C unions as Swift structures. Although Swift doesn’t support natively declared unions, a C union imported as a Swift structure still behaves like a C union."

Guendolen answered 7/12, 2018 at 13:34 Comment(0)
E
0

Your union doesn't specify the type of the structure, so I would assume you are always deriving that from the context (you always know if the value is either output or input). In that case, I would translate it into two separate structs:

struct OutputValue {
    ...
}

struct InputValue {
    ...
}

If you want the structs to have some common properties/methods, declare a protocol for it.

Evasive answered 22/6, 2015 at 11:7 Comment(0)
H
0

Couldn't one do something like this:

struct MyUnion {           // A poor man's |union| in Swift.
    var lsb = UInt8(0)
    var msb = UInt8(0)

    var uint16 : UInt16 {
        get { return (UInt16(msb) << 8) + UInt16(lsb) }
        set { self.msb = UInt8(newValue >> 8); self.lsb = UInt8(newValue) }
        }
    }
Hoenack answered 21/5 at 21:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.