Многомерный массив Фортрана в переполнении стека

Я пытаюсь передать многомерный массив Фортрана в программу на С ++, во взаимодействующую программу на С ++ на Фортране. У меня есть базовое представление о том, как работает передача массивов из Fortran в C ++; Вы передаете расположение массива из Фортрана в C ++. Затем C ++ берет плоский массив, и вам нужно выполнить некоторые алгебраические вычисления, чтобы найти элемент в данном многомерном массиве.

Я смог успешно проверить эту идею на скалярном массиве. Не так сложно вычислить индекс элемента в C ++, потому что он линейно отображается из индекса Фортрана в C ++ со смещением -1. Примеры кодов для Fortran и C ++:

! Fortran main program
program fprogram

integer :: i
real*8 :: array(2)

array(1) = 1.0
array(2) = 2.0

! call cpp function
call cppfuncarray(array, 2)
write(*,*) array

end program
//cpp file
extern "C" {
void cppfuncarray_(double *array, int *imax);}

void cppfuncarray_(double *array, int *imax) {
int iMax = *imax;
for ( int i = 0; i < iMax; i++ ) {
array[i] = array + 1.0*i;
}
};

и выходные данные будут array (1) = 1 и array (2) = 3. Теперь я пытаюсь передать многомерные массивы, такие как A (2,2) или A (2,3,2) из ​​Fortran в C ++. Я знаю, что 2-мерный массив, такой как A (2,2), будет легко вычислить плоский массив в C ++. Но я считаю, что может быть немного сложнее найти элементы в C ++ для 3 или 4-мерных массивов.

Как правильно построить многомерный массив в C ++, чтобы я мог ссылаться на элементы в массиве A (k, j, i) в Fortran как A [i] [j] [k] в C ++?

Заранее спасибо!

3

Решение

Приведение указателя к скаляру (int * в примере ниже) к указателю на массив (N-1) -dim может быть полезным, хотя написание класса представления массива должно быть более гибким …

fortsub.f90:

subroutine fortsub()
implicit none
integer a(4,3,2)   !! this line will be changed in the EDIT below
integer ndims(3), i

ndims(:) = [ ( size( a, i ), i = 1, 3 ) ]
call cppfunc( a, ndims )

print *, "a(1,1,1) = ", a(1,1,1)   !! gives 10101
print *, "a(2,2,1) = ", a(2,2,1)   !! gives 20201
print *, "a(4,3,2) = ", a(4,3,2)   !! gives 40302
end subroutine

cppfunc.cpp:

extern "C" {
void fortsub_( void );

void cppfunc_( int *A, int *n )
{
typedef int (*A3d_t)[ n[1] ][ n[0] ];
A3d_t A3d = (A3d_t) A;  // get a pointer to 2-dim subarray

// 3-dim access
for( int k = 0; k < n[2]; k++ )
for( int j = 0; j < n[1]; j++ )
for( int i = 0; i < n[0]; i++ ) {
A3d[ k ][ j ][ i ] = (i+1)*10000 + (j+1)*100 + (k+1); // set test data
}
}
}; // extern "C"
int main()
{
fortsub_();
return 0;
}

Обобщение:

$ g++ fortsub.f90 cppfunc.cpp -lgfortran  # tested with gcc >=4.4.7

РЕДАКТИРОВАТЬ:
Хотя это может быть не по теме, я также попытался передать выделяемый массив или указатель массива на ту же самую процедуру cppfunc (). В частности, я изменил объявление (4,3,2) выше на одно из следующего:

!! case 1
integer, allocatable :: a(:,:,:)
allocate( a(4,3,2) )

!! case 2
integer, pointer :: a(:,:,:)
allocate( a(4,3,2) )

!! case 3 : an array view for contiguous memory
integer, target :: b(4,3,2,5)
integer, pointer :: a(:,:,:)
a => b( :, :, :, 5 )

!! case 4 : an array view for non-contiguous memory
integer, target :: c(5,4,3,2)
integer, pointer :: a(:,:,:)
a => c( 5, :, :, : )

При компиляции с

$ g++ fortsub.f90 cppfunc.cpp -lgfortran -fcheck-array-temporaries

все случаи дают правильный результат. Только в случае 4 компилятор создает временный массив, передает адрес его первого элемента в cppfunc () и копирует полученные данные обратно в фактический аргумент. В противном случае компилятор передает адрес первого элемента фактического массива непосредственно в cppfunc (), как в Fortran77.

РЕДАКТИРОВАНИЕ 2: Некоторые подпрограммы могут хотеть получить массив N-dim как массив указателей. В этом случае более традиционный подход будет примерно таким:

getptr3d.hpp:

template <typename T>
T*** get_ptr3d( T* A, int* n )
{
typedef T (*A3d_t)[ n[1] ][ n[0] ];
A3d_t A3d = (A3d_t) A;

T*** p = new T** [ n[2] ];

for( int k = 0; k < n[2]; k++ ) {
p[ k ] = new T* [ n[1] ];

for ( int j = 0; j < n[1]; j++ ) {
p[ k ][ j ] = (T*) &( A3d[ k ][ j ][ 0 ] );
}
}
return p;
}

template <typename T>
void free_ptr3d( T*** p, int*n )
{
for( int k = 0; k < n[2]; k++ ) { delete[] p[ k ]; }
delete[] p;
}

Модифицированный cppfunc.cpp:

#include "getptr3d.hpp"...
void cppfunc_( int* A, int* n )
{
int*** A3d = get_ptr3d( A, n );  // can also be used for double***
... // use A3d[ k ][ j ][ i ]
// or pass A3d to other functions like func( int*** B, ... )
free_ptr3d( A3d, n ); // when calculation finished
}
4

Другие решения


По вопросам рекламы [email protected]