Search code examples
c++fortrangfortran

How do you pass a 2d array to an external function when the precise shape is determined at runtime in C++?


I have a c++ program that looks as following:

using namespace std;
extern"C" {
void fortfunc_(int a[][4], int *size_x, int *size_y);
}
int main()
{
    // size_x and size_y are dynamically determined at runtime
    // setting them here directly for simplification
    int size_x = 5;
    int size_y = 4;

    //we need to use 4 instead of size_y to prevent a mismatch with the int a[][4] definition in fortfunc_
    int foo [size_x][4]; 
    
    for (int i=0 ; i<size_x ; i++ )
    {
        for (int y=0 ; y<size_y ; y++ )
        {
            foo[i][y] = i+y;
        }
    }
    fortfunc_(foo, &size_x, &size_y);
    return 0;
}

The respective fortran program only prints the array and looks like this:

subroutine fortfunc(foo, size_x, size_y)
    use :: ISO_C_BINDING
    integer :: size_x, size_y   
    integer(c_int), dimension(size_y,size_x), intent(in) :: foo
    integer :: i, y
    do y = 1, size_x ! We reverse the loop order because of the reverse array order in fortran
        do i = 1, size_y
            print*, "col ",i,y, foo(i,y)
        end do
        print*, "row"
    end do
    return
end

When compiled with gfortran -c testF.f90 && g++ -c testC.cpp && g++ -o test testF.o testC.o -lgfortran && ./test this works.

However, I would like to be able to dynamically determine the shape of the 2d array foo in my main function and call my external fortfunc_ function accordingly, instead of hardcoding int a[][4] there. How can I modify my program to accomplish that?


Solution

  • You typically allocate a 1d vector and treat it as a 2d array.

    Example:

    #include <vector>
    
    extern "C" {
    // note the changed signature with `int* a` instead:
    void fortfunc_(int* a, int *size_x, int *size_y);
    }
    
    int main() {
        int size_x = 5;
        int size_y = 4;
    
        // A 1d vector with size_y * size_x elements:
        std::vector<int> foo(size_y * size_x);
    
        for (int y = 0; y < size_y; y++) {
            for (int x = 0; x < size_x; x++) {
                // calculation for row-major order:
                foo[y * size_x + x] = y * size_x + x;
                // Note: Fortran uses column-major order so you may need to change
                // to `foo[x * size_y + y] = x * size_y + y;`
            }
        }
        fortfunc_(foo.data(), &size_x, &size_y);
        //            ^^^^^^
        // a pointer to the first `int` in the 1d vector
    }
    

    Output:

     col            1           1           0
     col            2           1           1
     col            3           1           2
     col            4           1           3
     row
     col            1           2           4
     col            2           2           5
     col            3           2           6
     col            4           2           7
     row
     col            1           3           8
     col            2           3           9
     col            3           3          10
     col            4           3          11
     row
     col            1           4          12
     col            2           4          13
     col            3           4          14
     col            4           4          15
     row
     col            1           5          16
     col            2           5          17
     col            3           5          18
     col            4           5          19
     row