I need to call a legacy C function (from swift) that expects a 3D array of Doubles as an argument. I am fairly new to Swift and have begun converting a large ObjC and C code base written for iOS and Mac over to Swift. The C code does a lot of complex astronomical math and for which Swift is just too cumbersome. I will not convert those, but I need to use them from Swift
The C function is declared like this and the .H file is visible to swift:
void readSWEDayData(double dData[DATA_ROWS_PER_DAY][NUM_PLANET_ELEMENTS][NUM_ELEMENTS_PER_PLANET]);
The Constants used in the declaration are defined to be:
DATA_ROWS_PER_DAY = 1
NUM_PLANET_ELEMENTS = 35
NUM_ELEMENTS_PER_PLANET = 4
I am struggling with declaring the array of doubles in a way that Swift will allow to be passed to the C function. I've tried several approaches.
First Approach:
I declare the array and call it like so:
var data = Array(repeating: Double(EPHEMERIS_NA), count:Int(DATA_ROWS_PER_DAY * NUM_PLANET_ELEMENTS * NUM_ELEMENTS_PER_PLANET))
readSWEDayData(&data)
I get this error: Cannot convert value of type 'UnsafeMutablePointer' to expected argument type 'UnsafeMutablePointer<((Double, Double, Double, Double),...
Second Approach:
If I declare the array this way:
var data = [(Double, Double, Double, Double)](repeating: (EPHEMERIS_NA, EPHEMERIS_NA, EPHEMERIS_NA, EPHEMERIS_NA), count: Int(NUM_PLANET_ELEMENTS))
readSWEDayData(&data)
I get this error: Cannot convert value of type 'UnsafeMutablePointer<(Double, Double, Double, Double)>' to expected argument type 'UnsafeMutablePointer<((Double, Double, Double, Double),
So, how the heck does one declare a 3D Array in Swift of a specific size so that it can be passed to a C Function?
The function needs an UnsafeMutablePointer to a 35-tuple of things, where each of those things are 4-tuples of Doubles. Yes, C arrays translate to tuples in Swift, because Swift doesn't have fixed size arrays. You could do:
var giantTuple = (
(EPHEMERIS_NA, EPHEMERIS_NA, EPHEMERIS_NA, EPHEMERIS_NA),
(EPHEMERIS_NA, EPHEMERIS_NA, EPHEMERIS_NA, EPHEMERIS_NA),
(EPHEMERIS_NA, EPHEMERIS_NA, EPHEMERIS_NA, EPHEMERIS_NA),
// 32 more times...
)
readSWEDayData(&giantTuple)
But I don't think you'd like that. You can create an array, and use some pointer magic to convert that to a tuple, as discussed in this Swift Forums post. In fact, that post is highly relevant to your situation.
To save some typing, we can write some type aliases first:
typealias Tuple35<T> = (T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T)
typealias Double4x35 = Tuple35<(Double, Double, Double, Double)>
Then we can do:
var giantTuple = Array(repeating: (EPHEMERIS_NA, EPHEMERIS_NA, EPHEMERIS_NA, EPHEMERIS_NA), count: NUM_PLANET_ELEMENTS).withUnsafeBytes { p in
p.bindMemory(to: Double4x35.self)[0]
}
readSWEDayData(&giantTuple)
This works because tuples and arrays have essentially the same "layout" in memory.
Note that I "cheated" a little bit here, since DATA_ROWS_PER_DAY is 1, you can just create one such giantTuple, and get a pointer to it. However, if it is greater than 1, you'd have to do something like:
var giantTuples = Array(repeating:
Array(repeating: (EPHEMERIS_NA, EPHEMERIS_NA, EPHEMERIS_NA, EPHEMERIS_NA), count: NUM_PLANET_ELEMENTS).withUnsafeBytes { p in
p.bindMemory(to: Double4x35.self)[0]
},
count: DATA_ROWS_PER_DAY)
readSWEDayData(&giantTuples)
To convert from the giant tuple back to an array, you can do something like this:
// converting the first giantTuples in "giantTuples" as an example
let arrayOf4Tuples = asCollection(giantTuples[0], Array.init)
let finalArray = arrayOf4Tuples.map { asCollection($0, Array.init) }
// these are adapted from the Swift forum thread
// you'll need two of these, because you have 2 types of tuples
// yes, working with C arrays is hard :(
func asCollection<T, E>(_ tuple: Tuple35<E>, _ perform: (UnsafeBufferPointer<E>)->T) -> T {
return withUnsafeBytes(of: tuple) { ptr in
let buffer = ptr.bindMemory(to: (E.self))
return perform(buffer)
}
}
func asCollection<T, E>(_ tuple: (E, E, E, E), _ perform: (UnsafeBufferPointer<E>)->T) -> T {
return withUnsafeBytes(of: tuple) { ptr in
let buffer = ptr.bindMemory(to: (E.self))
return perform(buffer)
}
}
Because Swift 5 lacks support for interoperability with C language multi-dimensional Arrays of fixed size except via tuples of explicitly declared structure (See Sweeper's answer above) and which is something I wish to avoid to keep my code flexible for future changes to the C Library being used, I opted to write a wrapper for the C function and make it appear to Swift as a 1 dimensional array.
This was necessary because the Constants used in the C Code change when readSWEDayData increases the array sizes to support additional elements and tuple declarations like this:
let Double4x35 = Tuple35<(Double, Double, Double, Double)>
will DEFINITELY break in a way that will be hard to find:
So my C wrapper function looks like so:
void readSWEDayDataForSwift(double *dData) {
readSWEDayData((double (*)[NUM_PLANET_ELEMENTS][NUM_ELEMENTS_PER_PLANET])dData);
}
Making it easy to call it from Swift like so:
var data = Array(repeating: Double(EPHEMERIS_NA), count:Int(DATA_ROWS_PER_DAY * NUM_PLANET_ELEMENTS * NUM_ELEMENTS_PER_PLANET))
I was surprised that this far into Swift's evolution there is no better way to do this!
My two cents for others..Hoping will help.
I got a similar problem, but hope can save time for other.
I had to pass down:
path (from String to char *)
title (from String to char *)
columns ([String] to array of char *)
a counter
to sum up I had to call "C" function:
bool OpenXLSXManager_saveIn(const char * cFullPath,
const char * sheetName,
char *const columnTitles[],
double *const values[],
int columnCount);
I started from excellent:
// https://oleb.net/blog/2016/10/swift-array-of-c-strings/
expanded a bit:
public func withArrayOfCStringsAndValues<R>(
_ args: [String],
_ values: [[Double]],
_ body: ([UnsafeMutablePointer<CChar>?] , [UnsafeMutablePointer<Double>?] ) -> R ) -> R {
var cStrings = args.map { strdup($0) }
cStrings.append(nil)
let cValuesArrr = values.map { (numbers: [Double]) -> UnsafeMutablePointer<Double> in
let pointer = UnsafeMutablePointer<Double>.allocate(capacity: numbers.count)
for (index, value) in numbers.enumerated() {
pointer.advanced(by: index).pointee = value
}
return pointer
}
defer {
cStrings.forEach { free($0) }
for pointer in cValuesArrr{
pointer.deallocate()
}
}
return body(cStrings, cValuesArrr)
}
so I can call:
func passDown(
filePath: String,
sheetName:
String,
colNames: [String],
values: [[Double]]
) -> Bool
{
let columnCount = Int32(colNames.count)
return withArrayOfCStringsAndValues(colNames, values) {
columnTitles, values in
let retval = OpenXLSXManager_saveIn(filePath, sheetName, columnTitles, values, columnCount)
return retval
}
}
(SORRY for formatting, S.O. formatter has BIG issues ..)
var nums = [1,2,3]
let emptyArray : [Int] = []
let sum1 = nums.reduce(emptyArray){ $0.append($1)}
let sum2 = nums.reduce(emptyArray){ total, element in
total.append(element)
}
let sum3 = nums.reduce(emptyArray){ total, element in
return total.append(element)
}
For all three approaches I'm getting the following error:
Type of expression ambiguous without more context
But looking at documentation and the method signature of reduce:
func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result
You can see that both the Result and Element can be correctly inferred. Result is obviously of type [Int] and Element is of type [Int].
So I'm not sure what's wrong. I also saw here but that doesn't help either
You're right that you're passing the correct types to be inferred. The error is misleading.
Had you instead wrote:
func append<T>(_ element: T, to array: [T]) -> [T]{
let newArray = array.append(element)
return newArray
}
Then the compiler would have given the correct error:
Cannot use mutating member on immutable value: 'array' is a 'let'
constant
So now we know what the correct error should have been:
That is both the Result and the Element are immutable within the closure. You have to think of it just like a normal func add(a:Int, b:Int) -> Int where a & b are both immutable.
If you want it to work you just need a temporary variable:
let sum1 = nums.reduce(emptyArray){
let temp = $0
temp.append($1)
return temp
}
Also note that the following is is wrong!
let sum3 = nums.reduce(emptyArray){ total, element in
var _total = total
return _total.append(element)
}
Why?
Because the type of _total.append(element) is Void it's a function. Its type is not like the type of 5 + 3 ie Int or [5] + [3] ie [Int]
Hence you have to do:
let sum3 = nums.reduce(emptyArray){ total, element in
var _total = total
_total.append(element)
return _total
}
The code below is an example for Array declaration in Kotlin,
fun main(args: Array<String>) {
var a = Array<Int>(2){0}
a[0] = 100
a[1] = 200
print(a[1])
}
Here variable a is an array of size 2 and having values 100 and 200 and it is printing the value of a[1] as 200.
My question is -> What is the role of "0" in var a = Array(2){0}?
I changed the value of "0" to some other integer value, still it is working fine, but i was not able to find the use case of it. Can anyone explain it?
Any help will be appreciated.
Thanks.
The 0 is what you initialize each element of your array (2 in your case) with, using the following constructor:
public inline constructor(size: Int, init: (Int) -> T)
You can make this visible by printing the array directly after its initialization:
var a = Array<Int>(2){0}
println(a.contentToString())
Please consider the use of arrayOf(0,0) for such a simple use case, which is more idiomatic.
Arrays in Kotlin are represented by the Array class, that has get and set functions (that turn into [] by operator overloading conventions), and size property, along with a few other useful member functions:
class Array<T> private constructor() {
val size: Int
operator fun get(index: Int): T
operator fun set(index: Int, value: T): Unit
operator fun iterator(): Iterator<T>
// ...
}
You can write
var a = Array(2){0}
Creates a new array with the specified [size], where each element is calculated by calling the specified
* [init] function. The [init] function returns an array element given its index.
public inline constructor(size: Int, init: (Int) -> T)
Read Arrays in Kotlin.
How can I declare an array of int in xtend?
I've tried ArrayList but I get the error "The primitive 'int' cannot be a type argument".
Please refer to the docs for details, but essentially its something along these lines:
val int[] x = newIntArrayOfSize(5)
or if you want to define an array literal:
val int[] x = #[1, 2, 3]
I have some problem to cast my objeсt Variable[A] where A <: Array[_]
I create a function to compare manifest and cast data into Array to the good type.
My object Variable[A] store a Manifest[A] into the def 'type'
I make a plugin of an existent software, so it's not me which instanciate this Variable with good type.
Prototype object and class :
object Prototype {
def apply[T](n: String)(implicit t: Manifest[T]) = new Prototype[T] {
val name = n
val `type` = t
}
}
trait Prototype[T] {
def name: String
def `type`: Manifest[T]
}
Variable Object and Class :
object Variable {
def apply[T](p: Prototype[T], v: T) = new Variable[T] {
val prototype = p
val value = v
}
}
trait Variable[T] {
def prototype: Prototype[T]
def value: T
}
My class which use :
class XYDataReader[A <: Array[_]](var data: Iterable[Variable[A]]) {
def get[T](variable: Variable[A])(implicit m: Manifest[T]): Option[T] = {
if (variable.prototype.`type` <:< m) {
Some(variable.value.asInstanceOf[T])
} else {
None
}
}
}
There is probably a mistake of my part when i instanciate Variable object used to compare, so i give also the code of instanciation :
val v:List[Any] = List[Any](1.2,2,3)
val p = Prototype[Array[Any]]("col1")
val myVariable = Variable(p, v.toArray(ClassTag(p.`type`.runtimeClass)))
I don't understand why pattern matching failed when i call get[Array[Double]](myVariable) where myVariable.value contain an Array[Double]
When i println() the two manifest :
variable array type : Array[double]
m type : Array[Double]
It seem an Array[Double] is not an Array[double], how can i resolve/cast this ?
This started out as a comment, since it's not an answer, but it's too big and needs formatting (plus my browser tab's auto-reload caused it to be lost the first time...)
So... For starters, your snippet of code is incomplete and / or incorrect. Potentially there are imports in effect which could alter the meaning of that code. Secondly, as shown it would not compile 'cause what appears to be a formal type parameter, the A has no binding. Thus unless you have an actual type named A that won't compile.
Secondly, Double is potentially ambiguous. There is both scala.Double and java.lang.Double and they are distinct. Scala auto-boxes and -unboxes primitive types for you, typically when they're used to instantiate type parameters for generic methods (and specialization is not used). A consequence of this is that Array[scala.Double] is distinct from Array[java.lang.Double]. Scala will create arrays of primitive types when possible, but Array[java.lang.Double] is explicitly an array of boxed double-precision floating point.
E.g.:
scala> val d1: scala.Double = 123.456
d1: Double = 123.456
scala> val d2: java.lang.Double = 234.567
d2: Double = 234.567
scala> d1.getClass
res25: Class[Double] = double
scala> d2.getClass
res26: Class[_ <: Double] = class java.lang.Double
scala> val ad1: Array[scala.Double] = Array(123.456, 234.567)
ad1: Array[Double] = Array(123.456, 234.567)
scala> val ad2: Array[java.lang.Double] = Array(234.567, 345.678)
ad2: Array[Double] = Array(234.567, 345.678)
scala> ad1.getClass
res27: Class[_ <: Array[Double]] = class [D
scala> ad2.getClass
res28: Class[_ <: Array[Double]] = class [Ljava.lang.Double;
So please, if you would, fill in the missing details of your sample code?
Finally, with help of colleague methods to resursively unArrayify Array, i resolve my runtime type reification problem. Now i can compare equality between Array[double] with Array[Double].
// Convert unknow A en Array[T], so you need to call get with Type :
// example : get[Array[Double](myVariable)
// return an Array[Double] only if it's possible for this Variable, else it return None
def get[T](variable: Variable[A])(implicit m: Manifest[T]): Option[T] = {
if (ClassUtils.assignable(variable.prototype.`type`.runtimeClass, m.runtimeClass)) {
val casted = variable.prototype.`type`.runtimeClass.cast(variable.value)
Some(casted.asInstanceOf[T])
} else {
None
}
I hope these methods can help other people :)
You can see helping method ClassUtils.assignable here :
https://gist.github.com/4686167
and on the source forge project :
https://forge.iscpif.fr/projects/openmole/repository/revisions/master/entry/core/openmole/misc/org.openmole.misc.tools/src/main/scala/org/openmole/misc/tools/obj/ClassUtils.scala