PDA

View Full Version : How to get a pointer for 2D array of QVector?



tfmp
5th September 2012, 20:10
Hello All,

1D and 2D arrays with QVector can be declared as follows:

QVector< int > myA1D;
QVector<QVector<int> > myA2D;

and I can use them: myA1D[i] and myA2D[i][j].

I need to get pointers of them because the pointers should be given to Fortran codes.

For 1D, we can do....

int *pA1D = myA1D.data();

For 2D, How to get the pointer?

int **pA2D = ????

Could someone help me? Please!



I could do it by using additional memory spaces (Not verified yet);

QVector<QVector<int> > myA2D;
QVector< int* > tempPtA2D;
int **pA2D;

- tempPt2D[0] = myA2D[0].data() ....;
- pA2D = tempPt2D.data();
- int aaa = pA2D[][];

But it uses additional memory spaces, which I don't like.

Please let me know good techniques or other libraries for me.
- I would like to avoid "using new and delete to generate 2D array." (That's why I want to use QVector.)

Thank you.

amleto
5th September 2012, 20:37
simply, you cannot. The data of QVector<QVector<int> > is not in a contiguous block.

Your code is wrong as well.

tfmp
5th September 2012, 20:56
Can you tell me which part is wrong?

The following codes work:

QVector<QVector<int> > test2D;

test2D.resize(10);
for(int i=0; i<10; ++i)
{
(test2D[i]).resize(5);
}

for(int i=0; i<10; ++i)
for(int j=0; j<5; ++j) test2D[i][j] = 3*i+j;

amleto
5th September 2012, 22:59
This just doesn't make sense:


QVector<QVector<int> > myA2D;
QVector< int* > tempPtA2D;
int **pA2D;

- tempPt2D[0] = myA2D[0].data() ....;
- pA2D = tempPt2D.data();
- int aaa = pA2D[][];




Can you tell me which part is wrong?

The following codes work:

QVector<QVector<int> > test2D;

test2D.resize(10);
for(int i=0; i<10; ++i)
{
(test2D[i]).resize(5);
}

for(int i=0; i<10; ++i)
for(int j=0; j<5; ++j) test2D[i][j] = 3*i+j;

yes, that 'works', but you cannot pass anything there to a fortran routine that expects a 2d array (int[][] for example).

Also, please use code tags in future.

tfmp
6th September 2012, 00:20
Back to my basic problem, I need to create 2D and 3D array, and need their pointers, which will be passed to fortran codes.
I am looking for an easy way to create and clear memory spaces, not using new[] or delete[] by myself. (There are so many arrays in codes which I'm working on).

Do you have any suggestion what kind of approaches or methods or libraries I could use?

Thank you for your reply and comments.

ChrisW67
6th September 2012, 08:25
I suggest you read this StackOverflow question (http://stackoverflow.com/questions/4083490/how-do-i-pass-a-2d-array-in-c-to-a-fortran-subroutine) which addresses the problem.

Ultimately there is a single memory allocation per array regardless of dimension count (it is not a an array of pointers to other blocks). You have to deal with column-major vs row-major differences and base index differences. Should be fairly easy to wrap into a basic class that automates the allocation/deallocation and index juggling but I'm sure that wheel has been invented before. What FORTRAN library are you interfacing with? Does it not have utility functions or guidance to help with this stuff?

See also:
http://arnholm.org/software/cppf77/cppf77.htm#Section3.5.3
http://arnholm.org/software/cppf77/cppf77.htm#Section6.3

tfmp
7th September 2012, 23:05
I have long fortran source codes, whose subroutines will be called by C++ functions. The issues regarding "column-major vs row-major" might cause big headaches.

Thank you for your info. and comments.

amleto
8th September 2012, 01:48
I'd be interested in how well this works:


template<class T, unsigned int M, unsigned int N>
class FortranMatrix
{
public:
FortranMatrix()
:array_(new T[M][N])
{}

// cpp interface
T& at(unsigned int x, unsigned int y)
{
return (array_)[x][y];
}

// fortran interface - transpose it
T& operator()(unsigned int x, unsigned int y)
{
return (array_)[y][x];
}

//pass the matrix to a fortran method accepting a T*
operator T*()
{
return &(*array_)[0];
}

private:
T (*array_)[N];
};


I cant be bothered to get fortran, but doesn't this avoid all the copying that FMATRIX does in those links?

tfmp
11th September 2012, 20:58
amleto, your idea is great. In C++, there is no problem, but I have no idea yet how to pass or apply your fortran interface (below)to Fortran:



// fortran interface - transpose it
T& operator()(unsigned int x, unsigned int y)
{
return (array_)[y][x];
}


Added after 15 minutes:

For 2D array, I want to use Boost library, which provides contiguous blocks for 2D and 3D arrays.

For the array transfer, I tried the following way:
- Generate "Column-major" memory space in C++, to facilitate the access from Fortran codes.
- In C++, use an operator () to access the memory in [j][i] pattern.
(My situation is: I wrote C++ codes, and have already-written Fortran codes using multi-dimension arrays.)
- In C++, (i,j) should be used for [i][j], not directly using [i][j]. (It is easy for me now).
- In Fortran, (i,j) is used (without any code modification).

Here is the codes: ( I finally found "Code" tag. :))

// File: *.h


/* Boost library. */
#ifndef Q_MOC_RUN // To avoid an error regarding "Boost_JOIN "
#include <boost/multi_array.hpp>
#endif

typedef boost::multi_array<double, 2> btArrayDbl2D_type;

/*-----*
Double-type 2D array.
- In C/C++, use MATDBL2D(i=0..., j=0...), not A, not A[i][j].
- In Fortran, use A(i=1..., j=1...).
*-----*/
struct MATDBL2D
{
public:
MATDBL2D() : szI_(0), szJ_(0) {}
btArrayDbl2D_type A;

private:
uint szI_, szJ_;

public:
// "Column-major" memory storage is used herein to transfer data to Fortran codes: A[1][1],A[2][1],A[3][1],A[1][2]...
// [Note] C++ uses a "Row-major" memory storage system: A[1][1],A[1][2],A[1][3],A[2][1]...
void makeMem(uint szI, uint szJ)
{
szI_ = szI;
szJ_ = szJ;
A.resize(boost::extents[szJ_][szI_]); // "Column-major" memory storage.
}

// For C++ interface.
// - NEVER access directly to A[][], which contains data as "Column-major" memory storage.
double & operator()(uint i, uint j) { return A[j][i]; }
const double & operator()(uint i, uint j) const { return A[j][i]; }

uint szI() { return szI_;} // For C/C++ & // To pass its size to Fortran subroutines.
uint szJ() { return szJ_;} // For C/C++ & // To pass its size to Fortran subroutines.

// For Fortran.
// "A" will be directly accessed: ex) A(i,j): i,j=1-based number.
// So "A" is set as a public variable.
};


File: *.cpp


int testFN()
{
int szI = 3;
int szJ = 4;
double b, X[3][4];

MATDBL2D A2D;
A2D.makeMem(szI, szJ);

for(int i=0; i<szI; ++i){
for(int j=0; j<szJ; ++j){
X[i][j] = i*10. + j;
A2D(i,j) = X[i][j];
}
}

// Testing......
// 2D Sum.
b=0.;
b=A2D(0,0)+A2D(1,2)+A2D(2,0); // C++

b = 0.;
int szFI = A2D.szI();
int szFJ = A2D.szJ();
SUM2D(A2D.A.data(), &b, &szFI, &szFJ); // Fortran subroutine.
}


Fortran subroutine.


subroutine SUM2D(a,b,I,J)
real*8 a(I,J),b
real*8 x(I,J)
integer I,J
do L=1,I
do M=1,J
x(L,M)=a(L,M)
enddo
enddo
b=a(1,1)+a(2,3)+a(3,1)
end subroutine


If you or someone have any better or easy methods or comments, please let me know.
Thanks.

amleto
11th September 2012, 22:06
amleto, your idea is great. In C++, there is no problem, but I have no idea yet how to pass or apply your fortran interface (below)to Fortran:



// fortran interface - transpose it
T& operator()(unsigned int x, unsigned int y)
{
return (array_)[y][x];
}



same way as FMATRIX - examples in links above.

tfmp
12th September 2012, 22:45
I was a little confused, and now understand what your codes mean.

A basic question: If an array is created with FortranMatrix as follows


void cppFN()
{
FortranMatrix < int, 3, 5 > intArray;

intArray(1,2) =1;
...
}

a user does not need to delete the intArray. It will be cleared by OS when leaving the cppFN(). Is my understanding correct?

d_stranz
12th September 2012, 23:43
a user does not need to delete the intArray. It will be cleared by OS when leaving the cppFN(). Is my understanding correct?

Yes, correct.

amleto
13th September 2012, 14:15
I was a little confused, and now understand what your codes mean.

A basic question: If an array is created with FortranMatrix as follows


void cppFN()
{
FortranMatrix < int, 3, 5 > intArray;

intArray(1,2) =1;
...
}

a user does not need to delete the intArray. It will be cleared by OS when leaving the cppFN(). Is my understanding correct?


Yes, correct.

but remember that in cpp code, you should be using 'at' to access the array elements, e.g


void cppFN()
{
FortranMatrix < int, 3, 5 > intArray;

intArray.at(1,2) = 1; // not 'intArray(1,2)' <-- fortran-only syntax
...
}

d_stranz
13th September 2012, 17:00
but remember that in cpp code, you should be using 'at' to access the array elements, e.g

No, I think you woke up too early (or maybe are staying awake much too late).

The code refers to your FortranMatrix template class, which has template methods for operator()( int, int ). His code is exactly correct for that class - the internal storage for that class is a C++ array allocated by operator new(), and the template class doe not define an "at()" method.

amleto
13th September 2012, 18:24
No, I think you woke up too early (or maybe are staying awake much too late).

The code refers to your FortranMatrix template class, which has template methods for operator()( int, int ). His code is exactly correct for that class - the internal storage for that class is a C++ array allocated by operator new(), and the template class doe not define an "at()" method.

I think you are confused.

I know it refers to my FortranMatrix, which has operator()(...) ONLY for fortran - it is commented as such. Lets remind ourselves:

I'd be interested in how well this works:


template<class T, unsigned int M, unsigned int N>
class FortranMatrix
{
public:
FortranMatrix()
:array_(new T[M][N])
{}

// cpp interface
T& at(unsigned int x, unsigned int y)
{
return (array_)[x][y];
}

// fortran interface - transpose it
T& operator()(unsigned int x, unsigned int y)
{
return (array_)[y][x];
}

//pass the matrix to a fortran method accepting a T*
operator T*()
{
return &(*array_)[0];
}

private:
T (*array_)[N];
};



Quite clearly there IS a method 'at'. Funnily enough, I commented that this method to be used in cpp code, which is why I remarked that this method, and not operator()(...), should be used.

Regards

d_stranz
13th September 2012, 21:47
Quite clearly there IS a method 'at'.

:o And equally clear is the fact that I was the one up too early to be typing comments into the forum...

tfmp
19th September 2012, 19:21
Thank you for all replies and comments, given above. The issue regrading data-sharing between Fortran and C++ is resolved.