WeyHan Ng WeyHan Ng - 1 month ago 12
C Question

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


I'm trying to convert a lua bridge from Swift 2 to Swift 3. I am not the original author so there are aspects of the library I don't know very well and the original author seems not interested to continue working on the project. I have most of the conversion done but there remain one place I'm stuck and could not figure out. I've tried searching on SO and on the Internet but could not find anything that could help me solve the problem.

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

Allow me setup the context to the error I'm stuck on. There is a Userdata class, specifically in the method

userdataPointer<T>() -> UnsafeMutablePointer<T>
the c function
lua_touserdata
returns the block address of userdata as a
void *
pointer type.

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 the conversion with Xcode 8 migration tool, Xcode is complaining about the return line with error
Cannot invoke initializer for type 'UnsafeMutablePointer<T>' with an argument list of type '(UnsafeMutableRawPointer?)'
:

return UnsafeMutablePointer<T>(ptr)


I've fixed it with:

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


Following that above change, now Xcode 8 is now complaining about the calling statement 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'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
}


Where I'm getting stuck is the line :

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


I believe the original author is attempting to clear the memory block where the pointer returned by
userdataPointer()
call is pointing to.

With the Xcode 8 auto migration tool the above line is converted as below:

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


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

From my research, the change to the return line in
userdataPointer
seems correct, so I think the issue is with the cast to UnsafeMutableRawPointer. I've tried dropping the cast to
UnsafeMutableRawPointer
and invoke
ud.userdataPointer().deinitialize()
directly but I get this error
Generic parameter 'T' could not be inferred
.

Other things I've tried is to convert the UnsafeMutablePointer to UnsafeMutableRawPointer but It always result in Xcode 8 complaining one thing or another. Any suggestion on how to get this to work?

Answer

Try creating an instance of UnsafeMutableRawPointer instead of trying to cast it:

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

Comments