C ++中的Fortran多维数组

J·布拉德·曼恩

我试图在C ++ Fortran互操作程序中将多维Fortran数组传递给C ++程序。我对如何将数组从Fortran传递到C ++有一个基本的想法;您将数组的位置从Fortran传递到C ++。然后,C ++将采用展平的数组,您必须进行一些代数计算才能找到给定多维数组中的元素。

我能够在标量数组上成功测试该想法。找出C ++中元素的索引并不难,因为它从Fortran索引线性映射到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 ++。我知道二维数组(例如A(2,2))将很容易弄清楚C ++中的扁平数组。但是我认为在C ++中为3或4维数组定位元素可能会更具挑战性。

什么是在C ++中构造多维数组的正确方法,以便我可以在Fortran中将数组A(k,j,i)中的元素称为C ++中的A [i] [j] [k]?

提前致谢!

罗格维布

尽管编写数组视图类应该更灵活,但是将标量指针(在下面的示例中为int *)转换为(N-1)-dim数组的指针可能会很有用。

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()例程。具体来说,我将上述a(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(),然后将获得的数据复制回实际参数。否则,如Fortran77一样,编译器会将实际数组的第一个元素的地址直接传递给cppfunc()。

编辑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
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章