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+
...
}