I need to have a pointer array like in C, in Swift.
The following code works:
let ptr = UnsafeMutableBufferPointer<Int32>.allocate(capacity: 5)
ptr[0] = 1
ptr[1] = 5
print(ptr[0], ptr[1]) // outputs 1 5
The following code, however, does not work:
let ptr = UnsafeMutableBufferPointer<String>.allocate(capacity: 5)
print(ptr[0]) // Outputs an empty string (as expected)
print(ptr[1]) // Just exits with exit code 11
When I do print(ptr[1]) in the swift REPL, I get the following output:
Execution interrupted. Enter code to recover and continue.
Enter LLDB commands to investigate (type :help for assistance.)
How can I create a C-like array with Strings (or any other reference type, as this also doesn't seem to work with classes).
What should I adjust?
You need to initialize the memory with valid String data.
let values = ["First", "Last"]
let umbp = UnsafeMutableBufferPointer<String>.allocate(capacity: values.count)
_ = umbp.initialize(from: values)
print(umbp.map { $0 })
umbp[0] = "Joe"
umbp[1] = "Smith"
print(umbp.map { $0 })
Prints:
["First", "Last"]
["Joe", "Smith"]
Related
Context
From Swift, I am trying to call a specific function of the libxlsxwriter C library. The documentation is here: https://libxlsxwriter.github.io/worksheet_8h.html#a62bf44845ce9dcc505bf228999db5afa
The function assembles a "rich string" (the equivalent of an AttributedString, where different formats/styles apply to different ranges) and writes it to a specific cell in the Excel worksheet. In C, the function works like this:
lxw_format *bold = workbook_add_format(workbook);
format_set_bold(bold);
lxw_format *italic = workbook_add_format(workbook);
format_set_italic(italic);
lxw_rich_string_tuple fragment11 = {.format = NULL, .string = "This is " };
lxw_rich_string_tuple fragment12 = {.format = bold, .string = "bold" };
lxw_rich_string_tuple fragment13 = {.format = NULL, .string = " and this is "};
lxw_rich_string_tuple fragment14 = {.format = italic, .string = "italic" };
lxw_rich_string_tuple *rich_string1[] = {&fragment11, &fragment12,
&fragment13, &fragment14, NULL};
worksheet_write_rich_string(worksheet, CELL("A1"), rich_string1, NULL);
The Problem
I have an array of lxw_rich_string_tuple structs, but I'm unclear how to convert this into the array of pointers that worksheet_write_rich_string() accepts:
// The worksheet object and `lxw_format` objects are already existent.
// This array contains multiple well-formed `lxw_rich_string_tuple` structs, which I can see in the debugger.
//
var rawTuples: [lxw_rich_string_tuple] = ...
// Parameters:
// - the worksheet on which to write this string
// - the row of the cell in which to write
// - the column of the cell in which to write
// - a null-terminated array of pointers to `lxw_rich_string_tuple` structs
// - an optional format object to use, null in this case.
//
worksheet_write_rich_string(worksheet, 0, 1, ?, NULL);
The trouble is the ?. I've tried all sorts of withUnsafeBytes and withUnsafeMutableBytes and UnsafeMutableRawPointer().bindMemory(to:capacity:) and I cannot figure out the magic Swift gibberish to do what is such a SIMPLE thing in C. Thanks.
My Attempt
This gives no compiler errors, but crashes with a Bad Access exception:
let argsSize: Int = rawTuples.count
rawTuples.withUnsafeMutableBufferPointer { rawTuplesPointer in
let ptr = UnsafeMutableRawPointer(rawTuplesPointer.baseAddress!).bindMemory(to: lxw_rich_string_tuple.self, capacity: argsSize)
var tuplePointers: [UnsafeMutablePointer<lxw_rich_string_tuple>?] = []
for i in 0 ..< argsSize
{
let tp: UnsafeMutablePointer<lxw_rich_string_tuple>? = ptr + (i * MemoryLayout<lxw_rich_string_tuple>.stride)
tuplePointers.append(tp)
}
tuplePointers.append(nil)
tuplePointers.withUnsafeBufferPointer { tpPointer in
let m = UnsafeMutablePointer(mutating: tpPointer.baseAddress)
worksheet_write_rich_string(lxw_worksheet, cell.row, cell.col, m, nil)
}
}
Try this:
rawTuples.withUnsafeMutableBufferPointer { p in
guard let arrBaseAddress = p.baseAddress else { return }
var pointersToEachArrayElement: [UnsafeMutablePointer<_>?] =
Array(arrBaseAddress ..< arrBaseAddress.advanced(by: p.count))
pointersToEachArrayElement.append(nil)
pointersToEachArrayElement.withUnsafeMutableBufferPointer { q in
// use q.baseAddress in the call to worksheet_write_rich_string
}
}
The idea is similar to your attempt - to create an array of pointers to each of the array elements. But unlike your attempt, I avoided calling the initialisers of the pointer types (which I don't think you are supposed to do), and instead tried to use the withXXX functions as much as I could.
You should also consider just writing Objective-C wrappers around the C function and lxw_rich_string_tuple. Sometimes C functions are just not bridged into Swift in a very convenient way, and this is not the first time I've experienced something like this, unfortunately :(
I was surprised that this swift code behaves nicely:
let values = ["Hello", "Test"]
var count = 0
for string: String in values {
count = count + 1
print("count is: ", count)
print(string)
}
with output of:
count is: 1
Hello
count is: 2
Test
but making the String into String? creates an infinite loop.
let values = ["Hello", "Test"]
var count = 0
for string: String? in values {
count = count + 1
print("count is: ", count)
print(string)
}
with output of:
count is: 1
Optional("Hello")
count is: 2
Optional("Test")
count is: 3
nil
count is: 4
nil
count is: 5
nil
count is: 6
nil
count is: 7
nil
count is: 8
(ad infinitum)
Swift has been so good at catching weird code problems that I was surprised I could walk into such a mess without warning or error.
Is this really what one would expect from Swift 4? And if so, why?
To understand this issue it helps to recollect how for-in loops work:
for s in values {
print(s)
}
creates an iterator of the sequence, and calls the iterator's next() method until that returns nil:
var it = values.makeIterator()
while let s = it.next() {
print(s)
}
Your second version is equivalent to
var it = values.makeIterator()
while let s: String? = it.next() {
print(s)
}
and now the compiler warns:
warning: explicitly specified type 'String?' adds an additional level
of optional to the initializer, making the optional check always succeed
while let s: String? = it.next() {
^ ~~~~~~~ ~~~~~~~~~
String
So what happens here is that the String? returned from it.next()
is wrapped into a “nested optional” .some(it.next()) of type String??, which is then optionally bound to s: String?.
This does always succeed, because .some(it.next()) is not String??.none.
Therefore the loop never terminates.
One could argue that the compiler should warn as well for
for s: String? in values {
print(s)
}
Your "for" runs on indexes. Under the index in excess of the number of elements is nill.
There is no error. In the first function it will keep going until it no more strings.
But other hand the second function you set the string to optional so when its no more string it will still keep going. Because nil is a nil value and it’s not the same as nothing. You have nothing that ends that loop.
I was reading this article about high order functions
https://medium.com/#mimicatcodes/simple-higher-order-functions-in-swift-3-0-map-filter-reduce-and-flatmap-984fa00b2532
then I started playing around with them and found this:
let someArray = [1, 2, 3, 4, 5]
let newArray = someArray.map {
print($0)
$0 * 2
}
print(a)
this would return empty values in newArray
while removing the print function or specifically setting the function's return type would work:
let a = someArray.map { number -> Int in
print(number)
return number * 2
}
print(a)
Any explanations?
This is because if you use Shorthand Argument Names in closure you have to return from very first line i.e. inline return.
In your first closure you'r first line is print() which returns nothing.
If you do like this:
let newArray = someArray.map {
$0 * 2
}
Or simply this:
let newArray = someArray.map { $0 * 2 }
you will get the result.
Read more about this here in developer docs.
Update
As shown by #hamish, multiple calculations can be done with shorthand arguments but then we need to explicitly mention data type of result and also return statement as:
let newArray: [Int] = someArray.map {
print($0)
return $0 * 2
}
If you do not use return in a closure that should return something, then the result of the first statement is returned. In this case void (aka the empty tuple: () ) which is returned by the print function.
Calling functions like print in a map closure is bad practice since it could have side effects and should be avoided.
I just tried out a HackerRank challenge, and if a question gives you x lines of input, putting x lines of let someVariable = readLine() simply doesn't cut it, because there are lot's of test cases that shoot way more input to the code we write, so hard coded readLine() for each line of input won't fly.
Is there some way to get multiple lines of input into one variable?
For anyone else out there who's trying a HackerRank challenge for the first time, you might need to know a couple of things that you may have never come across. I only recently learned about this piece of magic called the readLine() command, which is a native function in Swift.
When the HackerRank system executes your code, it passes your code lines of input and this is a way of retrieving that input.
let line1 = readLine()
let line2 = readLine()
let line3 = readLine()
line1 is now given the value of the first line of input mentioned in the question (or delivered to your code by one of the test cases), with line2 being the second and so on.
Your code may work just great but may fail on a bunch of other test cases. These test cases don't send your code the same number of lines of input. Here's food for thought:
var string = ""
while let thing = readLine() {
string += thing + " "
}
print(string)
Now the string variable contains all the input there was to receive (as a String, in this case).
Hope that helps someone
:)
Definitely you shouldn't do this:
while let readString = readLine() {
s += readString
}
This because Swift will expect an input string (from readLine) forever and will never terminate, causing your application die by timeout.
Instead you should think in a for loop assuming you know how many lines you need to read, which is usually this way in HackerRank ;)
Try something like this:
let n = Int(readLine()!)! // Number of test cases
for _ in 1 ... n { // Loop from 1 to n
let line = readLine()! // Read a single line
// do something with input
}
If you know that each line is an integer, you can use this:
let line = Int(readLine()!)!
Or if you know each line is an array of integers, use this:
let line = readLine()!.characters.split(" ").map{ Int(String($0))! }
Or if each line is an array of strings:
let line = readLine()!.characters.split(" ").map{ String($0) }
I hope this helps.
For new version, to get an array of numbers separated by space
let numbers = readLine()!.components(separatedBy: [" "]).map { Int($0)! }
Using readLine() and AnyGenerator to construct a String array of the std input lines
readLine() will read from standard input line-by-line until EOF is hit, whereafter it returns nil.
Returns Characters read from standard input through the end of the
current line or until EOF is reached, or nil if EOF has already been
reached.
This is quite neat, as it makes readLine() a perfect candidate for generating a sequence using the AnyGenerator initializer init(body:) which recursively (as next()) invokes body, terminating in case body equals nil.
AnyGenerator
init(body: () -> Element?)
Create a GeneratorType instance whose next method invokes body
and returns the result.
With this, there's no need to actually supply the amount of lines we expect from standard input, and hence, we can catch all input from standard input e.g. into a String array, where each element corresponds to an input line:
let allLines = AnyGenerator { readLine() }.map{ $0 }
// type: Array<String>
After which we can work with the String array to apply whatever operations needed to solve a given task (/HackerRank task).
// example standard input
4 3
<tag1 value = "HelloWorld">
<tag2 name = "Name1">
</tag2>
</tag1>
tag1.tag2~name
tag1~name
tag1~value
/* resulting allLines array:
["4 3", "<tag1 value = \"HelloWorld\">",
"<tag2 name = \"Name1\">",
"</tag2>",
"</tag1>",
"tag1.tag2~name",
"tag1~name",
"tag1~value"] */
I recently discovered a neat trick to get a certain amount of lines. I'm gonna assume the first line gives you the amount of lines you get:
guard let count = readLine().flatMap({ Int($0) }) else { fatalError("No count") }
let lines = AnyGenerator{ readLine() }.prefix(count)
for line in lines {
}
I usually use this form.
if let line = readLine(), let cnt = Int(line) {
for _ in 1...cnt {
if let line = readLine() {
// your code for a line
}
}
}
Following the answer from dfrib, for Swift 3+, AnyIterator can be used instead of AnyGenerator, in the same way:
let allLines = AnyIterator { readLine() }.map{ $0 }
// type: Array<String>
I can't figure out how to load a string from a file and have variables referenced in that string be interpolated.
Let's say a text file at filePath that has these contents:
Hello there, \(name)!
I can load this file into a string with:
let string = String.stringWithContentsOfFile(filePath, encoding: NSUTF8StringEncoding, error: nil)!
In my class, I have loaded a name in: let name = "George"
I'd like this new string to interpolate the \(name) using my constant, so that its value is Hello there, George!. (In reality the text file is a much larger template with lots of strings that need to be swapped in.)
I see String has a convertFromStringInterpolation method but I can't figure out if that's the right way to do this. Does anyone have any ideas?
This cannot be done as you intend, because it goes against type safety at compile time (the compiler cannot check type safety on the variables that you are trying to refer to on the string file).
As a workaround, you can manually define a replacement table, as follows:
// Extend String to conform to the Printable protocol
extension String: Printable
{
public var description: String { return self }
}
var string = "Hello there, [firstName] [lastName]. You are [height]cm tall and [age] years old!"
let firstName = "John"
let lastName = "Appleseed"
let age = 33
let height = 1.74
let tokenTable: [String: Printable] = [
"[firstName]": firstName,
"[lastName]": lastName,
"[age]": age,
"[height]": height]
for (token, value) in tokenTable
{
string = string.stringByReplacingOccurrencesOfString(token, withString: value.description)
}
println(string)
// Prints: "Hello there, John Appleseed. You are 1.74cm tall and 33 years old!"
You can store entities of any type as the values of tokenTable, as long as they conform to the Printable protocol.
To automate things further, you could define the tokenTable constant in a separate Swift file, and auto-generate that file by using a separate script to extract the tokens from your string-containing file.
Note that this approach will probably be quite inefficient with very large string files (but not much more inefficient than reading the whole string into memory on the first place). If that is a problem, consider processing the string file in a buffered way.
There is no built in mechanism for doing this, you will have to create your own.
Here is an example of a VERY rudimentary version:
var values = [
"name": "George"
]
var textFromFile = "Hello there, <name>!"
var parts = split(textFromFile, {$0 == "<" || $0 == ">"}, maxSplit: 10, allowEmptySlices: true)
var output = ""
for index in 0 ..< parts.count {
if index % 2 == 0 {
// If it is even, it is not a variable
output += parts[index]
}
else {
// If it is odd, it is a variable so look it up
if let value = values[parts[index]] {
output += value
}
else {
output += "NOT_FOUND"
}
}
}
println(output) // "Hello there, George!"
Depending on your use case, you will probably have to make this much more robust.