键入编组以从C#调用fortran子例程
我正在尝试使用P / invoke从C#代码调用FORTRAN77子例程 – 如果您感兴趣,我正在尝试包装ARPACK库提供的一些function( http://www.caam.rice。教育/软件/ 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)
我在这里找到了一些有价值的信息: 我应该为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 );
我在这里做了几件事:
- 传递作为
UnmanagedType.I4
编组的int
对象以匹配FORTRANinteger
对象 - 传递
float[,]
对象的大小(m,n)并编组为UnmanagedType.LPArray
以匹配FORTRANReal(n,m)
对象 -
将获得的
byte[]
对象作为UnmanagedType.LPArray
编组,以匹配FORTRANCharacter*n
对象。byte[]
对象的计算方法如下:System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding(); byte[] which = encoding.GetBytes(myString);
- 传递一个
int
对象BY VALUE并编组为UnmanagedType.I4
以指示字符串的长度。 请注意,我试图将该参数放在字符串之后以及参数列表的末尾。
这是我最好的镜头 – 无论是这个还是我试过的所有其他事情都没有。 该方法将执行,但它超快速退出(当它应该进行一些非常严格的计算时)。 而且,我的3个数组被转换成奇怪的单维数组。 这是调试器给我的:
任何的想法?
我建议你从一些小的测试代码开始。 使用带有简单参数的子程序编译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
具有实数和整数forms的各种标量和数组值。 例如, DROP1
是输入1Darrays。 PARRAY
将2Darrays输出为1Darrays。 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); }
请注意我没有传递任何字符串。
1)引用维基百科 :
单精度,在C语言系列中称为“float”, 在Fortran中称为“ real”或“real * 4” 。 这是一种占用32位(4字节)的二进制格式,其有效位数的精度为24位(约7位十进制数)。
所以将它作为一个浮标编组。 你可以测试那个,它可以是浮点数也可以是双精度数。
2)引用此Fortran 77教程 :
Fortran 77使用所谓的call-by-reference范例。 这意味着不是仅仅传递函数/子例程参数的值(按值调用),而是传递参数(指针)的内存地址。
通过引用传递每个参数。