В настоящее время я модернизирую некоторые программы на Фортране, и я хочу написать обертку вокруг подпрограммы старого стиля Фортран 77 с подписью
SUBROUTINE INITMATRIX( M , N , A , LDA)
INTEGER M, N, LDA
DOUBLE PRECISION A(LDA,*)
Для этой подпрограммы я написал обертку, принимающую матрицу стиля Фортран 90 какinput
SUBROUTINE INITMATRIX_F90( A )
DOUBLE PRECISION, INTENT(INOUT) :: A(:,:)
INTEGER :: M, N, LDA
M = SIZE(A,1)
N = SIZE(A,2)
LDA = SIZE(A,1)
CALL INITMATRIX(M, N, A, LDA)
END SUBROUTINE
Это работает хорошо, если я не передам часть в процедуру.Например, у меня есть матрица 20 x 20, и я хочу инициализировать только первые 10 строк.Тогда я бы назвал
DOUBLE PRECISION A(20,20)
CALL INITMATRIX_F90(A(1:10,1:20))
, что приведет к ошибке, так как моя оболочка получает неправильный начальный размер массива.В примере у меня есть LDA=10
вместо LDA = 20
.Есть ли способ получить доступ к шагу / расширению массива, чтобы восстановить ведущее измерение?Что касается заголовочного файла ISO_Fortran_binding.h для совместимости с C, информация хранится внутри дескриптора массива.
Чтобы визуализировать проблему, вот MWE, демонстрирующий проблему.
PROGRAM MAIN
INTERFACE
SUBROUTINE INITMATRIX_F90(A)
DOUBLE PRECISION, INTENT(INOUT) :: A(:,:)
END SUBROUTINE
END INTERFACE
DOUBLE PRECISION :: A(20,20)
A = 1.0D0
CALL INITMATRIX_F90(A(1:10,1:20))
END PROGRAM MAIN
SUBROUTINE INITMATRIX_F90( A )
USE ISO_C_BINDING
IMPLICIT NONE
DOUBLE PRECISION, INTENT(INOUT), POINTER :: A(:,:)
INTEGER :: M, N, LDA
TYPE(C_PTR) :: LOC1, LOC2
INTEGER*16 :: LOCX1, LOCX2
CHARACTER*32 :: TMP
M = SIZE(A,1)
N = SIZE(A,2)
WRITE(TMP, *) C_LOC(A(1,1))
READ(TMP, *) LOCX1
WRITE(TMP, *) C_LOC(A(1,2))
READ(TMP, *) LOCX2
LDA = (LOCX2-LOCX1) / C_SIZEOF(A(1,1))
WRITE(*,*) "M = ", M
WRITE(*,*) "N = ", N
WRITE(*,*) "LOC = ", LOCX1, LOCX2
WRITE(*,*) "LDA(COMPUTED) = ", LDA
END SUBROUTINE
(я знаю, что указатель отсутствует в интерфейсе, он только для того, чтобы заставить работать C_LOC.)
Выходные данные тогда
M = 10
N = 20
LOC = 140721770410864 140721770411024
LDA(COMPUTED) = 20
где очевидно, что ведущее измерение правильно вычисляется с помощью грязного хака.Внутренние структуры, используемые компилятором GNU Fortran или привязкой ISO C <-> Fortran (отличной от используемой в GNU), содержат информацию о том, как получить к ним доступ из Fortran без подвохов.
Другой MWE - это следующая обертка вокруг DLASET LAPACK:
PROGRAM MAIN
INTERFACE
SUBROUTINE INITMATRIX2_F90(A)
DOUBLE PRECISION, INTENT(INOUT) :: A(:,:)
END SUBROUTINE
END INTERFACE
DOUBLE PRECISION :: A(20,20)
A = 0.0D0
CALL INITMATRIX2_F90(A(1:10,1:20))
WRITE(*,*) A(1:20,1)
END PROGRAM MAIN
SUBROUTINE INITMATRIX2_F90( A )
USE ISO_C_BINDING
IMPLICIT NONE
DOUBLE PRECISION, INTENT(INOUT) :: A(:,:)
INTEGER :: M, N, LDA
EXTERNAL DLASET
M = SIZE(A,1)
N = SIZE(A,2)
CALL DLASET( "All", M, N, 1.0D0, 1.0D0, A(1,1) , M)
END SUBROUTINE
Это дает 20 единиц в первом столбце A вместо десяти единиц и десяти нулей.