2

As a C++ dev picking up Swift, I'm wondering how, in a struct or other class, to store a "pointer" or "reference" to a property of of another object of another class.

For example- a controlResponder class (or struct) that listens for events to modify the value target of a target in an effect class.

Control responder:

struct controlResponder {
    var ccNum : Int!  // CC number to respond to
    var target : Double! // Points to the variable of another object
    var min : Double!
    var max : Double!
}

Effects, in my case, can be of different classes but the thing that will be targeted for modification will always be a Double (To be precise- I'm working with AudioKit and am targeting effects like AKVariableDelay.time, or AKMoogLadderFilter.cutoff )

Any insight would be greatly appreciated, thanks!

Below is an abridged version of some of my actual (non functioning) code:

import Foundation
import AudioKit
import SwiftyJSON

class Effect : AKNode, AKMIDIListener {

  enum CustomError: Error {
    case badEffectName
  }

  struct ccListener {
    var ccNum : Int!
    var target : Int!
    var min : Double!
    var max : Double!
  }

  var listeners : [ccListener]

  var effectType = ""
  var effect = AKNode()
  var channel = 0
  var midi : AKMIDI

  init(connectionInput : AKNode, midi : AKMIDI, subJson : JSON, channel : Int) throws {

    self.midi = midi
    self.channel = channel
    var insertType = subJson["type"]

    if insertType == "distortion" {
      print("Adding a DISTORTION")
      effect = AKTanhDistortion(connectionInput)

      if let efx = effect as? AKTanhDistortion {
        let gainVal = random(subJson["gain random low"].doubleValue, subJson["gain random high"].doubleValue)
        print("gainVal: \(gainVal)")
        efx.pregain = gainVal
      }

    }
    else if insertType == "moog" {

      print("Adding a MOOG FILTER")
      /// CUTOFF
      let cutoffVal = random(subJson["cutoff random low"].doubleValue, subJson["cutoff random high"].doubleValue)
      print("cutoffVal: \(cutoffVal)")
      /// RESONANCE
      let resonanceVal = random(subJson["resonance random low"].doubleValue, subJson["resonance random high"].doubleValue)
      print("resonanceVal: \(resonanceVal)")

      effect = AKMoogLadder(connectionInput,
        cutoffFrequency: cutoffVal,
        resonance: resonanceVal)
    }
    else {
      print("BAD EFFECT TYPE: \(insertType)")
      throw CustomError.badEffectName
    }

    /////// MIDIZ

    midi.openInput("vIn")

    super.init()

    for (key, cc) in subJson["ccs"] as JSON {
      let efx = effect as! AKMoogLadder
      listeners.append(ccListener(ccNum: cc["cc number"].intValue, target: efx.cutoffFrequency, min: cc["min"].doubleValue, max: cc["max"].doubleValue))
    }

    midi.addListener(self)
    print("End of Effect init()")
  }


  func receivedMIDIController(_ controller: MIDIByte, value: MIDIByte, channel: MIDIChannel) {
    print("Self channel: \(self.channel), incoming channel: \(Int(channel))")
      if self.channel == Int(channel){
        print("Effect got a CC!")
      }
    }

    func changeVal(ccNum: Int, newValue: Int) {

      for listener in listeners {
        if listener.ccNum == ccNum {
          listener.target = newValue
        }
      }

    }

  }
E.R.
  • 163
  • 7
  • 1
    If the question is not about C++ itself, please leave out the [tag:c++] tag. – Henri Menke Sep 17 '17 at 21:25
  • What you're asking is how to say `var target = «Some syntax»responder.target` and then `target = 0.5`, such that the original "responder"'s property value changes? – jscs Sep 17 '17 at 21:44
  • @JoshCaswell Essentially, yep! This "Effect" class will receive an event and I want to store an array mappings so that the incoming value of the event, will modify the actual value of a property in another object. – E.R. Sep 17 '17 at 22:30
  • If you're working in Swift 4, key paths should do the trick. – jscs Sep 18 '17 at 13:24

1 Answers1

1

In Swift, the only pointers that you can reliably store are those allocated with UnsafeMutablePointer.allocate. The pointers that you can get from the address-of operator and withUnsafeMutablePointer are cheats and they are only valid for a short time; using them beyond that point will have unpredictable results.

This means that, generally speaking, you can't store a pointer to a value type (struct instances) that was allocated with "automatic storage" (to borrow from C++ terminology). If you need to share value types, you need to wrap them, at some convenient level, in a reference type (class instances).

The most generic you could get would be to use a pair of closures, one that returns the value and another one that sets it. However, there's probably a less generic but more useful way to do it for your specific case.

zneak
  • 134,922
  • 42
  • 253
  • 328
  • Ah! So, if in the code above I were to make ccListener a class rather than a struct, if I init one and pass, say, `let newListener = ccListener(target: self.effect.cutoff)` (a double) as an init parameter, that would actually be a reference and not just the double value? – E.R. Sep 17 '17 at 22:37
  • @EricR, did you understand that a `class`'s `init` parameters become references by virtue of `class` being a reference type? If so, that's not what I meant, and it's not the case. `newListener` would be a reference (more like a C++ pointer, I guess), but its `target` would still be a value with automatic storage. You still can't create a long-term pointer to a value with automatic storage, but you can share the `newListener` reference with other components so that they can work on the right, shared `Double`. – zneak Sep 18 '17 at 04:05