【问题标题】:Fortran DLL in DelphiDelphi 中的 Fortran DLL
【发布时间】:2020-12-20 14:29:25
【问题描述】:

我正在尝试在 DLL 中编译一些非常古老的 Fortran 程序,以便能够在 Delphi 中使用它们。虽然 Fortran 代码不是很大(750-800 行),但它的结构非常复杂,有几十个 GOTO 命令,翻译也不是很容易(我尝试了一些有用的代码,但我失败了)。 虽然我是 Fortran 新手,调用 DLL 的经验也不是很丰富,但我逐渐克服了所有困难,但其中之一就是能够调用具有多个动态数组的 Fortran 子程序。这是我创建的一个简单示例:

      SUBROUTINE  MYSUB1( NoEquations, INTARR1 ) 
!DEC$ ATTRIBUTES DLLEXPORT::MYSUB1
!DEC$ ATTRIBUTES C, REFERENCE, ALIAS:'MYSUB1' :: MYSUB1
C
C***************************************************************
C
         INTEGER    NoEquations, I
         INTEGER    INTARR1(*)
C
C***************************************************************
C
                 DO 100, I=1,NoEquations
                 INTARR1(I) = I 
  100    CONTINUE
      RETURN
C
      END

      SUBROUTINE  MYSUB2( NoEquations, INTARR1, INTARR2 ) 
!DEC$ ATTRIBUTES DLLEXPORT::MYSUB2
!DEC$ ATTRIBUTES C, REFERENCE, ALIAS:'MYSUB2' :: MYSUB2
C
C***************************************************************
C
         INTEGER    NoEquations, I
         INTEGER    INTARR1(*)
         INTEGER    INTARR2(*)
C
C***************************************************************
C
                 DO 100, I=1,NoEquations
                 INTARR2(I) = INTARR1(I) 
  100    CONTINUE
      RETURN
C
      END

我用 mingw-w64 用以下命令编译 Fortran 代码:

gfortran -shared -mrtd -fno-underscoring -o simple.dll simple.f

我在 Delphi 中声明该过程:

  procedure mysub1(var NoEquations: integer; var INTARR1        : array of integer); stdcall; external 'simple.dll';
  procedure mysub2(var NoEquations: integer; var INTARR1,INTARR2: array of integer); stdcall; external 'simple.dll';

Delphi proram 可以正确编译,但是当我运行它时,mysub1 可以正常工作并更新 INTARR1,但是 mysub2 给了我访问冲突。显然,第二个动态数组使编译器感到困惑,但我不知道如何使其理解。 提前致谢

【问题讨论】:

  • 不要使用开放数组参数。使用指向数组元素类型的指针。开放数组参数有额外的隐含参数来指定长度。
  • 谢谢!你的意思是一个大的整数数组和指向这个数组的指针?
  • @Stelios Antoniou 问题不在两个数组参数中,所有声明都是错误的,第一个过程意外地正常工作。 INTARR1,INTARR2: PInteger 可能会有所帮助。还有内存分配呢?
  • @David Heffernan。有没有关于如何拨打电话的例子?我需要同时更改 Delphi 和 Fortran 代码吗?
  • 这两种方式的工作似乎一样多。 1000 行的任何内容并不难翻译,而且像 fortran 这样冗长的东西更是如此。我在这里压倒性的偏好是重写。 “这太难了”似乎是坚持传统复杂性海锚的微不足道的理由。我怀疑这 1000 行可能会在 Delphi 中被重写为一半或更小的大小,同时也变得更加清晰和易于阅读。您最终可能还会了解代码在其末尾的作用——我们可以推断您目前不了解。

标签: delphi dll fortran


【解决方案1】:

我不了解 Delphi,但您可以通过以下方式创建可从 C 语言访问的 DLL。我希望你觉得这对你有帮助。这是您修改的 F77 代码,使其可通过 Fortran 的 iso_c_binding 模块功能进行互操作:

      SUBROUTINE MYSUB1(NoEquations,INTARR1) bind(C,name="MYSUB1")
         !DEC$ ATTRIBUTES DLLEXPORT :: MYSUB1
         use, intrinsic :: iso_c_binding, only: IK => c_int32_t
         integer(IK), intent(in), value :: NoEquations
         integer(IK), intent(out)       :: INTARR1(NoEquations)
         integer                        :: I
         DO 100, I=1,NoEquations
             INTARR1(I) = I
  100    CONTINUE
         RETURN
      END SUBROUTINE MYSUB1

C***************************************************************
C***************************************************************

      SUBROUTINE MYSUB2(NoEquations,INTARR1,INTARR2)
     +bind(C,name="MYSUB2")
         !DEC$ ATTRIBUTES DLLEXPORT :: MYSUB2
         use, intrinsic :: iso_c_binding, only: IK => c_int32_t
         integer(IK), intent(in), value :: NoEquations
         integer(IK), intent(in)        :: INTARR1(NoEquations)
         integer(IK), intent(out)       :: INTARR2(NoEquations)
         integer                        :: I
         DO 100, I=1,NoEquations
             INTARR2(I) = INTARR1(I)
  100    CONTINUE
         RETURN
      END SUBROUTINE MYSUB2

请注意我对您的代码所做的许多细微但重要的更改,以使其与 C 互操作:

  1. bind(C,name="MYSUB1") 修正了子例程的名称,因此您不需要额外的第二个编译器指令(我现在已从您的代码中删除)。
  2. 另外,请注意 value 属性,它告诉编译器按值传递,就像在 C 中所做的那样。
  3. 另外,请注意,我在子例程的接口中将整数种类定义为c_int32_t,以与 C 处理器的整数种类兼容。
  4. 另外,请注意,我已将您的 assumed-size INTARR1 和 INTARR2 数组转换为显式形状数组 INTARR1(NoEquations)、INTARR1(NoEquations)。

由于您的代码中有 Intel !DEC$ 编译器指令,我假设您在 Windows 上使用 Intel Fortran 编译器。请注意,我在两个 Fortran 子例程中删除了您的第二行指令。

现在,假设您将上述代码存储在名为 mysubs.F 的文件中,然后在 Intel-Windows 命令提示符下通过 Intel Fortran 编译器 ifort 编译此文件,如下所示,

ifort mysubs.F /dll /out:libsubs

将在当前文件夹中生成一个名为mysubs.dll的DLL,并在屏幕上打印以下信息,

Intel(R) Visual Fortran Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 19.1.1.216 Build 20200306
Copyright (C) 1985-2020 Intel Corporation.  All rights reserved.

Microsoft (R) Incremental Linker Version 14.16.27027.1
Copyright (C) Microsoft Corporation.  All rights reserved.

-out:mysubs.dll
-dll
-implib:mysubs.lib
mysubs.obj
   Creating library mysubs.lib and object mysubs.exp

要测试这个DLL,你可以试试下面存储在main.c中的C代码,

#include <stdio.h>
#include <stdint.h>
#include <string.h>

void MYSUB1(int32_t, int32_t []);
void MYSUB2(int32_t, int32_t [], int32_t []);

int main(int argc, char *argv[])
{
    const int32_t NoEquations = 5;
    int32_t INTARR1[NoEquations];
    int32_t INTARR2[NoEquations];
    int loop;

    // C rules for argument passing apply here

    MYSUB1(NoEquations,INTARR1);
    printf("\nINTARR1:\n"); for(loop = 0; loop < NoEquations; loop++) printf("%d ", INTARR1[loop]);

    MYSUB2(NoEquations,INTARR1,INTARR2);
    printf("\nINTARR2:\n"); for(loop = 0; loop < NoEquations; loop++) printf("%d ", INTARR2[loop]);

    return 0;
}

使用英特尔 C 编译器编译此 C 代码,

icl main.c -c

在屏幕上打印以下内容,

Intel(R) C++ Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 19.1.1.216 Build 20200306
Copyright (C) 1985-2020 Intel Corporation.  All rights reserved.

main.c

并生成 C 主目标文件。最后,将 C 目标文件与 Fortran DLL 库链接,通过以下命令生成可执行文件,

icl main.obj mysubs.lib -o main.exe

在屏幕上打印以下内容,

Intel(R) C++ Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 19.1.1.216 Build 20200306
Copyright (C) 1985-2020 Intel Corporation.  All rights reserved.

Microsoft (R) Incremental Linker Version 14.16.27027.1
Copyright (C) Microsoft Corporation.  All rights reserved.

-out:main.exe
main.obj
mysubs.lib

测试 DLL 调用。只需调用生成的可执行文件main.exe,

main.exe

在屏幕上打印,

INTARR1:
1 2 3 4 5
INTARR2:
1 2 3 4 5

现在,要从 Delphi 调用此 DLL,只需假设您使用 C 主代码中指定的原型调用 C 函数。就这样。无需在 Delphi 内部进一步处理 Fortran。

最后的建议:

  1. Fortran 具有强大的标准互操作性功能,例如我添加到您的 F77 代码中的功能,可以轻松地将几乎任何 Fortran 代码连接到任何语言(通过 C)。

  2. 远离已经快半个世纪的 FORTRAN77,即使 Fortran 90 已经有 3 多年的历史了。最新的 Fortran 标准于 2018 年发布,与 Fortran 2008 一起,使 Fortran 成为用于数值计算的极其强大、高级、快速、本机矢量化、并发、共享和分布式并行编程语言。

【讨论】:

  • 其实问题出在Delphi代码中。
  • @DavidHeffernan 很公平。但我没有得到你的反对意见。该解决方案通过将“适当的标准方法”引入 Fortran 互操作,解决了在原始代码和编译过程中完成的许多临时编译器补丁,尽管它没有解决将其连接到 Delphi 的问题。但我也在我的回答中明确说明了这一点。
  • 问题是,Delphi代码中使用Delphi开放数组参数的错误是症结所在。一个完整的答案必须解决这个问题。
  • @King。感谢您的详细回复。 Delphi 完全可以接受与 C 兼容的 DLL。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多