I have a std::vector<double>
containing M*N
values, and I'd like to reshape this into a double**
which behaves like a double[N][M]
multidimensional array.
These start and end points may seem strange, but they are unfortunately both decided by external libraries, so there's not much I can do about them. I'm asking this question to see if there's a way to accomplish this without simply copying all the data manually.
I did read this question, with an excellent answer, but it has slightly different starting point - rather than going from std::vector<double>
to double**
, it goes from double[]
to double[][]
. I tried to understand what was going on there and apply the same principles to my case, but I can't get my code to work. How do I do this correctly?
const int M = 3;
const int N = 2;
std::vector<double> f = { 0, 1, 2, 3, 4, 5 };
double* f1d = f.data();
// compiles and doesn't crash
// but f2d seems to be unitialized (at least f2d[1][1] is garbage)
// also has the wrong type (double[][] rather than double**)
double (&f2d)[N][M] = reinterpret_cast<double (&)[N][M]>(f1d);
// segfaults when trying to access e.g. f2d[0][0]
double** f2d = reinterpret_cast<double**>(f1d);
Eventually, I need to pass my data into a method which takes a double**
parameter, so the following has to compile and run without problem:
#include <iostream>
#include <vector>
void test(int M, int N, double **arr) {
for (int i = 0; i < N; ++i) {
for (int j = 0; j < M; ++j) {
std::cout << arr[i][j] << " ";
}
std::cout << std::endl;
}
};
int main() {
const int M = 3;
const int N = 2;
std::vector<double> f = { 0, 1, 2, 3, 4, 5 };
// whatever I need to do here to get f2d
test(M, N, f2d);
return 0;
}
Expected output:
0 1 2
3 4 5
A double**
-based "array" is a completely different beast from an actually multidimensional array, i.e. double[N][M]
. It has a completely different layout and stores different information, so you cannot possibly do what you want without storing any additional information.
The way a double**
-based "array" works is with a two-level structure, where the first level is an array that contains pointers to various regular one-dimensional arrays. A contiguous multidimensional array cannot function directly as a double**
-based "array" because that first level structure with the pointers is nowhere to be seen: the various subarrays in a double[N][M]
are implicit from the base address and the sizes.
double[3][2]
+----+----+----+----+----+----+
| 00 | 01 | 02 | 10 | 11 | 12 |
+----+----+----+----+----+----+
double**
+------------+------------+
| 0xDEADD00D | 0xDEADBABE |
+------------+------------+
| |
+-----+ |
| |
v |
+----+----+----+ |
| 00 | 01 | 02 | |
+----+----+----+ |
v
+----+----+----+
| 10 | 11 | 12 |
+----+----+----+
So now, that that is out of the way and we understand how a double**
-based "array" works, we can start trying to solve your problem.
What you need to do to get a double**
is to provide that first level structure yourself by filling a separate array of pointers with pointers to various addresses within your single contiguous array.
std::vector<double*> index;
index.reserve(M);
for(int i = 0; i < M; ++i) {
index.push_back(&f[i*N]);
}
double** f2d = index.data();
This gives you minimal hassle, and minimal space overhead. It also performs no copies of the data whatsoever, since we're only collecting a bunch of pointers to the already existing storage.
You end up with a layout that looks like this:
index
+------------+------------+
| 0xDEADD00D | 0xDEADBABE |
+------------+------------+
| |
+-----+ +---+
| |
v v
+----+----+----+----+----+----+
f | 00 | 01 | 02 | 10 | 11 | 12 |
+----+----+----+----+----+----+
Obviously, you couldn't get a valid double**
out of the array, since there is no array of double*
to point to. You'll need to reinterpret the flat array as a two-dimensional array, not an array of pointers.
The first attempt is almost correct; but it reinterprets a reference to the pointer f1d
, rather than the array it points to, as a 2-dimensional array. Dereferencing the pointer, and reinterpreting the resulting reference to the array, should work:
double (&f2d)[N][M] = reinterpret_cast<double (&)[N][M]>(*f1d);
^
Alternatively, you could reinterpret a reference to f[0]
without needing an intermediate pointer.
As you can see, using reinterpret_cast
is very error prone. You might consider instead wrapping the vector in a class, with accessor functions to perform the necessary arithmetic for two-dimensional indexing.
UPDATE: If for some reason you really want a double**
rather than a two-dimensional array, you'll need to build an array of pointers yourself, along the lines of
std::vector<double*> pointers;
for (i = 0; i < M; ++i) {
pointers.push_back(&f[i*N]);
}
double** f2d = pointers.data();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With