2016-10-05 10 views
6

Próbuję przekonwertować mostek Lua z Swift 2 na Swift 3. Nie jestem oryginalnym autorem, więc są pewne aspekty biblioteki, których nie znam zbyt dobrze, a oryginalny autor nie jest zainteresowany kontynuowaniem pracy nad projekt. Mam większość konwersji, ale pozostało jedno miejsce, w którym utknąłem i nie mogłem się zorientować. Próbowałem szukać w SO i Internecie, ale nie mogłem znaleźć niczego, co mogłoby mi pomóc rozwiązać problem.Jak przekonwertować "void *" powrót z funkcji C na "UnsafeMutableRawPointer" w Swift 3?

Jeśli ktoś jest zainteresowany patrząc na pełnym kodem źródłowym, tu jest mój widelec projektu na github: https://github.com/weyhan/lua4swift (mój zmianach w branży Swift3)

Pozwól mi Setup kontekst do błędu I” utknąłem na. Istnieje klasa Userdata, konkretnie w metodzie userdataPointer<T>() -> UnsafeMutablePointer<T> funkcja c zwraca wartość bloku adresu danych użytkownika jako typ wskaźnika void *.

oryginalny kod napisany w Swift 2:

public class Userdata: StoredValue { 

    public func userdataPointer<T>() -> UnsafeMutablePointer<T> { 
     push(vm) 
     let ptr = lua_touserdata(vm.vm, -1) 
     vm.pop() 
     return UnsafeMutablePointer<T>(ptr) 
    } 

    public func toCustomType<T: CustomTypeInstance>() -> T { 
     return userdataPointer().memory 
    } 

    public func toAny() -> Any { 
     return userdataPointer().memory 
    } 

    override public func kind() -> Kind { return .Userdata } 

} 

Po konwersji z Xcode 8 narzędzie do migracji, Xcode narzeka na przewodzie powrotnym z błędem Cannot invoke initializer for type 'UnsafeMutablePointer<T>' with an argument list of type '(UnsafeMutableRawPointer?)':

return UnsafeMutablePointer<T>(ptr) 

Naprawiłem go z:

return (ptr?.assumingMemoryBound(to: T.self))! 

Po powyższej zmianie, teraz Xcode 8 jest teraz skarżą się na oświadczenie wzywające w createCustomType:

public func createCustomType<T: CustomTypeInstance>(setup: (CustomType<T>) -> Void) -> CustomType<T> { 
    lua_createtable(vm, 0, 0) 
    let lib = CustomType<T>(self) 
    pop() 

    setup(lib) 

    registry[T.luaTypeName()] = lib 
    lib.becomeMetatableFor(lib) 
    lib["__index"] = lib 
    lib["__name"] = T.luaTypeName() 

    let gc = lib.gc 
    lib["__gc"] = createFunction([CustomType<T>.arg]) { args in 
     let ud = args.userdata 

     // ******* Here's the line that is causing problem in Swift 3 
     (ud.userdataPointer() as UnsafeMutablePointer<Void>).destroy() 
     // ******* 

     let o: T = ud.toCustomType() 
     gc?(o) 
     return .Nothing 
    } 

    if let eq = lib.eq { 
     lib["__eq"] = createFunction([CustomType<T>.arg, CustomType<T>.arg]) { args in 
      let a: T = args.customType() 
      let b: T = args.customType() 
      return .Value(eq(a, b)) 
     } 
    } 
    return lib 
} 

Gdzie Utknąłem coraz to linia:

(ud.userdataPointer() as UnsafeMutablePointer<Void>).destroy() 

wierzę oryginalny autor próbuje wyczyścić blok pamięci, gdzie wskaźnik zwracany przez wywołanie userdataPointer() wskazuje.

pomocą narzędzia Xcode 8 automatycznej migracji powyższa linia przekształca się, jak poniżej:

(ud.userdataPointer() as UnsafeMutableRawPointer).deinitialize() 

Jednakże Xcode się następnie zarzuca Cannot convert call result type 'UnsafeMutablePointer<_>' to expected type 'UnsafeMutableRawPointer'.

Z moich badań wynika, że ​​zmiana na linię powrotną w userdataPointer wydaje się poprawna, więc myślę, że problem dotyczy obsady UnsafeMutableRawPointer. Próbowałem upuścić obsadę do UnsafeMutableRawPointer i wywołać ud.userdataPointer().deinitialize() bezpośrednio, ale pojawia się ten błąd Generic parameter 'T' could not be inferred.

Inne rzeczy, których próbowałem, to przekonwertować UnsafeMutablePointer na UnsafeMutableRawPointer, ale zawsze powoduje to, że Xcode 8 narzeka na jedno lub drugie. Wszelkie sugestie, jak to zrobić?

Odpowiedz

3

Jak się pewnie dowiedziałeś, Swift 3 stara się zapewnić lepsze bezpieczeństwo, jeśli chodzi o wskaźniki. UnsafeMutablePointer może teraz reprezentować tylko wskaźnik do instancji znanego typu.W Swift 2, C void * było reprezentowane przez UnsafeMutablePointer<Void>, pozwalając na traktowanie w ten sam sposób pustych i nieważnych wskaźników, w tym próbując wywołać deinicjalizator wskazanego typu, co jest metodą w problematyczne linii kodu:

(ud.userdataPointer() as UnsafeMutablePointer<Void>).destroy() 

Swift 3 de-inicjatora na wskazywany jest wywoływana z zastosowaniem metody konstrukcji UnsafeMutablePointerdeinitialize(). Wygląda na to, że asystent migracji został zdezorientowany. Linia

(ud.userdataPointer() as UnsafeMutableRawPointer).deinitialize() 

ma wielkiego sensu, ponieważ (1) UnsafeMutablePointer nie może być przeliczone as do UnsafeMutableRawPointer; (2) UnsafeMutableRawPointer nie ma metody deinitialize(). W Swift 3, UnsafeMutableRawPointer jest specjalny typ reprezentujący void*. Jest całkiem zrozumiałe, dlaczego narzędzie migracji popełniło ten błąd: ślepo zastąpiło destroy() z deinitialize() i UnsafeMutablePointer<Void> z odpowiednim typem Swift 3 UnsafeMutableRawPointer, nie wiedząc, że konwersja nie zadziała.

Nie do końca rozumiem, dlaczego wywołanie destroy() na pustym wskaźniku działałoby w Swift 2. Może to był błąd w programie lub sztuczka kompilatora pozwalała na wywołanie prawidłowego de-inicjalizatora. Nie wiedząc wystarczająco dużo o kodzie, nie mogę być bardziej konkretny niż sugerować analizowanie go, aby dowiedzieć się, jaki typ wskazywał ten wskaźnik, na który został wywołany destroy(). Na przykład, jeśli wiemy na pewno, że jest to zawsze rodzaj zastępczy T wykorzystywane na następującej linii:

let o: T = ud.toCustomType() 

następnie linia wykraczająca po prostu staje

(ud.userdataPointer() as UnsafeMutablePointer<T>).deinitialize() 

Musimy konwersję w nawiasach pozwól kompilatorowi na określenie parametru ogólnego.

Dziękuję za poruszenie interesującego problemu. BTW, gdy przejdziesz przez tę przeszkodę, prawdopodobnie napotkasz inne problemy. Jedna rzecz, która wyskakuje to, że UnsafeMutablePointer nie ma .memory w Swift 3; będziesz musiał zamiast tego użyć .pointee.

Oto aktualizacja. Po uruchomieniu Swift 2.2 na Linuksie, zdaję sobie sprawę, że wywołanie destroy() na UnsafeMutablePointer<A> nie będzie wywoływać deinicjalizatora A, nawet jeśli ma on jeden. Więc trzeba być ostrożnym z tej linii ...

+0

Swift 3 migrator wydaje się konwersji tych '.memory' do '.pointee' w prawo. Masz rację, że wędrowiec wpadł w zamieszanie w kilku miejscach, ale generalnie działa w większości konwersji. Udało mi się to naprawić, zmieniając nieprawidłową linię na: 'UnsafeMutablePointer (ud.userdataPointer()). Deinitialize()' Myślę, że to jest poprawne, ale jeśli wiesz, że to źle, doceniłbym, gdybyś pozwolił ja wiem. Dzięki za długa analizę! –

+0

To, co zrobiłeś, działa tak samo, jak sugerowałem. Tak czy inaczej, potrzebujemy rzutowania lub inicjalizacji tylko po to, aby powiedzieć kompilatorowi, co jest parametrem generycznym podczas wywoływania 'userdataPointer()'.Uważaj jednak: problematyczny wiersz w starym kodzie nie deinicjalizuje danych użytkownika. Nowy kod będzie. Jeśli kolejne wywołanie 'toCustomType()' spróbuje zwrócić ten sam obiekt userdata, zostanie ono już zdenokalizowane. Oczywiście nie znam kodu, po prostu dzielę się ogólnymi spostrzeżeniami. – OmniProg

+0

Stary kod oznaczający tę linię? '(ud.userdataPointer() jako UnsafeMutablePointer ) .destroy()'. Dzięki za heads up. Będę się nad tym zastanawiać. –

1

Spróbuj utworzyć instancję UnsafeMutableRawPointer zamiast próbować go obsada:

UnsafeMutableRawPointer<T>(ud.userdataPointer()).destroy()

+0

Chociaż formularz, którego używam, był następujący: 'UnsafeMutablePointer (ud.userdataPointer()). Deinitialize()' Dzięki. –

Powiązane problemy