0

I got a vector like this:

vector<Vec3f> myCoolVector

that gets filled with arrays like [1.0, 2.0, 3.1] and [4.2, 2.1, 7.7], ....


I'd like to convert the myCoolVector back to a 2d-array of floats to send it back to Swift as a simple multidimensional Float Array (like this: [[Float]]).

But what I get returned (to Swift) is a variable with this crazy type:

Optional<UnsafeMutablePointer<Optional<UnsafeMutablePointer<Float>>>>

...instead of just this type: [[Float]].


Please have a look at my code:

(1) Code - C++:

+(float **)myCoolFunction {
    vector<Vec3f> myCoolVector

    // here fillMyCoolVector() fills the myCoolVector - NICE

    float **floatArrayBackToSwift;
    floatArrayBackToSwift = new float*[myCoolVector.size()];

    for( size_t i = 0; i < myCoolVector.size(); i++ )
    {
        ary[i] = new float[3];
        ary[i][0] = myCoolVector[i][0];
        ary[i][1] = myCoolVector[i][1];
        ary[i][2] = myCoolVector[i][2];
    }
    return floatArrayBackToSwift;
}

(2) Code - Swift:

let floatArray = MyBridge.myCoolFunction() // should be [[1.0, 2.0, 3.1], [4.2, 2.1, 7.7], ...]
print(floatArray)

My question: How can I send the myCoolVector as a simple 2d-Array like [[Float]] back to Swift?

Any help would be very appreciated!

Jive Dadson
  • 16,680
  • 9
  • 52
  • 65
  • Don't know if this has anything to do with your issue, but `float**` is not a two-dimensional array. In addition, the way you created your "2d array" is not guaranteed to have contiguous data elements. Does Swift require contiguous data? – PaulMcKenzie Mar 25 '18 at 00:28
  • It may seem like a "crazy" type, but that's ***exactly*** what `float **` means in C++. All `T *` for any type `T` (note: this doesn't apply to references in C++) in C/C++ are just like `Optional`, in that they're a sum type whose set of valid values is composed of `nullptr` plus the set of all possible values of `T`. And pointers aren't *really* actually arrays. They happen to point to arrays, but there's no guarantee that a `float **` points to an "array" of floats (a contiguous set of `float` values in memory). There could be nothing there at all. – Alexander Mar 25 '18 at 00:29
  • I just like to send the vector back to Swift as [[Float]] type. How is this possible?? @Alexander –  Mar 25 '18 at 00:49
  • I understand, and I don't know, hence why I didn't answer your question. Merely explaining why you see the "crazy" behaviour that you see – Alexander Mar 25 '18 at 01:02
  • @JaneDeverly Did you read my comment? A `float**` is *not* a 2 dimensional array, it is merely a pointer to a pointer, nothing more, nothing less. I am a Swift know-nothing, so I'll ask again -- what is the format of a 2-dimensional array that Swift accepts? The way you created your "array" is far different than say [this example, which creates a contiguous array](https://stackoverflow.com/questions/21943621/how-to-create-a-contiguous-2d-array-in-c/21944048#21944048). If Swift expects a bonafide 2-d array (not one created in the way you created it), then your C++ example will not work. – PaulMcKenzie Mar 25 '18 at 02:15

1 Answers1

0

Your myCoolFunction returns float **, pointer to pointer to float. And any pointer types in C/C++ cannot be automatically converted into Swift Array, neither any other types in C/C++.

If you really want the result in Array<Array<Float>> (aka [[Float]]), you need to write some Swift code to convert Optional<UnsafeMutablePointer<Optional<UnsafeMutablePointer<Float>>>> to Array<Array<Float>>.

Something like this:

class MyWrappter {
    static func myCoolFunction(count: Int) -> [[Float]] {
        let ptrPtrFloat: UnsafeMutablePointer<UnsafeMutablePointer<Float>?>! = MyBridge.myCoolFunction()

        let bpPtrFloat = UnsafeBufferPointer(start: ptrPtrFloat, count: count)
        return bpPtrFloat.map {ptrFloat in
            [ptrFloat![0], ptrFloat![1], ptrFloat![2]]
        }
    }
}

Remember, your myCoolFunction does not return any info about the size of the vector<Vec3f>, so you may need to pass it to Swift side, with some other method.


Swift is not good at optimizing nested arrays. Better consider adopting simd.float3 or something like that.

OOPer
  • 47,149
  • 6
  • 107
  • 142
  • @JaneDeverly, I'm showing you a full code (only `import Foundation` omitted), which compiles perfectly in my Xcode 9.2. I suspect you have modified other parts of my code. – OOPer Mar 25 '18 at 09:15
  • I'm sorry, looks like a caching bug in Xcode - restarted and WORKS. However - Did I get it right? It's not possible to get the `count` of the array from the `MyBridge.myCoolFunction()`-call? –  Mar 25 '18 at 09:24
  • @JaneDeverly, right. Your `MyBridge.myCoolFunction()` just returns a pointer and does not contain an info about how many elements the pointer is pointing. – OOPer Mar 25 '18 at 09:29
  • Ok - thats pretty bad! Can I add some kind of `try-catch` routine to avoid the crash at `[ptrFloat![0], ptrFloat![1], ptrFloat![2]]` if the count is above the real length? @OOPer –  Mar 25 '18 at 09:34
  • @JaneDeverly, with your code, the inner arrays have always 3 elements, so you have no need to check with try-catch. And the outer array, once converted to a pointer, has no info where runtime check can use. So, try-catch may never work. Sometimes it crashes immediately, other times it runs with wrong data. You may need to write another method to return count or add a parameter to assign count. – OOPer Mar 25 '18 at 09:40
  • @JaneDeverly, I have been missing to note one thing. Your C++ code is actually written in Objective-C++, so, if rewrite your C++ code to use Objective-C types like `NSArray *`, the Swift side code can be simpler. – OOPer Mar 25 '18 at 11:42