Search code examples
cswiftlanguage-interoperability

Passing swift string to c function char *


I am trying to pass a string argument from swift function wrapper to C function which takes char*.

This a c function

long swe_fixstar2_ut(char* star, double tjd_ut, long iflag, double* xx, char* serr);

The parameter star must provide for at least 41 characters for the returned star name. If a star is found, its name is returned. This function searches the star name from the txt file.

When imported to Swift function looks like this

swe_fixstar_ut(star: UnsafeMutablePointer<Int8>!, tjd_ut: Double, iflag: int32,
                xx: UnsafeMutablePointer<Double>!, serr: UnsafeMutablePointer<Int8>!)

I want to achieve something like this

public func sweFixStarsUT(star: String, tjdUT: Double, iFlag: Int32) {
      let xx: UnsafeMutablePointer = UnsafeMutablePointer<Double>.allocate(capacity:6)
      let serr:UnsafeMutablePointer = UnsafeMutablePointer<CChar>.allocate(capacity:256)
      swe_fixstar_ut(star, tjdUT, iFlag, xx, serr)
}

I looked around few of the similar questions but it doesn't solve my problem.

Convert a Swift Array of String to a to a C string array pointer

How to pass an array of Swift strings to a C function taking a char ** parameter

Actually this function comes from Swiss ephemeris C library. Here is the link if you guys are interested to look

https://www.astro.com/swisseph/swephprg.htm#_Toc505244846


Solution

  • As far as I read the doc, the parameter star is used for both in and out, so your star of the Swift function should be inout.

    And long is imported as Int, and in Apple's 64-bit platforms, it represents 64-bit signed integer type, if it is actually 32-bit, you may need to update the source files of your C code. I assume it as Int.

    So, I would write the bridging code like this:

    public func sweFixStarsUT(star: inout String, tjdUT: Double, iFlag: Int) {
        let starLen = max(star.utf8.count, 41)
        var starBuf: [CChar] = Array(repeating: 0, count: starLen+1)
        strcpy(&starBuf, star)
        var xx: [Double] = Array(repeating: 0.0, count: 6)
        var serr: [CChar] = Array(repeating: 0, count: 256)
        swe_fixstar2_ut(&starBuf, tjdUT, iFlag, &xx, &serr)
        star = String(cString: starBuf)
    }
    

    I prefer using Arrays when passing pointers to a C-function, when the function does not keep the pointers for later use. With using Arrays, you have no need to worry about deallocating.

    You can see how the code is converting the input star to an Array of CChar and coverting back the Array into String.

    If you find something wrong with this code, please tell me.