2

I'm having problems with the mixed-language programming as mentioned in the title, more precisely getting arrays from Ada to the Fortran code. My Ada procedure declaration looks like:

    procedure Get_Double_Array
      (Double_Array    : in System.Address;
       Length_of_Array : in System.Address);
    pragma Export(Fortran, Get_Double_Array, "Get_Double_Array_");

The corresponding body of my procedure is

    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;

The declaration of the Double_Precision_Array looks like

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

Making this procedure available in the DLL is already working. dumpbin /exports on the created dll shows the Get_Double_Array_ as expected.

The Fortran program looks like

    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

The fortran code is compiled with gfortran and linked with the lib corresponding to the created dll. The command line is

    gfortran -o test.exe test.f Ada_Lib.lib

When I inserted debugging output into the fortran code before the Call statement I can see that the Get_Double_Array procedure is called but I get the exception

    raised PROGRAM_ERROR: Name_Of_The_Ada_Body.adb: misaligned address value

The line number in this message is the one where I declare the Array_Length variable. I know about the Ada Attribute 'Alignment, but I don't know how to use it in this situation, because I'm already using Fortran compatible data types (at least I think so).

When I export the C Convention on the Ada side and also use the C convention for the array declaration and adapt the cDEC$ line in Fortran with 'C, DLLIMPORT, ALIAs', the Length value is always correct but the contents of the array are completely useless.

The range of the Arrays is only fixed for debugging. Later the Array can be of any length, which is why I also need to return the length of the array.

Any useful tipps or explanations what I'm doing wrong and what I can try next?

4

2 回答 2

4

问题是 Fortran 无法以 Ada 理解的方式将不受约束的数组传递给 Ada(与 Fortan 90 的假定形状数组有明确的对应关系,但我看不到告诉 Ada 方面这是什么预计)。

我一直在尝试解决这个问题,并且在带有 GCC 4.8.0 的 Mac OS X 上——在我记得在 Ada 运行时链接之后——它给出了与你得到的相同的异常(顺便说一下,什么版本的编译器(s ) 你正在用吗?)。

当我尝试将不受约束的数组传递给过程时,

   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

(为什么这是一个警告而不是我不知道的错误!)以及运行时的相同异常。

我认为你可以做的是声明一个巨大的数组类型(当然从不实际创建一个):

with Interfaces.Fortran; use Interfaces.Fortran;
package Problematic is

   type Double_Precision_Array is
     array (Fortran_Integer range 1 .. Fortran_Integer'Last)
     of Double_Precision;
   pragma Convention (Fortran, Double_Precision_Array);

   procedure Get_Double_Array
     (Double_Array        :    out Double_Precision_Array;
      Double_Array_Length : in     Fortran_Integer;
      Output_Length       :    out Fortran_Integer);
   pragma Export(Fortran, Get_Double_Array, "get_double_array_");

end Problematic;

(注意小写的导出名称),带有正文

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

(编译器无法处理您的INTERFACE部分)。

笔记!到目前为止,Ada 没有调用任何 Ada 运行时设施。如果是这样,你必须

  • 安排要链接的运行时
  • 初始化它,并可能完成它。

我想那将是另一个问题!

于 2013-06-18T14:26:17.617 回答
0

在进行了更多试验并得到了一些同事的帮助之后,我们发现了使用 C 约定的灵魂。

  1. 我忘记了 AdaInit 和 AdaFinal 程序。这些函数是由 binder 在创建 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 和 gnatdll 我们创建了 Ada dll 并使用 windows 命令行

    lib -machine:IX86 -def:Name_Of_Ada_Package.def -out:Name_Of_Ada_Package.lib > nul

对应的库

  1. 在 Fortran 端,我们现在使用外部命令而不是接口,所以程序看起来像

    PROGRAM TEST
    IMPLICIT NONE
    EXTERNAL initdll
    EXTERNAL getdblarray
    INTEGER :: LENGTH
    DOUBLE PRECISION, DIMENSION(3):: ARR
    CALL initdll
    CALL getdblarray(ARR, LENGTH)
    END PROGRAM TEST
    

针对创建的库编译和链接这个程序就可以了

于 2013-06-19T08:10:02.640 回答