Correct usage of _modify/yield with UnsafeMutablePointer.withMemoryRebound(to:capacity:)

what is the right way to yield a value from a UnsafeMutablePointer.withMemoryRebound(to:capacity:) call?

i tried this, but it does not compile:

_modify
{
    yield &pointer.withMemoryRebound(to: T.self, capacity: 1) 
    {
        $0.pointee 
    }
}
1 Like

It's better to just yield a raw pointer or a typed view over a raw pointer. But presumably this is needed for C interop. In that case, use a defer statement to rebind memory since coroutines aren't a thing yet.

struct S {
  var pointer: UnsafeMutablePointer<Int64>
  var field: UInt64 {
    get {
      return UInt64(pointer.pointee)
    }
    _modify {
      // Relies on exclusivity to ensure that the memory at self.pointer is not simultaneously
      // accessed.
      let rawPtr = UnsafeMutableRawPointer(pointer)
      let uint64Ptr = rawPtr.bindMemory(to: UInt64.self, capacity: 1)
      defer { rawPtr.bindMemory(to: Int64.self, capacity: 1) }
      yield &uint64Ptr.pointee
    }
  }
}
6 Likes

great! by the way, is there any reason to implement a get instead of a _read?

1 Like

i’m running into some really strange issues with passing pointers to instance members using & inside a _modify subscript.

for some reason, this code works, where the access to &self.core (which is passed to a C API taking an UnsafeMutablePointer) in nested inside a function,

_modify
{
    let raw:UnsafeMutableRawPointer =
    {
        guard let raw:UnsafeMutableRawPointer = 
            Godot.api.functions.godot_array_operator_index(&self.core, .init(index))
            .map(UnsafeMutableRawPointer.init(_:))
        else 
        {
            fatalError("nil pointer to list element (\(index))")
        }
        return raw
    }()
    
    let pointer:UnsafeMutablePointer<Godot.Variant.Unmanaged> = 
        raw.bindMemory(to: Godot.Variant.Unmanaged.self, capacity: 1)
    defer 
    {
        raw.bindMemory(to:           godot_variant.self, capacity: 1)
    }
    
    yield &pointer.pointee
} 

(Godot.Variant.Unmanaged is a value type from swift’s perspective, but the C API views it as a reference-counted type. the deinit on the swift class providing the _modify subscript manually deinitializes it using a deinitialization API provided by the Godot C framework.)

but if i move this outside the function wrapper, like this

_modify
{
    guard let raw:UnsafeMutableRawPointer = 
        Godot.api.functions.godot_array_operator_index(&self.core, .init(index))
        .map(UnsafeMutableRawPointer.init(_:))
    else 
    {
        fatalError("nil pointer to list element (\(index))")
    }
    
    let pointer:UnsafeMutablePointer<Godot.Variant.Unmanaged> = 
        raw.bindMemory(to: Godot.Variant.Unmanaged.self, capacity: 1)
    defer 
    {
        raw.bindMemory(to:           godot_variant.self, capacity: 1)
    }
    
    yield &pointer.pointee
} 

the program crashes when the _modify subscript gets called. any idea what’s going on here?

is there any reason to implement a get instead of a _read ?

If you're already implementing _modify then I supposed you may as well use _read. If self has any references, then that might avoid a retain.

Speaking of references, if that UnsafePointer points into a class, then you need to extend self's life time

 defer {
   raw.bindMemory(...)
   withExtendedLifetime(self) {}
  }

If lifetime extension doesn't fix the crash, then it appears to be a compiler bug.

1 Like

that was one of the first things i tried, but it did not fix the issue. however, an ordinary set subscript works, even if the godot_array_operator_index call is not nested inside another function.

the pointer does not point into the class, but it points to a C-managed object that Swift decrements the reference count of when the deinit of the Swift wrapper class runs. (the Swift code increments the reference count when it wraps the object). however, the C API passes the object guaranteed (the C API’s equivalent of guaranteed, not Swift guaranteed), so this shouldn’t be the issue.

Terms of Service

Privacy Policy

Cookie Policy