MacOS - A C function pointer cannot be formed from a closure that captures context

Hi, posting in iOS because there's no macOS option.

I'm using the MetaTracker, and have set up everything in the macOS starter project as per the instructions in the Swift tutorial to be able to receive the data. I was able to connect the sensor and everything, and have it stream data outputted to the console, but I've been unable to do anything with the data because if I try and do anything other than just print it, I get the error "A C function pointer cannot be formed from a closure that captures context"

@IBAction func startPressed(_ sender: Any) {
    let board = device.board
    guard mbl_mw_metawearboard_lookup_module(board, MBL_MW_MODULE_ACCELEROMETER) != MODULE_TYPE_NA else{
        print("No accelerometer")
        return
        }
    let signal = mbl_mw_acc_get_acceleration_data_signal(board)

    mbl_mw_datasignal_subscribe(signal, bridge(obj: self)) { (context, data) in
        let _: NextViewController = bridge(ptr: context!)
        let obj: MblMwCartesianFloat = data!.pointee.valueAs()
        if (obj.x>0.2||obj.y>0.2){
            print(obj.x, obj.y, obj.z)
            self.adding(obj.x, obj.y, obj.z)
            }
        }
    mbl_mw_acc_enable_acceleration_sampling(board)
    mbl_mw_acc_start(board)
}

it works fine if I remove the line self.adding(obj.x, obj.y, obj.z) but I'm not sure how i'm supposed to use the data otherwise

Any help would be greatly appreciated, thanks!

Comments

  • edited March 2019

    Use the context pointer to refer to self.

  • Sorry I'm a little bit new to swift, how would I do that/where would I put it?

  • Your code is already casting the context pointer to a NextViewController. Use that variable instead of self.

  • @Eric said:
    Your code is already casting the context pointer to a NextViewController. Use that variable instead of self.

    same error not working

  • edited November 2019

    Just create an array and save the data as it comes in: (obj.x, obj.y, obj.z):
    https://stackoverflow.com/questions/24002733/add-an-element-to-an-array-in-swift

    Then when you are done, turn that array into a json or csv type to create the output of your choice:
    http://www.justindoan.com/tutorials/2016/9/9/creating-and-exporting-a-csv-file-in-swift

    Please note these are not metawear related questions and your posts are general "how to write code" questions. You should be a competent swift developer to use our API libraries: https://developer.apple.com/library/archive/referencelibrary/GettingStarted/DevelopiOSAppsSwift/
    It is not our job to teach you to code, only to guide you to use our APIs. Future generic "how to code" posts will be deleted.

  • Hi there!
    Would it be possible for you to clarify your approach a bit more. Me and my team have run into the same issue and couldn’t seem to fix it with the solutions provided here. Our goal is to assign the incoming data (obj.x, obj.y, obj.z) to some variable that is accessible within the class.
    We followed the documentation and had everything working on the streaming side and were even able to print the incoming data as it came in. As soon as we tried appending them to an array, or assigning it to some variable who’s scope is outside the method we would get the same C pointer error described above.
    A notable difference on our end was that since we followed the documentation, we didn’t have the line ‘let _: NextViewController = bridge(ptr: context!)’ and also couldn’t quite figure out its role since we are using SwiftUI and not conventional ViewControllers.
    I am assuming we are probably missing some minor detail to bridge the context correctly but some help to point us in the right direction would be greatly appreciated. Thanks in advance.

  • edited May 2021

    @iamjannisgrimm
    that is right, the line let _: NextViewController = bridge(ptr: context!) does nothing since @Mitch2446 doesn't use it.

    // Some class 
    class MyClass {
    
        // Some Data Point Saving function
        func newDataPoint(value: CGFloat) {
          // Do something with data
        }
    
      // Some MetaWear function stuff that happens at some point
      mbl_mw_datasignal_subscribe(accel, bridge(obj: self)) { (context, data) in
        // Creating a bridge to the MyClass for the mbl_mw_datasignal_subscribe callback
        let _self: MyClass = bridge(ptr: context!)
        let point: MblMwCartesianFloat = data!.pointee.valueAs()
        // Callback will know to call my function newDataPoint 
        // and knows it belongs to MyClass thanks to my bridge
        _self.newDataPoint(value: CGFloat(point.magnitude))
      }
    
    }
    
  • edited May 2021

    @Mitch2446, it should be something like:

    @IBAction func startPressed(_ sender: Any) {
        let board = device.board
        guard mbl_mw_metawearboard_lookup_module(board, MBL_MW_MODULE_ACCELEROMETER) != MODULE_TYPE_NA else{
            print("No accelerometer")
            return
            }
        let signal = mbl_mw_acc_get_acceleration_data_signal(board)
    
        mbl_mw_datasignal_subscribe(signal, bridge(obj: self)) { (context, data) in
            let contBridge: NextViewController = bridge(ptr: context!)
            let obj: MblMwCartesianFloat = data!.pointee.valueAs()
            if (obj.x>0.2||obj.y>0.2){
                print(obj.x, obj.y, obj.z)
            //should be legit if NextViewController has some adding method
                contBridge.adding(obj.x, obj.y, obj.z) 
            //prob would be contBridge.savemyData(obj.x, obj.y, obj.z) 
                }
            }
        mbl_mw_acc_enable_acceleration_sampling(board)
        mbl_mw_acc_start(board)
    }
    
  • Hi @Laura,
    I'm getting the same error as the name of this discussion thread, but I'm already doing what the supposed solution is:

    var handlers = MblMwLogDownloadHandler()
    handlers.context = Unmanaged.passRetained(self).toOpaque()
    handlers.received_progress_update = { (observer, remainingEntries, totalEntries) in
        let mySelf = Unmanaged<AnyObject>.fromOpaque(observer!).takeRetainedValue() // get back self
        let progress = Double(totalEntries - remainingEntries) / Double(totalEntries)
        print("Swift: Log download progress: \(progress)")
        if remainingEntries == 0 {
            print("Swift: Done downloading log :D")
            mySelf.notifyListeners("logFinished\(ID)", data: nil)
        }
    }
    

    (Unmanaged.passRetained(self).toOpaque() is equivalent to bridgeRetained(obj: self) and Unmanaged<AnyObject>.fromOpaque(observer!).takeRetainedValue() is equivalent to bridgeTransfer(ptr: observer!))

    The third line of the code gives the error.
    This is following the docs, so I am not sure how to fix this.

  • edited July 2022

    Hello @Nick,

    Is there a reason why you would want to use Unmanaged.passRetained instead of simply calling bridge? Bridge method definitely works, but I have never used Unmanaged Retained so I am not sure what is the direct relationship there.

    One thing I am wondering is that let contBridge: NextViewController = bridge(ptr: context!) cast the pointer to the class but Unmanaged<AnyObject> seems to imply mySelf is AnyObject, in that case would you still be able to call mySelf.notifyListeners?

  • edited July 2022

    Hi @JCagle95,

    I'm using Unmanaged.passRetained because a separate API that I'm using has a function called bridge that causes errors when trying to call it. However, if you look at the source code of bridge, it is simply just a wrapper of Unmanaged.passRetained.

    As for the second point, you are probably right, however, using Unmanaged<NameOfMyClass> still gives me the same error "A C function pointer cannot be formed from a closure that captures context".

  • I figured it out, the variable ID which I reference in the callback is defined in the enclosing function of my code snippet and is not a class-level variable, thus I had no way to access it within the callback. Removing the reference to the variable resolved my error.

Sign In or Register to comment.