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 30

    @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 30

    @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)
    }
    
Sign In or Register to comment.