键入编组以从C#调用fortran子例程

时间:2011-08-05 19:07:45

标签: c# fortran marshalling

我正在尝试使用P / invoke从C#代码调用FORTRAN77子例程 - 如果您感兴趣,我正在尝试包装ARPACK库(http://www.caam.rice.edu/software/ARPACK)提供的一些功能。我有两个问题。


首先,在这种情况下,我找不到关于类型编组的任何地方的明确指示。更具体地说,这是在我的FORTRAN子例程中声明的类型:

       subroutine getEigenVectors
      &   ( Matrix, n, which, nev, ncv, maxn, maxnev, maxncv, ldv, v, d)

 c     %------------------%
 c     | Scalar Arguments |
 c     %------------------%

       character        which*2
       integer          n, nev, maxn, maxnev, maxncv, ldv

 c     %-----------------%
 c     | Array Arguments |
 c     %-----------------%
 c
       Real           
      &                 Matrix(n,n), v(ldv,maxncv), d(maxncv,2)

我在这里找到了一些有价值的信息: What Should I MarshalAs for Character Type in Fortran?,我暗示(我可能错了)我应该使用:

  • [MarshalAs(UnmanagedType.I4)] int传递整数
  • [MarshalAs(UnmanagedType.LPArray)] byte[]传入字符串

但是,我完全不知道如何处理Real数组。 有人对此有任何想法吗?


其次,我很困惑我是否应该将我的论据作为参考传递。我对FORTRAN并不熟悉 - 我知道,这使得任务有点困难;然而,只有ARPACK才能做我想做的事情。虽然FORTRAN子程序默认将所有参数作为引用,但我确实在某处读过。 我是否应该将所有参数作为参考传递?

提前感谢您的帮助! 纪尧姆


编辑(8/6/11)

所以这是我最后的决定:

    [DllImport("Arpack.dll", EntryPoint = "#140")]
    private static extern void getEigenVectors(
        [MarshalAs(UnmanagedType.LPArray)] ref float[,] matrix, 
        [MarshalAs(UnmanagedType.I4)] ref int n,
        [MarshalAs(UnmanagedType.LPArray)] ref byte[] which,
        [MarshalAs(UnmanagedType.I4)] int whichLength,
        [MarshalAs(UnmanagedType.I4)] ref int nev, 
        [MarshalAs(UnmanagedType.I4)] ref int ncv,
        [MarshalAs(UnmanagedType.I4)] ref int maxn,
        [MarshalAs(UnmanagedType.I4)] ref int maxnev,
        [MarshalAs(UnmanagedType.I4)] ref int maxncv,
        [MarshalAs(UnmanagedType.I4)] ref int ldv,
        [MarshalAs(UnmanagedType.LPArray)] ref float[,] v,
        [MarshalAs(UnmanagedType.LPArray)] ref float[,] d
    );

我在这里做了几件事:

  • int个对象编组为UnmanagedType.I4以匹配FORTRAN integer个对象
  • 传递float[,]个大小为(m,n)的对象并编组为UnmanagedType.LPArray以匹配FORTRAN Real(n,m)个对象
  • 将获得的byte[]个对象作为UnmanagedType.LPArray编组,以匹配FORTRAN Character*n个对象。 byte[]个对象的计算方法如下:

    System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
    byte[] which = encoding.GetBytes(myString);
    
  • 传递int对象BY VALUE并编组为UnmanagedType.I4以指示字符串的长度。请注意,我试图将该参数放在字符串之后以及参数列表的末尾。

这是我最好的一次 - 我和其他所有尝试过的东西都没有。该方法将执行,但它超快速退出(当它应该进行一些非常严格的计算时)。而且,我的3个数组被转换成奇怪的单维数组。这是调试器给我的:

enter image description here

有什么想法吗?

2 个答案:

答案 0 :(得分:2)

我建议你从一些小的测试代码开始。使用带有简单参数的子程序编译FORTRAN .dll,并使用 C#来调用它。此外,您可能希望将包含许多参数的Fortran包装到单个结构(TYPE关键字)中,这使得互操作变得更加容易。

这是一个工作示例,您可以使用有关它如何工作的许多想法。

原始FORTRAN代码:

  SUBROUTINE CALC2(BLKL,BLKW, N_LAMINA,N_SLICE, LOAD, SLOP,SKW,    &
                    DIA1,DIA2, Y1, Y2, N1, N2, DROP1, DROP2,        &
                    PARRAY, P_MAX, P_MAX_INDEX, ENDEFCT)
  !DEC$ ATTRIBUTES DLLEXPORT :: CALC2
  !DEC$ ATTRIBUTES ALIAS:'CALC2' :: CALC2
  !DEC$ ATTRIBUTES VALUE :: BLKL, BLKW, N_LAMINA, N_SLICE, LOAD, SLOP, SKW
  !DEC$ ATTRIBUTES VALUE :: DIA1, DIA2, Y1, Y2, N1, N2
  IMPLICIT NONE
  INTEGER*4, INTENT(IN) ::N_LAMINA, N_SLICE
  REAL*4, INTENT(IN) :: BLKL, BLKW, LOAD, SLOP, SKW,     &
                        DIA1, DIA2, Y1, Y2, N1, N2,   &
                        DROP1(MAX_LAMINA), DROP2(MAX_LAMINA)
  REAL*4, INTENT(OUT):: PARRAY(MAX_PATCH), P_MAX
  INTEGER*4, INTENT(OUT) :: P_MAX_INDEX, ENDEFCT
  INTEGER*4 :: NDIAG, N_PATCH
  REAL*4 :: SLNG, SWID
  REAL*4 :: DROPS_1(MAX_LAMINA), DROPS_2(MAX_LAMINA)

...

  END SUBROUTINE CALC2

具有实数和整数形式的各种标量和数组值。例如DROP1是输入1D数组。 PARRAY正在输出2D数组作为一维数组。 BLKL是输入浮点数。

请注意!DEC$ ATTRIBUTES VALUE装饰,以避免将所有内容声明为ref

C#中,代码由

调用
    [DllImport("mathlib.dll")]
    static extern void CALC2(float major_dim, float minor_dim, 
        int N_lamina, int N_slices, float total_load, 
        float slope, float skew, float diameter_1, float diameter_2, 
        float youngs_1, float youngs_2, float nu_1, float nu_2, 
        float[] drops_1, float[] drops_2, float[] pressures, 
        ref float p_max, ref int p_max_index, ref EndEffect end_effect);

...
   {
        float max_pressure = 0;
        int max_pressure_index = 0;
        float[] pressures = new float[Definition.MAX_PATCH];
        EndEffect end_effect = EndEffect.NO;

        CALC2(length, width, lamina_count, slice_count, load, slope, skew, 
            dia_1, dia_2, y_1, y_2, n_1, n_2, drops_1, drops_2, pressures, 
            ref max_pressure, ref max_pressure_index, ref end_effect);
    }

note 我没有传递任何字符串。

答案 1 :(得分:0)

1)引用Wikipedia

  

单一精度,在C语言系列中称为“float”,和   Fortran中的“真实”或“真实* 4”。这是占用的二进制格式   32位(4字节)及其有效位数的精度为24位   (约7位小数)。

所以将它作为一个浮标编组。你可以测试那个,它可以是浮点数也可以是双数。

2)引用此Fortran 77 Tutorial

  

Fortran 77使用所谓的call-by-reference范例。这意味着   而不是只传递函数/子例程的值   参数(call-by-value),参数的内存地址   (指针)代替传递。

通过引用传递每个参数。