"qsort_r" in debian Linux

Hi,
I was watching this video from Chris Eidhof
Mobile Warsaw Edition #1 - Chris Eidhof - Swift and C Interop - YouTube on Swift C inter op. Thought of trying it out on Linux.
(23:10 in the video if you want to seek through the timeline)

I was able to get the basic "qsort" function working. But when i try to use the "qsort_r" function as presented in the talk, Source KIT LSP failed to provide any "qsort_r". After some googling around I figured that "qsort_r" and "qsort_b" are supported as part of BSD. However there is GLibc version of "qsort_r" is available. But looks like "import Foundation" on linux doesn't support that?

Need some help and inputs on how to get the "qsort_r" API accessible in Debian Linux destributions?

Here is my code

import Foundation

var numbers = [24, 234, 2, 7]

extension Comparable {
  static func compare(l: UnsafeRawPointer?, r: UnsafeRawPointer?) -> Int32 {
    guard let left = l?.assumingMemoryBound(to: Self.self),
      let right = r?.assumingMemoryBound(to: Self.self)
    else { return -1 }
    if left.pointee < right.pointee {
      return -1
    } else if left.pointee > right.pointee {
      return 1
    } else {
      return 0
    }
  }
}

func compare(l: UnsafeRawPointer?, r: UnsafeRawPointer?) -> Int32 {
  guard let left = l?.assumingMemoryBound(to: Int.self),
    let right = r?.assumingMemoryBound(to: Int.self)
  else { return -1 }
  if left.pointee < right.pointee {
    return -1
  } else if left.pointee > right.pointee {
    return 1
  } else {
    return 0
  }
}

func quickSort<A:Comparable>(_ xs: [A]) -> [A] {
  var xs = xs
  var cmp = A.compare
  //qsort(&xs, xs.count, MemoryLayout<A.Type>.size, cmp) // This works 
  qsort_r(&xs, xs.count, MemoryLayout<A.Type>.stride, cmp)  // --> Not able to find "qsort_r"
  return xs
}

var input = numbers
// qsort(&input, input.count, MemoryLayout<Int>.size, compare)
//print(input)
print(quickSort(numbers))

The glibc version of qsort_r is guarded by a _GNU_SOURCE macro, which means you need to expose it indirectly through a C target. In essence, define a new C target in your SPM package that defines one function:

void qsort_r_shim(
    void *, size_t, size_t, int (*)(const void *, const void *, void *), void *
);

Then, in your shim.c file in that target, implement this by calling out to qsort_r.

Arguably we should be defining _GNU_SOURCE (rather, parsing system C headers in -std=gnuxx mode) on Linux platforms by default, as there's a lot of useful platform-idiomatic API behind that, and we shouldn't make people write unsafe shims to get at it.

It also seems like we should maybe allow C and C++ language version setting as safe flags in a SwiftPM build (e.g. -std=gnu2x)

3 Likes

To be clear, we should not define _GNU_SOURCE; rather we should make -std= a supported safe flag in SwiftPM, and maybe also change the default for the clang importer on Linux. The compiler (/ clang importer) in turn sets _GNU_SOURCE or not based on the specified standard version.

3 Likes

I suspect that this could be somewhat problematic as SPM moves closer to explicit module builds, since the -std values for all Clang modules would have to match throughout the graph for them to be compatible across all instances of ClangImporter that would (transitively) import them.

This would be less of an issue if @_implementationOnly import were made official, so that the specific flags used by arbitrary Clang modules don't become viral like this.

2 Likes

Even with implementation-only imports, you can still end up with a conflicting flags problem when debugging your own libraries. That’s not new (unsafeFlags has the same problem with the top-level target), but it’s something that should at least be acknowledged.

1 Like

Are there problems here which don't already exist with the cxxLanguageStandard setting?

Thank you @lukasa . This was the only working solution I could get it working.