How do I access program arguments in Swift? - swift

C and derivatives have argc and argv (and envp) parameters to their entry point functions, but Swift doesn't have one proper: top-level code is just code and it doesn't have parameters.
How can one access the equivalent of argc and argv in a Swift program?

Process was just renamed into CommandLine (since Swift 3.0 August 4 snapshot)
let arguments = CommandLine.arguments
(for some reason this wasn't mentioned on the changelog)

Process.arguments is your friend!
Fortunately this is much easier, and built in: no importing anything, no getting your hands dirty with C, objective or otherwise.
Consider this, let's call it args.swift:
Swift 2 version:
var c = 0;
for arg in Process.arguments {
println("argument \(c) is: \(arg)")
c++
}
Swift 3 version:
var c = 0;
for arg in CommandLine.arguments {
print("argument \(c) is: \(arg)")
c += 1
}
We can compile and run it like this:
$ swift -o args args.swift && ./args fee fi fo fum
argument 0 is: ./args
argument 1 is: fee
argument 2 is: fi
argument 3 is: fo
argument 4 is: fum
Note that the first argument is the program name, as you might expect.
It seems every argument is a String, as you might also expect.
I hope very much that Process becomes more useful as Swift matures, but right now it seems to only give you the arguments. Which is a lot, if you're trying to write a pure-Swift program.

As soon as your app is up I'd use the process info:
let args = NSProcessInfo.processInfo().arguments
print(args)
Nothing unsafe there, very convenient.
Note that you have to import Foundation (or Cocoa / UIKit).

For Swift 3 you can use this code:
let argc = CommandLine.argc
let argv = UnsafeMutableRawPointer(CommandLine.unsafeArgv).bindMemory(to: UnsafeMutablePointer<Int8>.self, capacity: Int(CommandLine.argc))
which is equivalent of argc and argv parameters used in Objective-C main function:
int main(int argc, char *argv[])
For older versions of Swift, you can use Process.argc and Process.unsafeArgv or C_ARGC and C_ARGV.
You can pass this variables to UIApplicationMain function in iOS app:
Swift 3:
let argc = CommandLine.argc
let argv = UnsafeMutableRawPointer(CommandLine.unsafeArgv).bindMemory(to: UnsafeMutablePointer<Int8>.self, capacity: Int(CommandLine.argc))
UIApplicationMain(argc, argv, nil, NSStringFromClass(AppDelegate.self))
previous Swift versions:
UIApplicationMain(Process.argc, Process.unsafeArgv, nil, NSStringFromClass(AppDelegate.self))
or:
UIApplicationMain(C_ARGC, C_ARGC, nil, NSStringFromClass(AppDelegate.self))
Objective-C:
int main(int argc, char *argv[])
{
#autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

import Foundation
println(C_ARGC) //CInt
println(C_ARGV) // CString
As in the above code, you can use C_ARGC to get number of arguments. C_ARGV to get this arguments.

An elegant alternative to CommandLine.arguments is the Swift Argument Parser. ArgumentParser will do the parsing of the command line arguments for you, mapping the arguments into a struct. It provides all the features you’d expect from a command line app including type safe parsing of arguments, automatic help features, short and long options, etc.
Just use the “Swift Package Manager” to add the ArgumentParser package to your command line project (e.g., in Xcode’s “File” » “Add Package” command).

Related

Swift C interop: Passing Swift Argv to C argv

I am working on a swift wrapper for a C library. One such function in this library expects the command line arguments, in the form of char const *const *. This is linked to swift as Optional<UnsafePointer<UnsafePointer<Int8>?>> From swift I can obtain the command line arguments as CommandLine.unsafeArgv, of type UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>. How can I convert this to the expected immutable type? I know UnsafePointer has a constructor that takes an UnsafeMutablePointer, but I'm unsure of how to handle the nested types. Suggestions on how to correctly convert this?
Try the following (tested with Xcode 12 / swift 5.3)
let values: UnsafePointer<UnsafePointer<Int8>?> =
UnsafeRawPointer(CommandLine.unsafeArgv).bindMemory(
to: UnsafePointer<Int8>?.self,
capacity: Int(CommandLine.argc)
)
your_api_func(Int(CommandLine.argc), values)
Alternate: (on #MartinR comment) tested & worked as well.
CommandLine.unsafeArgv.withMemoryRebound(
to: UnsafePointer<Int8>?.self,
capacity: Int(CommandLine.argc)) { ptr in
your_api_func(Int(CommandLine.argc), ptr)
}

Freeglut doesn't initialize when using it from Swift

I've tried to use the Freeglut library in a Swift 4 Project. When the
void glutInit(int *argcp, char **argv);
function is shifted to Swift, its declaration is
func glutInit(_ pargc: UnsafeMutablePointer<Int32>!, _ argv: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>!)
Since I don't need the real arguments from the command line I want to make up the two arguments. I tried to define **argv in the Bridging-Header.h file
#include <OpenGL/gl.h>
#include <GL/glut.h>
char ** argv[1] = {"t"};
and use them in main.swift
func main() {
var argcp: Int32 = 1
glutInit(&argcp, argv!) // EXC_BAD_ACCESS
glutInitDisplayMode(UInt32(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH));
glutCreateWindow("my project")
glutDisplayFunc(display)
initOpenGL()
glutMainLoop()
but with that I get Thread 1: EXC_BAD_ACCESS (code=1, address=0x74) at the line with glutInit().
How can I initialize glut properly? How can I get an UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>! so that it works?
The reason the right code in C char * argv[1] = {"t"}; does not work is because Swift imports fixed size C-array as a tuple, not a pointer to the first element.
But your char ** argv[1] = {"t"}; is completely wrong. Each Element of argv needs to be char **, but you assign char * ("t"). Xcode must have shown you a warning at first build:
warning: incompatible pointer types initializing 'char **' with an expression of type 'char [2]'
You should better take incompatible pointer types warning as error, unless you know what you are doing completely.
Generally, you should better not write some codes generating actual code/data like char * argv[1] = {"t"}; in a header file.
You can try it with Swift code.
As you know, when you want to pass a pointer to single element T, you declare a var of type T and pass &varName to the function you call.
As argcp in your code.
As well, when you want to pass a pointer to multiple element T, you declare a var of type [T] (Array<T>) and pass &arrName to the function you call.
(Ignoring immutable case to simplify.)
The parameter argv matches this case, where T == UnsafeMutablePointer<Int8>?.
So declare a var of type [UnsafeMutablePointer<Int8>?].
func main() {
var argc: Int32 = 1
var argv: [UnsafeMutablePointer<Int8>?] = [
strdup("t")
]
defer { argv.forEach{free($0)} }
glutInit(&argc, &argv)
//...
}
But I wonder if you really want to pass something to glutInit().
You can try something like this:
func main() {
var argc: Int32 = 0 //<- 0
glutInit(&argc, nil)
//...
}
I'm not sure if freeglut accept this, but you can find some articles on the web saying that this works in some implementation of Glut.

How can I use getopt with command line arguments in Swift 3?

I'm trying to use getopt with command line arguments in Swift 3. I have from Michele Dall'Agata's nice stackoverflow contribution:
let pattern = "abc:"
var buffer = Array( pattern.utf8 ).map { Int8($0) }
When I then use this code:
let option = Int( getopt( CommandLine.argc, CommandLine.arguments, buffer ) )
I get this error:
Cannot convert value of type '[String]' to expected argument type
'UnsafePointer<UnsafeMutablePointer<Int8>?>!'
for CommandLine.arguments, which I am trying to use as argv. Does anyone know the proper syntax for the 2nd argument for getopt? Thanks in advance!
#Hamish already answered the question and explained how to pass CommandLine.unsafeArgv to getopt() in Swift (and why).
Here is a complete self-contained example how a typical getopt
loop can be implemented in Swift 3:
var aFlag = false
var bFlag = false
var cValue: String?
while case let option = getopt(CommandLine.argc, CommandLine.unsafeArgv, "abc:"), option != -1 {
switch UnicodeScalar(CUnsignedChar(option)) {
case "a":
aFlag = true
case "b":
bFlag = true
case "c":
cValue = String(cString: optarg)
default:
fatalError("Unknown option")
}
}
print(aFlag, bFlag, cValue ?? "?")
Remarks:
You can pass a Swift string (here: "abc:") directly to a C
function expecting a (constant) C string, the compiler will automatically
generate a temporary UTF-8 representation.
getopt() return either -1 (if the argument list is exhausted) or an unsigned char converted to an int. Therefore it is safe to
convert the return value to CUnsignedChar (which is UInt8 in Swift).
while is used (abused?) with pattern matching plus an additional
boolean condition to implement the typical C pattern
while ((option = getopt(argc, argv, "abc:")) != -1) { ... }
in Swift.
CommandLine.arguments gives you a friendly Swift [String] of the arguments passed – however you're looking to send the arguments straight back to C. Therefore you can simply use CommandLine.unsafeArgv instead, which will give you the actual raw value of argv passed to your program.
let option = Int( getopt( CommandLine.argc, CommandLine.unsafeArgv, buffer ) )

Convert String to UnsafeMutablePointer<char_t> in Swift

I'm working with a third party c API I'm trying to call one of the functions with a simple string. Something like this:
some_c_func("aString");
I get a build error:
Type 'UnsafeMutablePointer<char_t>' does not conform to protocol 'StringLiteralConvertible'
I've seen some suggestions to use utf8 on String or similar conversions, which gets nearly there, but with the following error:
some_c_func("aString".cStringUsingEncoding(NSUTF8StringEncoding));
'UnsafePointer<Int8>' is not convertible to 'UnsafeMutablePointer<char_t>'
How can I create an UnsafeMutablePointer?
It all depends on what char_t is.
If char_t converts to Int8 then the following will work.
if let cString = str.cStringUsingEncoding(NSUTF8StringEncoding) {
some_c_func(strdup(cString))
}
This can be collapsed to
some_c_func(strdup(str.cStringUsingEncoding(NSUTF8StringEncoding)!))
WARNING! This second method will cause a crash if func cStringUsingEncoding(_:) returns nil.
Updating for Swift 3, and to fix memory leak
If the C string is only needed in a local scope, then no strdup() is needed.
guard let cString = str.cString(using: .utf8) else {
return
}
some_c_func(cString)
cString will have the same memory lifecycle as str (well similar at least).
If the C string needs to live outside the local scope, then you will need a copy. That copy will need to be freed.
guard let interimString = str.cString(using: .utf8), let cString = strdup(interimString) else {
return
}
some_c_func(cString)
//…
free(cString)
it may be simpler than that - many C APIs pass strings around as char * types, and swift treats these as unsafe.
try updating the C API (good) or hack it's header files (bad) to declare these as const char * instead.
in my experience this allows you to pass standard swift String types directly to the C API.
apparently a constant is required, in order to conform to the protocol.
I haven't tried passing strings like that, but I have a C function that I call from Swift, that takes a lot more parameters than shown here, among which is a reference to a Swift C typecast buffer to hold an error string. The compiler doesn't complain and the function call works. Hopefully this will steer you closer to the answer and you can provide an update with the final answer or someone else can.
var err = [CChar](count: 256, repeatedValue: 0)
var rv = somefunc((UnsafeMutablePointer<Int8>)(err))
if (rv < 0) {
println("Error \(err)")
return
}

Working with C strings in Swift, or: How to convert UnsafePointer<CChar> to CString

While playing with Standard C Library functions in Swift, I came across problems
when passing C strings around. As a simple example (just to demonstrate the problem), the Standard C Library function
char * strdup(const char *s1);
is exposed to Swift as
func strdup(_: CString) -> UnsafePointer<CChar>
which means that the return value of strdup() cannot be passed to another strdup() call:
let s1 : CString = "abc"
let s2 = strdup(s1) // OK, s2 is a UnsafePointer<CChar>
let s3 = strdup(s2) // error: could not find an overload for '__conversion' that accepts the supplied arguments
My question is: How to create a Swift CString from a UnsafePointer<CChar>,
so that the C string returned by one standard library function can be passed to another function?
The only way that I could find is (using code from How do you convert a String to a CString in the Swift Language?):
let s2a = String.fromCString(s2).bridgeToObjectiveC().UTF8String
let s3 = strdup(s2a)
But I do not find this satisfying for two reasons:
It is too complicated for a simple task.
(Main reason:) The above conversions works only if the C string is a valid UTF-8
string, otherwise it fails with a runtime exception. But a C string is an arbitrary
sequence of characters, delimited by a NUL character.
Remarks/Background: Of course, high-level functions using high-level data structures like Swift String or Objective-C NSString are preferable. But there are BSD functions in the
Standard C Library which do not have an exact counterpart in the Foundation frameworks.
I came across this problem while trying to answer Accessing temp directory in Swift.
Here, mkdtemp() is a BSD function for which no exact NSFileManager replacement exists
(as far as I know).
mkdtemp() returns a UnsafePointer<CChar> which has to be passed to the
NSFileManager function stringWithFileSystemRepresentation which takes a CString
argument.
Update: As of Xcode 6 beta 6, this problem does not exist anymore because the mapping of C-Strings into Swift has been simplified. You can just write
let s1 = "abc" // String
let s2 = strdup(s1) // UnsafeMutablePointer<Int8>
let s3 = strdup(s2) // UnsafeMutablePointer<Int8>
let s4 = String.fromCString(s3) // String
Swift 1.1 (or perhaps earlier) has even better C string bridging:
let haystack = "This is a simple string"
let needle = "simple"
let result = String.fromCString(strstr(haystack, needle))
The CString type is gone completely.
The String struct in Swift has an init routine you can use like:
let myString = String(cString: myUnsafePointer)
see also init(cString: UnsafePointer)