Fortran中设置OpenBLAS子程序指针兼容单双精度编译报错求助
解决Fortran中过程指针指向OpenBLAS子程序的显式接口错误
错误原因
编译器报错Error: Explicit interface required for ‘sgemv’ at (1): value argument,核心原因是:
- 仅通过
external声明sgemv/dgemv只能告诉编译器它们是外部子程序,但无法提供参数的类型、属性(比如value)等关键信息。 - Fortran标准规定:当子程序参数使用
value属性时,必须提供显式接口才能正确调用或赋值给过程指针,否则编译器无法验证参数匹配性。
解决方案
方案一:修改原有双模块结构,添加显式接口
在每个精度模块中,为对应的OpenBLAS子程序定义显式接口块,替代单纯的external声明:
单精度模块修改后代码
module SinglePrecision implicit none integer, parameter :: precision = selected_real_kind(6, 37) ! 为sgemv定义显式接口,匹配OpenBLAS单精度gemv原型 interface subroutine sgemv(trans, m, n, alpha, a, lda, x, incx, beta, y, incy) bind(C, name='sgemv') import :: precision character(len=1), intent(in) :: trans integer(4), intent(in), value :: m, n, lda, incx, incy real(precision), intent(in), value :: alpha, beta real(precision), dimension(lda, *), intent(in) :: a real(precision), dimension(*), intent(in) :: x real(precision), dimension(*), intent(inout):: y end subroutine sgemv end interface abstract interface subroutine gemv(trans, m, n, alpha, a, lda, x, incx, beta, y, incy) import :: precision character :: trans integer(4), value :: m, n, lda, incx, incy real(precision), value :: alpha, beta real(precision), dimension(lda, *), intent(in) :: a real(precision), dimension(*), intent(in) :: x real(precision), dimension(*), intent(inout) :: y end subroutine gemv end interface procedure(gemv), pointer :: MatrixVectorMultiplication => null() end module SinglePrecision
双精度模块修改后代码
module DoublePrecision implicit none integer, parameter :: precision = selected_real_kind(15, 307) ! 为dgemv定义显式接口,匹配OpenBLAS双精度gemv原型 interface subroutine dgemv(trans, m, n, alpha, a, lda, x, incx, beta, y, incy) bind(C, name='dgemv') import :: precision character(len=1), intent(in) :: trans integer(4), intent(in), value :: m, n, lda, incx, incy real(precision), intent(in), value :: alpha, beta real(precision), dimension(lda, *), intent(in) :: a real(precision), dimension(*), intent(in) :: x real(precision), dimension(*), intent(inout):: y end subroutine dgemv end interface abstract interface subroutine gemv(trans, m, n, alpha, a, lda, x, incx, beta, y, incy) import :: precision character :: trans integer(4), value :: m, n, lda, incx, incy real(precision), value :: alpha, beta real(precision), dimension(lda, *), intent(in) :: a real(precision), dimension(*), intent(in) :: x real(precision), dimension(*), intent(inout) :: y end subroutine gemv end interface procedure(gemv), pointer :: MatrixVectorMultiplication => null() end module DoublePrecision
主程序代码保持不变,编译命令也无需调整。
方案二:合并为单模块,用编译宏控制精度(更简洁)
将两个模块合并为一个,通过编译宏直接切换精度和对应的BLAS接口,减少冗余代码:
合并后的模块代码
module BLAS_GEMV implicit none ! 默认单精度,编译时-Ddouble切换为双精度 integer, parameter :: precision = selected_real_kind(6, 37) #ifdef double integer, parameter :: precision = selected_real_kind(15, 307) #endif ! 定义统一的抽象接口 abstract interface subroutine gemv_interface(trans, m, n, alpha, a, lda, x, incx, beta, y, incy) import :: precision character(len=1) :: trans integer(4), value :: m, n, lda, incx, incy real(precision), value :: alpha, beta real(precision), dimension(lda, *), intent(in) :: a real(precision), dimension(*), intent(in) :: x real(precision), dimension(*), intent(inout) :: y end subroutine gemv_interface end interface ! 根据编译宏绑定对应精度的OpenBLAS接口,并初始化过程指针 #ifdef double interface subroutine dgemv(trans, m, n, alpha, a, lda, x, incx, beta, y, incy) bind(C, name='dgemv') import :: precision character(len=1), intent(in) :: trans integer(4), intent(in), value :: m, n, lda, incx, incy real(precision), intent(in), value :: alpha, beta real(precision), dimension(lda, *), intent(in) :: a real(precision), dimension(*), intent(in) :: x real(precision), dimension(*), intent(inout):: y end subroutine dgemv end interface procedure(gemv_interface), pointer :: MatrixVectorMultiplication => dgemv #else interface subroutine sgemv(trans, m, n, alpha, a, lda, x, incx, beta, y, incy) bind(C, name='sgemv') import :: precision character(len=1), intent(in) :: trans integer(4), intent(in), value :: m, n, lda, incx, incy real(precision), intent(in), value :: alpha, beta real(precision), dimension(lda, *), intent(in) :: a real(precision), dimension(*), intent(in) :: x real(precision), dimension(*), intent(inout):: y end subroutine sgemv end interface procedure(gemv_interface), pointer :: MatrixVectorMultiplication => sgemv #endif end module BLAS_GEMV
简化后的主程序
program test_MatrixMultiplication use BLAS_GEMV ! 直接使用MatrixVectorMultiplication调用对应精度的gemv,无需手动赋值 end program test_MatrixMultiplication
编译命令和原命令一致:
! 单精度编译 gfortran -Dsingle -cpp -L/usr/local/opt/openblas/lib/ -I/usr/local/opt/openblas/include/ -lopenblas -ffree-line-length-1024 SingleDoublePrecisionMatrixMultiplication.f90 ! 双精度编译 gfortran -Ddouble -cpp -L/usr/local/opt/openblas/lib/ -I/usr/local/opt/openblas/include/ -lopenblas -ffree-line-length-1024 SingleDoublePrecisionMatrixMultiplication.f90
关键注意事项
- 添加
bind(C, name='sgemv')/bind(C, name='dgemv')是为了确保子程序名称与OpenBLAS的C接口严格匹配,避免编译器修改Fortran名称导致链接错误。 - 抽象接口的参数必须与具体BLAS接口的参数类型、属性(
value、intent)完全一致,否则过程指针赋值会失败。
内容的提问来源于stack exchange,提问作者Navid Khairdast
相关产品推荐
相关产品推荐

