Swift as a C language REPL
Systems programmers who frequently switch between operating systems suffer from POSIX amnesia. That’s when you know what POSIX function to use, but forget some edge cases, return codes, or boundary conditions. What are the default F_GETFL
status flags on a file descriptor opened with a O_NONBLOCK
? What would fcntl(fd, F_SETSIZE)
do on “/dev/null” on macOS?
Turns out, assuming you have Xcode installed, the swift
CLI doubles as a fun C REPL.
Run the swift
CLI from the terminal. In order for Swift REPL to pick up many of the standard POSIX functions you also have to import Foundation
.
Now, we can run C functions directly. Here’s open(2)
:
Invoking open("/dev/null", ...)
comes back with a 3
, which is the expected return value. The return value (in green) is prefixed with a return value’s type, Int32
. The Swift’s Int32
(also aliased as CInt
) corresponds to C’s int
type, which is predictably 32-bit on LP64 platforms. The Swift’s Int
type, after all, can be expected to be 64-bit, so Int
is different from CInt
.
Let’s use Swift’s let
to capture the return value:
Now, let’s do something interesting. Let’s recover the file‘s path by its file descriptor. Unfortunately, this is going to be a little involving and Swift-heavy. Here’s what to cut-n-paste as you follow along:
import Foundation
let fd = open("/dev/null", O_RDONLY)
var buf = [UInt8](repeating: 0, count: .init(PATH_MAX))
buf.withUnsafeMutableBytes { ptr in
fcntl(fd, F_GETPATH, ptr.baseAddress!)
}
String(cString: buf)
After entering the last line (String(cString: buf)
) we get back the representation of the Swift’s String
value containing the path of the file:
Well, I think I might have just discouraged you from using Swift as a C REPL with all this withUnsafeMadness
complexity. Let’s try something simpler that does not require any buffer management. Let’s try to resize the /dev/null
file and see how it fails:
As you see, this looks much more manageable, and we even recovered a printable error message corresponding to the fcntl
failure.
As a final example, let’s see how to get the size of the dirent
structure using Swift. The MemoryLayout
can be used as C’s sizeof
substitute:
Well, this is it. The Swift REPL can be used to try out standard C functions, check out values of POSIX constants (and some #define
s) and sizes of structures. It can be handy or somewhat frustrating to invoke certain things, and at times you may need to know more Swift than you wanted to. But it works and it makes my day a little bit easier, on both macOS and Linux.