2016-10-05 17 views
6

Tôi đang cố chuyển đổi cầu lua từ Swift 2 sang Swift 3. Tôi không phải là tác giả gốc nên có các khía cạnh của thư viện mà tôi không biết rõ và tác giả gốc dường như không quan tâm để tiếp tục làm việc dự án. Tôi có hầu hết các chuyển đổi được thực hiện nhưng vẫn còn một nơi tôi đang mắc kẹt và không thể tìm ra. Tôi đã thử tìm kiếm trên SO và trên Internet nhưng không thể tìm thấy bất cứ điều gì có thể giúp tôi giải quyết vấn đề.Làm thế nào để chuyển đổi 'void *' trở về từ một hàm C thành 'UnsafeMutableRawPointer' trong Swift 3?

Nếu bất cứ ai quan tâm đến việc nhìn vào mã nguồn đầy đủ, đây là ngã ba của tôi về dự án trên github: https://github.com/weyhan/lua4swift (những thay đổi của tôi là tại các chi nhánh Swift3)

Cho phép tôi thiết lập bối cảnh đến lỗi tôi m bị kẹt trên. Có một lớp Userdata, cụ thể trong phương thức userdataPointer<T>() -> UnsafeMutablePointer<T> hàm c lua_touserdata trả về địa chỉ khối của userdata dưới dạng kiểu con trỏ void *.

đang gốc viết bằng 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 } 

} 

Sau khi chuyển đổi với công cụ di cư Xcode 8, Xcode được phàn nàn về dòng trở lại với lỗi Cannot invoke initializer for type 'UnsafeMutablePointer<T>' with an argument list of type '(UnsafeMutableRawPointer?)':

return UnsafeMutablePointer<T>(ptr) 

Tôi đã khắc phục nó với:

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

Sau thay đổi ở trên, bây giờ Xcode 8 hiện đang phàn nàn về tuyên bố kêu gọi trong 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 
} 

Nơi tôi đang bị mắc kẹt là dòng:

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

Tôi tin rằng tác giả ban đầu đang nỗ lực để xóa các khối bộ nhớ nơi con trỏ trở lại bởi userdataPointer() cuộc gọi đang trỏ đến.

Với công cụ Xcode 8 tự động chuyển đổi dòng trên được quy đổi như sau:

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

Tuy nhiên Xcode tại là sau đó phàn nàn rằng Cannot convert call result type 'UnsafeMutablePointer<_>' to expected type 'UnsafeMutableRawPointer'.

Từ nghiên cứu của tôi, thay đổi đối với dòng trả về trong userdataPointer có vẻ chính xác, vì vậy tôi nghĩ vấn đề là với dàn diễn viên tới UnsafeMutableRawPointer. Tôi đã thử bỏ dàn diễn viên xuống UnsafeMutableRawPointer và gọi trực tiếp ud.userdataPointer().deinitialize() nhưng tôi gặp phải lỗi này Generic parameter 'T' could not be inferred.

Những thứ khác tôi đã thử là chuyển đổi UnsafeMutablePointer thành UnsafeMutableRawPointer nhưng luôn dẫn đến Xcode 8 phàn nàn một điều này hay điều khác. Bất kỳ gợi ý về cách để có được điều này để làm việc?

Trả lời

3

Như bạn đã biết, Swift 3 cố gắng cung cấp sự an toàn kiểu tốt hơn khi nói đến con trỏ. UnsafeMutablePointer bây giờ có thể chỉ đại diện cho một con trỏ đến một thể hiện của một loại đã biết.Trong Swift 2, một C void * được đại diện bởi UnsafeMutablePointer<Void>, cho phép các con trỏ void và non-void được xử lý theo cùng một cách, bao gồm cả việc cố gắng gọi bộ khởi tạo của kiểu được chỉ định, đó là phương thức destroy() trong dòng mã có vấn đề gì:

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

trong Swift 3 de-initializer trên pointee được gọi là sử dụng phương pháp deinitialize() của cấu trúc UnsafeMutablePointer. Dường như trợ lý di chuyển bị lẫn lộn. Dòng

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

làm cho chút ý nghĩa bởi vì (1) UnsafeMutablePointer không thể chuyển đổi sử dụng as-UnsafeMutableRawPointer; (2) UnsafeMutableRawPointer không có phương pháp deinitialize(). Trong Swift 3, UnsafeMutableRawPointer là một loại đặc biệt để đại diện cho void*. Nó thực sự là khá dễ hiểu lý do tại sao công cụ di chuyển thực hiện sai lầm này: nó thay thế một cách mù quáng destroy() với deinitialize()UnsafeMutablePointer<Void> với Swift 3 loại tương ứng UnsafeMutableRawPointer, mà không nhận ra rằng chuyển đổi sẽ không hoạt động.

Tôi không hiểu tại sao gọi destroy() trên con trỏ trống sẽ hoạt động trong Swift 2. Có thể đây là lỗi trong chương trình hoặc một số thủ thuật biên dịch cho phép trình khởi tạo de đúng được gọi. Nếu không biết đủ về mã, tôi không thể cụ thể hơn là đề nghị phân tích nó để tìm ra loại được trỏ tới bởi con trỏ mà trên đó destroy() được gọi. Ví dụ, nếu chúng ta biết chắc chắn rằng nó luôn luôn là loại placeholder T sử dụng trên các dòng sau:

let o: T = ud.toCustomType() 

sau đó dòng vi phạm đơn giản trở nên

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

Chúng ta cần chuyển đổi trong ngoặc đơn để cho phép trình biên dịch suy ra tham số chung.

Cảm ơn bạn đã đưa ra một vấn đề thú vị. BTW, một khi bạn vượt qua trở ngại này, bạn có khả năng gặp phải các vấn đề khác. Một điều mà nhảy ra là UnsafeMutablePointer không có .memory trong Swift 3; bạn sẽ phải sử dụng .pointee để thay thế.

Đây là bản cập nhật. Sau khi chơi với Swift 2.2 trên Linux, tôi nhận ra rằng việc gọi số destroy() trên số UnsafeMutablePointer<A> sẽ là UnsafeMutablePointer<Void> sẽ không gọi deinitializer của A, ngay cả khi nó có. Vì vậy, bạn phải cẩn thận với dòng ...

+0

Swift 3 migrator dường như để chuyển đổi những '.memory' để '.pointee' đúng. Bạn đúng rằng người di cư đã nhầm lẫn ở một vài nơi nhưng nhìn chung nó hoạt động trong phần lớn chuyển đổi. Tôi đã cố gắng sửa chữa nó bằng cách thay đổi dòng vi phạm thành: 'UnsafeMutablePointer (ud.userdataPointer()). Deinitialize()' Tôi nghĩ điều này là đúng nhưng nếu bạn biết nó sai tôi sẽ đánh giá cao nếu bạn để tôi biết. Cảm ơn bạn đã phân tích dài! –

+0

Điều bạn đã làm tương tự như những gì tôi đã đề xuất. Dù bằng cách nào chúng ta cần một diễn viên hoặc khởi tạo chỉ để nói cho trình biên dịch biết tham số chung là gì khi gọi 'userdataPointer()'.Tuy nhiên, hãy cẩn thận: dòng có vấn đề trong mã cũ đã không hủy khởi tạo dữ liệu người dùng. Mã mới sẽ. Nếu cuộc gọi 'toCustomType()' tiếp theo cố gắng trả về cùng một đối tượng userdata, nó sẽ được hủy khởi tạo. Tất nhiên, tôi không biết mã, chỉ cần chia sẻ những quan sát chung. – OmniProg

+0

Mã cũ có nghĩa là dòng này? '(ud.userdataPointer() là UnsafeMutablePointer ) .destroy()'. Cảm ơn cho những người đứng đầu lên. Tôi sẽ xem xét nó. –

1

Hãy thử tạo một thể hiện của UnsafeMutableRawPointer thay vì cố gắng để cast nó:

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

+0

Mặc dù biểu mẫu tôi sử dụng là: 'UnsafeMutablePointer (ud.userdataPointer()). Deinitialize()' Cảm ơn. –

Các vấn đề liên quan