2015-06-21 23 views
8

Jak mogę zadeklarować i użyć typu C union w Swift?C typ unii w Swift?

Próbowałem:

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

ale to nie działa ...

aktualizacja: Chcę używać unii podzielić 8 bytes number do 2 x 4 bytes number.

+0

Nie ma bezpośredniego wsparcia dla związków w Swift, ale można użyć wyliczenia, aby spełnić swoje wymagania. – Amit89

+2

Związki są disfavoured przez prawie każdego standardu kodowania C i C++ znanego człowiekowi, ponieważ są one tak często używane do pisania nieprzenośny kod. Jeśli masz wybór w tej sprawie, nigdy nie używaj "unii" w C, a we wszystkich przypadkach rozpakuj dane zawarte w operacjach bitowych. – marko

+0

@ marko, musiałbym nie zgodzić się z twoim założeniem. Na przykład podczas odbierania wiadomości, w której wiadomość może mieć jeden z kilku różnych formatów, prawie zawsze jest ona implementowana jako zjednoczenie tych formatów, zazwyczaj tam, gdzie pierwsze, nie nagłówkowe pole wskazuje, który typ wiadomości zawiera się w pozostałej części. wiadomość, gdzie resztę wiadomości definiuje się jako połączenie wszystkich typów wiadomości. – user3629249

Odpowiedz

10

W dokumencie Jabłko Swift, Enumerations może zrobić coś podobnego i więcej.

Alternatywnie enumeration członkowie mogą określić związane z nimi wartości dowolnego typu mają być przechowywane wraz z każdą inną wartość członkowskim, podobnie jak unions lub variants zrobić w innych językach. Można zdefiniować wspólny zestaw powiązanych elementów jako część jednego wyliczenia, z których każdy ma inny zestaw wartości odpowiednich typów z nim powiązanych.

1) Jeśli po prostu chcesz podzielić liczbę 8 bajtów do 2 x 4 bajty numerów, jak mogłeś wiadomo, Bitwise Operation Swift może pomóc. Podobnie jak

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

2) Jeśli chcesz, aby symulować zachowanie union jak C języka Próbowałem sposobu takiego. Mimo że działa, wygląda okropnie.

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 
+3

(2) w ogóle nie przypomina związku C, to tylko konwersja pomiędzy podwójnym i wewnętrznym. Złącza AC miałyby oba vary współdzielić tę samą lokalizację pamięci, więc zapisanie 8 bajtów Double (5.078) zajmuje i odczytuje je jako Int nie zwróci 5, ale 0x40144FDF3B645A1D (4617403338154, 3344949, yikes!) – GoatInTheMachine

+0

Semantycznie jest podobny do unia. Związki oznaczają dzielenie miejsca w pamięci. Robi to szybkie wyliczanie, z tym wyjątkiem, że dodaje bajt do wartości wyliczeniowej. –

2

Cóż, nie ma bezpośredniego wsparcia dla związków, w Swift, ale możemy użyć wyliczenia dla naszego celu.

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 
+0

Wygląda na to, że nie jest to ten sam cel, chcę podzielić "8 bajtów" na 2 x "4 bajty liczb" –

+2

To niebezpieczna operacja, która nie jest celowo obsługiwana w Swift. Teraz oczywiste jest, że to, co chcesz zrobić, jest trywialne, aby osiągnąć zmiany i bitowe operacje. Wyliczenie Swift będzie _nie_ umożliwiać ponowną interpretację danych w inny sposób. W pełni zamierzony. – gnasher729

0

Twój związek nie określa typ konstrukcji, więc zakładam, że są zawsze wynikające z kontekstu (zawsze wiedzieć, jeśli wartość jest albo output lub input). W tym przypadku, chciałbym przełożyć je na dwóch oddzielnych strukturach:

struct OutputValue { 
    ... 
} 

struct InputValue { 
    ... 
} 

Jeśli chcesz, kodowanym mieć pewne wspólne właściwości/metod, zadeklarować dla niego.

2

Swift zniechęca "niebezpieczne" wzorce programowania, takie jak union, jednak istnieje obejście. To trochę brzydki, ale tu idzie (używając 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) 
     } 
    } 
} 

Teraz szybkie sprawdzenie sizeof(VariantA) pokaże cała struktura nadal trwa tylko osiem bajtów (co jest jednym 64-bitowa liczba całkowita). Jeśli teraz stworzymy instancję taką jak ta var a = VariantA(oneField: 1234567890987654321), możemy zapytać komponenty takie jak ten a.oneField, które zwrócą początkową wartość 1,234,567,890,987,654,321, a także a.variantB.memory.firstField zwróci 2,976,652,465, a a.variantB.memory.secondField zwróci 287,445,236.

Możemy zmienić jeden z komponentów jak ten a.variantB.memory.firstField++ a następnie zauważyć, że zmienia oryginalną wartość a.oneField do 1,234,567,890,987,654,32 , jak oczekiwano.

Brzydkie części to dla mnie słowa "niebezpieczny", "wskaźnik" i .memory., a także ta pomocnicza funkcja addressOf, która jest dostępna tylko po to, aby przezwyciężyć błąd kompilatora w Xcode 7.2!

Być może praca ze strukturami niższego poziomu, które wymagają takiej manipulacji na poziomie bajtowym, nie powinna odbywać się w języku wysokiego poziomu, jak na przykład Swift. Czy rozważałeś napisanie tej części swojego projektu w pliku .c? Dzięki odpowiedniemu nagłówkowi pomostowemu możesz nadal wykonywać większość projektu w Swift.

1

Tutaj definiuję MyUnion, który ma dwóch członków o nazwach f i g. Jak widać, union jest niebezpieczne, a preferowana jest enum z powiązaną wartością, chociaż jest nieco inna niż union.

// Simulate union in C 
// 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