Compiler Version Directive

Hello everybody,

I'd like to contribute to this thread with a sample code. It not only exemplifies this thread, but this other one as well: C interoperability, combinations of library and OS versions thread.

It shows how many different techniques have to be used in order to crawl through compiler options:

  • #if CUSTOM_OPTION
  • #if swift(...) || ... (the monster @hartbit wants to replace with #if compiler(...))
  • if #available(...)
  • the really nice @jrose's workaround against code duplication.
  • very long comments because otherwise this piece of code is really really challenging.
static func api(_ db: Database) -> UnsafePointer<fts5_api> {
    // Access to FTS5 is one of the rare SQLite api which was broken in
    // SQLite 3.20.0+, for security reasons:
    //
    // Starting SQLite 3.20.0+, we need to use the new sqlite3_bind_pointer api.
    // The previous way to access FTS5 does not work any longer.
    //
    // So let's see which SQLite version we are linked against:
    
    #if GRDBCUSTOMSQLITE || GRDBCIPHER
        // GRDB is linked against SQLCipher or a custom SQLite build: SQLite 3.20.0 or more.
        return api_v2(db, sqlite3_prepare_v3, sqlite3_bind_pointer)
    #else
        // GRDB is linked against the system SQLite.
        //
        // Do we use SQLite 3.19.3 (iOS 11.4), or SQLite 3.24.0 (iOS 12.0)?
        // We need to check for available(iOS 12.0, OSX 10.14, watchOS 5.0, *).
        //
        // This test requires the Swift 4.2 compiler.
        //
        // It does not need the Swift 4.2 language, though: the Swift 4.2
        // compiler running in Swift 4.0 compatibility mode is OK.
        //
        // On top of that, we want to preserve compatibility with Xcode 9.3+.
        //
        // So let's check exactly which compiler version we are using.
        //
        // Fortunately, this horribly complex check has been solved
        // by @hartbit: see https://forums.swift.org/t/compiler-version-directive/11952
        // and https://github.com/hartbit/swift-evolution/blob/compiler-directive/proposals/XXXX-compiler-version-directive.md
        #if swift(>=4.1.50) || (swift(>=3.4) && !swift(>=4.0))
            if #available(iOS 12.0, OSX 10.14, watchOS 5.0, *) {
                // SQLite 3.24.0 or more
                // setup: Xcode 10.0, SWIFT_VERSION = 4.0, iOS 12
                // setup: Xcode 10.0, SWIFT_VERSION = 4.2, iOS 12
                return api_v2(db, sqlite3_prepare_v3, sqlite3_bind_pointer)
            } else {
                // SQLite 3.19.3 or less
                // setup: Xcode 10.0, SWIFT_VERSION = 4.0, iOS 11
                // setup: Xcode 10.0, SWIFT_VERSION = 4.2, iOS 11
                return api_v1(db)
            }
        #else
            // SQLite 3.19.3 or less
            // setup: Xcode 9.4.1, iOS 11
            return api_v1(db)
        #endif
    #endif
}

private static func api_v1(_ db: Database) -> UnsafePointer<fts5_api> {
    // Code that targets SQLite before 3.20.0
    ...
}

private static func api_v2(
    _ db: Database,
    _ sqlite3_prepare_v3: @convention(c) (OpaquePointer?, UnsafePointer<Int8>?, Int32, UInt32, UnsafeMutablePointer<OpaquePointer?>?, UnsafeMutablePointer<UnsafePointer<Int8>?>?) -> Int32,
    _ sqlite3_bind_pointer: @convention(c) (OpaquePointer?, Int32, UnsafeMutableRawPointer?, UnsafePointer<Int8>?, (@convention(c) (UnsafeMutableRawPointer?) -> Void)?) -> Int32)
    -> UnsafePointer<fts5_api>
{
    // Code that targets SQLite 3.20.0+
    ...
}

full file