Ошибка Fortran DLL при использовании функции с несколькими переменными

В настоящее время я разрабатываю fortran DLL, и у меня есть проблема с функциями с несколькими переменными. Моя конечная цель состоит в том, чтобы

  • вызывать функции DLL из VBA
  • отлаживать DLL, используя код на фортране, вызывающий функции DLL

Вот мой упрощенный случай:

<сильный>1. DLL-код Фортрана

module mod_thermo

    implicit none

contains

    function y1(x1) result(y) bind(c, name = "Y1")
        use iso_c_binding, only : c_double
        !GCC$ attributes dllexport, stdcall :: y1
        real(c_double) :: x1
        real(c_double) :: y

        y = 2.d0 * x1

    end function

    function y2(x1, x2) result(y) bind(c, name = "Y2")
        use iso_c_binding, only : c_double
        !GCC$ attributes dllexport, stdcall :: y2
        real(c_double) :: x1
        real(c_double) :: x2
        real(c_double) :: y

        y = 2.d0 * x1 * x2

    end function

end module

<сильный>2. Параметры компиляции Fortran DLL с помощью GCC

Компилятор - GCC. Варианты компиляций:

  • -static (чтобы избежать зависимостей от других dll)
  • -Wl,--kill-at (относится к VBA)
  • -fno-подчеркивание (связано с VBA)

Выходные файлы находятся в папке проекта кода фортрана для будущей отладки:

  • dll_thermo.dll
  • libdll_thermo.a
  • libdll_thermo.def

<сильный>3. Код Fortran для тестирования DLL (интерфейсы + программа)

Библиотека подключается к коду путем добавления библиотеки libdll_thermo.a.

module mod_thermo

    implicit none

    interface

        function y1(x1) result(y) bind(c,name="Y1")
            use iso_c_binding, only : c_double
            real(c_double) :: x1
            real(c_double) :: y
        end function

        function y2(x1, x2) result(y) bind(c,name="Y2")
            use iso_c_binding, only : c_double
            real(c_double) :: x1
            real(c_double) :: x2
            real(c_double) :: y
        end function
    end interface

end module

program main

    use mod_thermo
    implicit none

    write(*,*)"y1 calls:"
    write(*,*)y1(1.d0) ! output ok
    write(*,*)y1(2.d0) ! output ok
    write(*,*)y1(3.d0) ! output ok

    write(*,*)"y2 calls:"
    write(*,*)y2(1.d0, 1.d0) ! output ok
    write(*,*)y2(2.d0, 2.d0) ! output fails
    write(*,*)y2(3.d0, 2.d0)

end program

<сильный>4. Вывод и заключение

Сбой при выполнении второго вызова Y2

Мой вывод состоит в том, что я неправильно выполняю многовариантный вызов функции DLL y2. Каким бы был ваш способ выполнения таких звонков?


person yolegu    schedule 15.12.2019    source источник
comment
Я думаю, вы хотите real(c_double), value :: x1 и для остальных аргументов то же самое. В противном случае они передаются как указатели, а вам нужны значения. Это повреждает стек, и второй вызов функции завершается ошибкой.   -  person John Alexiou    schedule 15.12.2019
comment
@ ja72 Я добавил value для x1 и x2 как в код DLL, так и в код отладки fortran, и теперь у меня возникает ошибка при первом вызове y2.   -  person yolegu    schedule 15.12.2019
comment
@ ja72 Я не понимаю, почему вы считаете, что требуется ценность. Код просто вызывает обычные функции Fortran в модуле, Bind(C) не означает, что функции находятся в C. Или атрибуты !GCC$ dllexport, stdcall фактически означают, что это больше не Fortran, поскольку обычные методы передачи аргументов были выброшенный?   -  person Ian Bush    schedule 15.12.2019
comment
Попробуйте изменить порядок вызова функций. Посмотрите, возникает ли ошибка в y2(x1,x2) или во втором вызове функции y1(x1). Это намекает на повреждение стека из-за несоответствия соглашения о вызовах.   -  person John Alexiou    schedule 15.12.2019
comment
@ ja72 Как вы и предложили, я поменял местами вызовы y1 и y2. Теперь я выполняю три вызова y2, за которыми следуют три вызова y1. Я не добавлял value в объявление переменной. В результате третий вызов y2 приводит к ошибке сегментации. По сравнению с исходным сообщением об ошибке #3 0x75cda88f заменено на #3 0x75b1a88f.   -  person yolegu    schedule 15.12.2019
comment
Хорошо, теперь попробуйте удалить stdcall из описания экспорта. Я думаю, что Fortran ожидает stdref для соглашения о вызовах.   -  person John Alexiou    schedule 15.12.2019
comment
Основываясь на этом ссылке, я считаю, что stdcall - это правильный способ сделать вызов. Более того, использование stdcall не вызывает никаких проблем в VBA. Единственная проблема - связь Fortran-Fortran.   -  person yolegu    schedule 15.12.2019
comment
этот пост поддерживает ваше утверждение, хотя они включите также use ISO_FORTRAN_ENV. Но в опубликованной вами ссылке на TCL отсутствует ключевое слово stdcall.   -  person John Alexiou    schedule 15.12.2019
comment
Мне очень не нравится, что module mod_thermo объявляется дважды. Модули не похожи на пространства имен, где код может охватывать несколько файлов. Весь блок interface в программе лишний, так как функции все равно объявляются внутри модуля. Я имею в виду, что вам не нужны интерфейсы при вызове других функций, объявленных в модулях и импортированных с использованием предложения use, например use iso_fortran_env.   -  person John Alexiou    schedule 15.12.2019
comment
Я изменил код тестирования DLL (см. редактирование вопроса), и теперь компилятор жалуется, что не может найти файл «mod_thermo.mod». Я не понимаю, зачем нужен этот файл, так как я ожидал, что dll встроит все необходимые зависимости. Я предполагаю, что ссылка на DLL не в порядке. Я добавил 'libdll_thermo.a' в библиотеку ссылок проекта, но этого недостаточно.   -  person yolegu    schedule 16.12.2019


Ответы (1)


Проблема возникла из-за ссылки на "libdll_thermo.a". Следуя руководству из здесь, я обнаружил, что должен был связать "libdll_thermo.dll". Теперь это работает.

person yolegu    schedule 26.12.2019