通过DLL从Fortran调用Ada过程

通过DLL从Fortran调用Ada过程,dll,fortran,ada,Dll,Fortran,Ada,我在标题中提到的混合语言编程中遇到了问题,更准确地说,就是从Ada到Fortran代码中获取数组。我的Ada过程声明如下所示: procedure Get_Double_Array (Double_Array : in System.Address; Length_of_Array : in System.Address); pragma Export(Fortran, Get_Double_Array, "Get_Double_Array_");

我在标题中提到的混合语言编程中遇到了问题,更准确地说,就是从Ada到Fortran代码中获取数组。我的Ada过程声明如下所示:

    procedure Get_Double_Array
      (Double_Array    : in System.Address;
       Length_of_Array : in System.Address);
    pragma Export(Fortran, Get_Double_Array, "Get_Double_Array_");
我的程序的相应主体是

    procedure Get_Double_Array
      (Double_Array : in System.Address;
       Length_Of_Array : in System.Address)
    is
        use Interfaces.Fortran;

        Array_Length : Fortran_Integer;
        for Array_Length'Address use Length_Of_Array;

        Result_Array : Double_Precision_Array(1..3);
        for Result_Array'Address use Double_Array;
    begin
        Result_Array(1) := Double_Precision(1.0);
        Result_Array(2) := Double_Precision(2.0);
        Result_Array(3) := Double_Precision(3.0);

        Array_Length := Fortran_Integer(Result_Array'Last);
    end Get_Double_Array;
双精度数组的声明如下所示

    type Double_Precision_Array is (Fortran_Integer range <>) of Double_Precision;
    pragma Convention(Fortran, Double_Precision_Array);
    PROGRAM TPROG
    IMPLICIT NONE

    INTERFACE
    SUBROUTINE GETARR(DPARR, LENGTH)
    cDEC$ ATTRIBUTES DLLIMPORT, ALIAS : '_Get_Double_Array_' :: GETARR
    DOUBLE PRECISION, DIMENSION (:) :: DPARR
    INTEGER :: LENGTH
    END SUBROUTINE
    END INTERFACE

    DOUBLE PRECISION, DIMENSION(3) :: XDOT
    INTEGER :: LENGTH
    CALL GETARR(XDOT, LENGTH)
    END PROGRAM TPROG
PROGRAM TEST
IMPLICIT NONE
EXTERNAL initdll
EXTERNAL getdblarray
INTEGER :: LENGTH
DOUBLE PRECISION, DIMENSION(3):: ARR
CALL initdll
CALL getdblarray(ARR, LENGTH)
END PROGRAM TEST
fortran代码使用gfortran编译,并与创建的dll对应的库链接。命令行是

    gfortran -o test.exe test.f Ada_Lib.lib
当我在Call语句之前将调试输出插入fortran代码时,我可以看到Get_Double_数组过程被调用,但我得到了异常

    raised PROGRAM_ERROR: Name_Of_The_Ada_Body.adb: misaligned address value
此消息中的行号是我声明Array_Length变量的行号。我知道Ada属性的对齐方式,但我不知道在这种情况下如何使用它,因为我已经在使用Fortran兼容的数据类型(至少我这么认为)

当我在Ada端导出C约定,并将C约定用于数组声明,并在Fortran中使用“C,DLLIMPORT,ALIAs”调整cDEC$行时,长度值总是正确的,但数组的内容完全无用

阵列的范围仅在调试时固定。稍后,数组可以是任意长度的,这就是为什么我还需要返回数组的长度


任何有用的提示或解释我做错了什么以及下一步可以尝试什么?

问题是Fortran无法以Ada理解的方式将无约束数组传递给Ada(Fortan 90的假定形状数组与Fortan 90有明确的对应关系,但我看不出有任何方法可以告诉Ada方面这就是预期的结果)

我一直在试验这个问题,在MacOSX上使用GCC4.8.0——在我记得在Ada运行时链接之后——它给出了与您得到的相同的异常(顺便问一下,您使用的编译器版本是什么?)

当我尝试将无约束数组传递给过程时

   procedure Get_Double_Array
     (Double_Array        :    out Double_Precision_Array;
编辑说

problem.ada:7:07: warning: type of argument "Get_Double_Array.Double_Array" is unconstrained array
problem.ada:7:07: warning: foreign caller must pass bounds explicitly
(我不知道为什么这是一个警告而不是错误!)和运行时的相同异常

我认为您可以做的是声明一个巨大的数组类型(当然,从来没有实际创建过):

(请注意下面的大小写导出名称),带主体

package body Problematic is

   procedure Get_Double_Array
     (Double_Array        :    out Double_Precision_Array;
      Double_Array_Length : in     Fortran_Integer;
      Output_Length       :    out Fortran_Integer)
   is
   begin
      Double_Array(1) := Double_Precision (1.0);
      Double_Array(2) := Double_Precision (2.0);
      Double_Array(3) := Double_Precision (3.0);

      Output_Length := Fortran_Integer (3);
   end Get_Double_Array;

end Problematic;
测试程序更改为

  PROGRAM TPROG
  IMPLICIT NONE

  DOUBLE PRECISION, DIMENSION(4) :: XDOT
  INTEGER :: LENGTH, J
  CALL GET_DOUBLE_ARRAY(XDOT, 4, LENGTH)
  PRINT *, 'output length is ', LENGTH
  PRINT *, (XDOT(J), J=1,LENGTH)
  END PROGRAM TPROG
(编译器无法处理您的
接口
部分)

注意!到目前为止,Ada没有调用任何Ada运行时工具。如果是的话,你就得

  • 安排在中链接运行时
  • 初始化它,并可能完成它

我想那将是另一个问题

经过更多的实验并得到一些同事的帮助,我们发现了一个使用C约定的解决方案

  • 我忘记了AdaInit和AdaFinal程序。这些函数是在创建dll期间由绑定器创建的。在使用任何功能之前,必须调用AdaInit函数。我们的方法是导出一个函数,如

    procedure Init_Dll;
    pragma Export(C, Init_Dll, "initdll_");
    
  • 在声明的非公开部分,我们添加了

        procedure AdaInit;
        pragma Import(C, AdaInit);
    
        procedure AdaFinal;
        pragma Export(C, AdaFinal);
    
    Init_Dll的主体是直接的

        procedure Init_Dll
        is
        begin
            AdaInit;
        end Init_Dll;
    
    然后使用gnatmake和gnatdell,我们创建了ADADDLL,并使用windows命令行

        lib -machine:IX86 -def:Name_Of_Ada_Package.def -out:Name_Of_Ada_Package.lib > nul
    
    对应的库

  • 在Fortran方面,我们现在使用外部命令而不是接口,因此程序看起来像

        type Double_Precision_Array is (Fortran_Integer range <>) of Double_Precision;
        pragma Convention(Fortran, Double_Precision_Array);
    
        PROGRAM TPROG
        IMPLICIT NONE
    
        INTERFACE
        SUBROUTINE GETARR(DPARR, LENGTH)
        cDEC$ ATTRIBUTES DLLIMPORT, ALIAS : '_Get_Double_Array_' :: GETARR
        DOUBLE PRECISION, DIMENSION (:) :: DPARR
        INTEGER :: LENGTH
        END SUBROUTINE
        END INTERFACE
    
        DOUBLE PRECISION, DIMENSION(3) :: XDOT
        INTEGER :: LENGTH
        CALL GETARR(XDOT, LENGTH)
        END PROGRAM TPROG
    
    PROGRAM TEST
    IMPLICIT NONE
    EXTERNAL initdll
    EXTERNAL getdblarray
    INTEGER :: LENGTH
    DOUBLE PRECISION, DIMENSION(3):: ARR
    CALL initdll
    CALL getdblarray(ARR, LENGTH)
    END PROGRAM TEST
    

  • 根据创建的库编译和链接此程序很好

    为什么要在Ada和Fortran中导出不同的符号?我将使用Fortran 2003与C的互操作性,而不是pragmas。不是说这是DEC pragma,不是GCC pragma,你确定gfortran明白吗,我想它不明白。另外,您是否确定您的
    双精度
    在两种语言中都表示相同的实类型,它几乎可以比默认类型大任何值,默认类型可以是任何值。@VladimirF:my lib中的ada函数名为
    \u name
    。有一个GCC pragma DLLIMPORT,但cGCC$DLLIMPORT::_Name不起作用。编译器不接受前导下划线。这就是为什么我建议Fortran 2003
    bind(C,name=“name”)
    并删除所有下划线的原因。@VladimirF:我找到了一些很好的例子,说明如何使用Fortran 2003,但使用2003标准不是我的决定。我应该只使用fortran 77构造。请注意,这些pragma在fortran 77标准的任何地方都没有定义,您正在玩有问题的东西,并为问题做好准备。还要注意,在示例中使用的是假定形状数组。这需要Fortran 90显式接口,根本不适合您。