How to convert the 'void *' return from a C function to 'UnsafeMutableRawPointer' in Swift 3?

I am trying to convert the lua bridge from Swift 2 to Swift 3. I am not the original author, so there are those aspects of the library that I don’t know very well, and the original author seems not interested in continuing to work on the project. I have most of the conversion, but there remains one place that I was stuck and could not understand. I tried searching on SO and the Internet, but could not find anything that could help me solve the problem.

If someone is interested in the full source code, here is my fork of the project on github: https://github.com/weyhan/lua4swift (My changes in the Swift3 branch)

Let me set the context for the error I'm stuck with. There is a Userdata class, especially in the userdataPointer<T>() -> UnsafeMutablePointer<T> lua_touserdata function c lua_touserdata returns the address of the userdata block as the pointer type void * .

Original code written in 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 } } 

After converting using the Xcode 8 migration tool, Xcode complains about the return line with the Cannot invoke initializer for type 'UnsafeMutablePointer<T>' with an argument list of type '(UnsafeMutableRawPointer?)' :

 return UnsafeMutablePointer<T>(ptr) 

I fixed it with

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

Following the change above, now Xcode 8 now complains about the calling operator in 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 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 } 

Where I stuck the line:

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

I believe that the original author is trying to clear the memory block, where the pointer returned by calling userdataPointer() points to.

Using the Xcode 8 automated migration tool, the above string is converted as shown below:

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

However, now Xcode complains that Cannot convert call result type 'UnsafeMutablePointer<_>' to expected type 'UnsafeMutableRawPointer' .

From my research, changing the return string in userdataPointer seems correct, so I think the problem is with casting to UnsafeMutableRawPointer. I tried to drop the cast in UnsafeMutableRawPointer and call ud.userdataPointer().deinitialize() directly, but I get this error Generic parameter 'T' could not be inferred .

Other things I've tried are converting UnsafeMutablePointer to UnsafeMutableRawPointer, but this always leads to Xcode 8 complaining about something else. Any suggestion on how to make this work?

+6
source share
2 answers

As you may have already discovered, Swift 3 is trying to provide better type safety when it comes to pointers. UnsafeMutablePointer can now only represent a pointer to an instance of a known type. UnsafeMutablePointer<Void> was introduced in Swift 2 C void * , allowing you to handle invalid and non-empty pointers in the same way, including trying to call the de-initializer of the specified type, which is what destroy() in the problem line of code:

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

In Swift 3, the de-initializer on the pointee is called using the deinitialize() method of the deinitialize() structure. The Migration Assistant seems confused. Line

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

makes little sense because (1) UnsafeMutablePointer cannot be converted using as to UnsafeMutableRawPointer ; (2) UnsafeMutableRawPointer does not have a deinitialize() method. In Swift 3, UnsafeMutableRawPointer is a special type for representing void* . In fact, it’s quite understandable why the migration tool made this mistake: it blindly replaced destroy() with deinitialize() and UnsafeMutablePointer<Void> with the corresponding Swift 3 type UnsafeMutableRawPointer , not realizing that the conversion would not work.

I don’t quite understand why calling destroy() in the void pointer will work in Swift 2. Perhaps it was a bug in the program or some kind of compiler trick allowed to call the correct deionizer. Unaware of the code, I cannot be more specific than offering to parse it to find out what the type is that the pointer that destroy() was called points to. For example, if we know for sure that it is always the type of placeholder T used in the following line:

  let o: T = ud.toCustomType() 

then the violation string just becomes

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

We need a conversion in parentheses to allow the compiler to output a common parameter.

Thanks for causing an interesting problem. By the way, as soon as you overcome this obstacle, you are likely to encounter other problems. One thing UnsafeMutablePointer : UnsafeMutablePointer does not have .memory in Swift 3; you will have to use .pointee .

Here's the update. After playing with Swift 2.2 on Linux, I understand that calling destroy() on UnsafeMutablePointer<A> when pressing UnsafeMutablePointer<Void> will not invoke the de-initializer, even if there is one. So you have to be careful with this line ...

+3
source

Try creating an instance of UnsafeMutableRawPointer instead of using it:

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

+1
source

All Articles