C_FUNLOC的结果是标量还是数组?

时间:2019-05-20 16:24:51

标签: fortran gfortran

我试图将某些Fortran子例程表示为c_funptrvoid *),以便通过漂亮的fdict库创建字典。在GCC文档here之后,我尝试致电c_funloc。但是,gfortran似乎返回c_funptr数组而不是标量值。

这是编译器中的错误还是缺少重要的东西?

gfortran -v的输出:

COLLECT_GCC=gfortran
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-pc-linux-gnu/8.3.0/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: /build/gcc/src/gcc/configure --prefix=/usr --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://bugs.archlinux.org/ --enable-languages=c,c++,ada,fortran,go,lto,objc,obj-c++ --enable-shared --enable-threads=posix --enable-libmpx --with-system-zlib --with-isl --enable-__cxa_atexit --disable-libunwind-exceptions --enable-clocale=gnu --disable-libstdcxx-pch --disable-libssp --enable-gnu-unique-object --enable-linker-build-id --enable-lto --enable-plugin --enable-install-libiberty --with-linker-hash-style=gnu --enable-gnu-indirect-function --enable-multilib --disable-werror --enable-checking=release --enable-default-pie --enable-default-ssp --enable-cet=auto
Thread model: posix
gcc version 8.3.0 (GCC)

我还尝试了使用ifort(19.0.2.187版),它提供了所需的行为(请参见下文)。

MWE:

! = minimum.f90 =
module test
    use iso_c_binding
    implicit none

    interface test_funptr
        module procedure test_funptr0
        module procedure test_funptr1
    end interface test_funptr
contains
    subroutine test_funptr0(fp)
        type(c_funptr) :: fp
        write(*,*) "fp0!"
    end subroutine test_funptr0
    subroutine test_funptr1(fp)
        type(c_funptr), dimension(:) :: fp
        write(*,*) "fp1!", shape(fp)
    end subroutine test_funptr1

    function bar(x) result(y) bind(c)
        real(c_double) :: x
        real(c_double) :: y
        y = -x**2 + x + 1
    end function bar
end module test
program main
    use iso_c_binding
    use test
    implicit none

    call test_funptr(c_funloc(bar))
end program main

gfortran minimum.f90 -o min

编译

任何地方的预期输出:

fp0

实际行为:fp1的gfortran形状为零,fp0的Intel编译器。

也许我只是缺少gfortran的正确选择?

2 个答案:

答案 0 :(得分:0)

内部模块c_funloc的函数iso_c_binding由Fortran标准指定,其功能结果为类型c_funptr的标量(Fortran 2018,18.2.3.5,与Fortran类似) 2003和Fortran 2008)。

具有函数结果的数组在程序本身符合标准时就违反了Fortran标准。

但是,在这种情况下,gfortran不提供数组函数结果:您可以使用类似以下的简单方法进行测试

print*, SHAPE(C_FUNLOC(bar))

相反,gfortran无法将通用过程test_funptr正确解析为特定的test_funptr0

也考虑这种情况

  use, intrinsic :: iso_c_binding, only : c_funptr, c_null_funptr
  use test, only : test_funptr, test_funptr1
  type(c_funptr) :: ptr = C_NULL_FUNPTR

  call test_funptr(ptr)
  call test_funptr1(ptr)
end

gfortran 8错误地解析了泛型,并允许直接调用test_funptr1

答案 1 :(得分:0)

我将尝试在某种程度上总结讨论并解决最初的问题-使用功能指针从fdict填充字典。该词典支持多种值类型,包括c_funptr

通过type(c_funptr)将Fortran过程转换为с_funloc并附加到字典似乎是明智的。 但是,由于gfortran中的问题,调用了错误的子例程,并且函数指针存储为type(c_funptr), dimension(:)。尝试将它们检索为标量会导致分段错误。为了克服它,您可以创建一个仅包含单个元素的函数指针数组。 像这样:

type(dictionary_t) :: fdict

fdict = ('bar' .kv. (/c_funloc(bar)/) ) //&
      & ('baz' .kv. (/c_funloc(baz)/) )

要取回函数,可以对一个指针数组进行下标并将其转换为fortran过程指针,如以下代码片段

type(c_funptr) :: cfp(1)
procedure(bar), pointer :: ffp => null()

call assign(cfp, dict, key) # generic from fdict
call c_f_procpointer(cfp(1), ffp)

带有用法示例的完整源代码可以在here中找到。

此解决方案也适用于ifort。两种变体产生的输出与上述示例相似:

baz [fp1] (...) 
bar [fp1] (...)
  -1.000   1.000  -1.000
  -1.000  -2.000 -10.000