Является ли результат C_FUNLOC скалярным или массивом? - PullRequest
1 голос
/ 20 мая 2019

Я пытаюсь представить некоторые подпрограммы на Фортране как c_funptr (void *), чтобы создать словарь с помощью библиотеки fdict . Следуя документам GCC здесь Я попытался позвонить 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 голосов
/ 21 мая 2019

Я постараюсь подвести итоги обсуждения (до некоторой степени) и решить исходную проблему - заполнить словарь из fdict указателями функций.Этот словарь поддерживает несколько типов значений, включая c_funptr.

Представляется целесообразным преобразовать процедуры Фортрана в type(c_funptr) через с_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)

Полный исходный кодкод с примером использования можно найти здесь .

Это решение работает и с ifort.Оба варианта выдают схожий результат с вышеупомянутым примером:

baz [fp1] (...) 
bar [fp1] (...)
  -1.000   1.000  -1.000
  -1.000  -2.000 -10.000
0 голосов
/ 20 мая 2019

Функция c_funloc встроенного модуля iso_c_binding определена стандартом Fortran, чтобы иметь в качестве результата функции скаляр типа c_funptr (Fortran 2018, 18.2.3.5, аналогичный в Fortran 2003 и Fortran 2008).

Наличие результата функции массив является нарушением стандарта Фортрана, когда программа сама соответствует.

Однако, в этом случае 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.

...