I can't understand the reason...
Error during code execution: Thread 2: EXC_BAD_ACCESS
slv.hpp
struct RoutingData {
int locationsCount;
int64_t const * const * distanceMatrix;
int64_t const * const * durationMatrix;
};
main.cpp
void Solve(const struct RoutingData *routingData) {
std::vector<std::vector<int64>> durations (routingData->locationsCount, std::vector<int64>(routingData->locationsCount, 0LL));
for (std::size_t fromNode = 0; fromNode < routingData->locationsCount; fromNode++) {
for (std::size_t toNode = 0; toNode < routingData->locationsCount; toNode++) {
durations[fromNode][toNode] = routingData->durationMatrix[fromNode][toNode];
}
}
}
or
void Solve(const struct RoutingData *routingData) {
std::vector<std::vector<int64>> durations (routingData->locationsCount, std::vector<int64>(routingData->locationsCount, 0LL));
printf("%lld\n", routingData->durationMatrix[0][0]);
}
Solve function called from Swift:
var s = RoutingData()
s.locationsCount = Int32(self.distanceResponse.distances.count)
s.distanceMatrix = UnsafePointer(self.distanceResponse.distances.map{UnsafePointer($0.map{Int64($0)})})
s.durationMatrix = UnsafePointer(self.distanceResponse.durations.map{UnsafePointer($0.map{Int64($0)})})
Solve(&s)
In debugger:
Before vector init:
*durationMatrix const int64_t *const 0x102ce5650 0x0000000102ce5650
After:
*durationMatrix const int64_t *const NULL 0x0000000000000000
jrose
(Jordan Rose)
2
You're making pointers to the contents of Arrays, and then those arrays immediately get deallocated because they're not referenced anymore. The Array-to-pointer conversion is also only valid for the duration of the call where the Array is passed as an argument…which in this case is the call to UnsafePointer's initializer. (We have an open bug to add a warning for this kind of code.)
There's not a great way to safely build C arrays of pointers to C arrays from a Swift Array-of-Arrays; the best I could come up with involves manually allocating some C arrays and cleaning them up afterwards.
let distances: [UnsafeMutableBuffer<Int64>] = self.distanceResponse.distances.map {
let buffer = UnsafeMutableBuffer<UInt64>.allocate(capacity: $0.count)
buffer.initialize(from: $0.lazy.map { Int64($0) })
return buffer
}
defer {
distances.forEach { $0.deallocate() }
}
// similar for "durations"
distances.withUnsafeBufferPointer { distancesBuffer in
durations.withUnsafeBufferPointer { durationsBuffer in
assert(distancesBuffer.count == durationsBuffer.count)
var s = RoutingData(
locationsCount: Int32(distancesBuffer.count),
distanceMatrix: distancesBuffer.baseAddress
durationMatrix: durationsBuffer.baseAddress)
Solve(&s)
}
}
That's a lot messier, to be sure, but it makes all the lifetimes explicit.
Thank you for your reply!
I will definitely try your solution tomorrow.
What do you mean about manually allocating C arrays? Function that takes size of array allocate array and return pointer (**) to Swift?
jrose
(Jordan Rose)
4
I'm referring to the use of UnsafeMutableBuffer<UInt64>.allocate (and only thinking about a single level of pointer here). If you had a single-dimensional array, I'd suggest just using Swift's Array type, which (as shown for the top-level arrays) can provide a pointer to its contents within a bounded scope.* But since you can't get pointers to every sub-Array of a top-level Array in this way, I gave up on trying to use Array for the inner arrays, and instead allocated my own memory.
* Swift.Array also supports being implicitly converted to a pointer when used as an argument, but the pointer isn't guaranteed to be valid after the call is over, so it's only useful as a shorthand for the bounded scope version
1 Like