我正在尝试使用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个数组被转换成奇怪的单维数组。这是调试器给我的:
有什么想法吗?
答案 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),参数的内存地址 (指针)代替传递。
通过引用传递每个参数。