我的主要问题是,为什么阵列做这种怪异的东西,以及是否有任何方式都做到以下几点以“干净”的方式。
我现在有一个C程序foo.c
接口连接的Fortran程序bar.f90
经由dlopen/dlsym
,大致像在下面的代码:
foo.c的:
#include <dlfcn.h>
#include <stdio.h>
int main()
{
int i, k = 4;
double arr[k];
char * e;
void * bar = dlopen("Code/Test/bar.so", RTLD_NOW | RTLD_LOCAL);
void (*allocArray)(int*);
*(void **)(&allocArray) = dlsym(bar, "__bar_MOD_allocarray");
void (*fillArray)(double*);
*(void **)(&fillArray) = dlsym(bar, "__bar_MOD_fillarray");
void (*printArray)(void);
*(void **)(&printArray) = dlsym(bar, "__bar_MOD_printarray");
double *a = (double*)dlsym(bar, "__bar_MOD_a");
for(i = 0; i < k; i++)
arr[i] = i * 3.14;
(*allocArray)(&k);
(*fillArray)(arr);
(*printArray)();
for(i = 0; i < 4; i++)
printf("%f ", a[i]);
printf("\n");
return 0;
}
bar.f90:
module bar
integer, parameter :: pa = selected_real_kind(15, 307)
real(pa), dimension(:), allocatable :: a
integer :: as
contains
subroutine allocArray(asize)
integer, intent(in) :: asize
as = asize
allocate(a(asize))
return
end subroutine
subroutine fillArray(values)
real(pa), dimension(as), intent(in) :: values
a = values
return
end subroutine
subroutine printArray()
write(*,*) a
return
end subroutine
end module
运行主要收益
0.0000000000000000 3.1400000000000001 6.2800000000000002 9.4199999999999999
0.000000 -nan 0.000000 0.000000
这表明的Fortran正确分配阵列,甚至正确地存储在给定值,但它们不是通过对dlsym再访问(对这些数据的结果的工作在段错误)。 我也试过这个固定大小的数组 - 的结果保持不变。
有谁知道这种行为的原因是什么? 我个人来说,本来期望的东西要么制定出双向或或者根本 - 这“的Fortran接受C数组而不是相反”让我不知道是否有一些基本的错误,我在访问从C数组以这种方式取得。
其他(更重要的)问题是,如何做数组访问像这些“正确的方式”。 目前,我甚至不知道是否坚持了“Fortran语言的。所以”接口是所有的好办法 - 我想这也有可能试图在这种情况下,混合编程。 尽管如此,阵列问题仍然是 - 我读这可以在某种程度上使用ISO C绑定来解决,但我无法弄清楚如何,但(我没有做了很多与Fortran语言,然而,特别是没有说绑定) ,这样有利于对这个问题,将不胜感激。
编辑:
好了,所以我读入ISO C绑定多一点,并找到了一个非常有用的方法在这里 。 使用C_LOC
我能得到C指针,以我的Fortran结构。 不幸的是,指向数组的指针似乎是指针的指针,需要在C代码被解除引用才可以treates为C数组 - 或类似的东西。
编辑:
得到了我的计划,现在的工作用C结合的方式弗拉基米尔˚F指出,至少大部分。 C文件和Fortran文件现在连接在一起,这样我就可以避开libdl接口,至少在Fortran语言的一部分 - 我还需要加载动态C库,在那里,通过得到一个函数指针的标志之一是作为函数指针的Fortran,后来调用函数作为其计算的一部分。 作为所述函数希望双* S [阵列],我无法使用管理C_LOC通过我的Fortran数组,奇怪-既不C_LOC(array)
也不C_LOC(array(1))
通过了正确的指针返回到C函数。 array(1)
并获得成功,但。 可悲的是,这是不是“干净”的方式来做到这一点。 如果任何人有一个提示,我如何做到这一点使用C_LOC
功能,那将是巨大的。 不过我接受弗拉基米尔·F公司的答案,因为我认为它是更安全的解决方案。
在我看来,这不是很好的做法,尝试用Fortran库访问全局数据。 它可以使用普通的块来完成,但他们是邪恶的,需要静态大小的数组。 一般存储协会是一件坏事。
千万不要访问模块符号“__bar_MOD_a”他们是编译器特定的,并不意味着可以直接使用。 通过使用功能和子程序poiters。
传递数组作为子例程的参数。 您还可以分配阵列中的C和它传递给Fortran语言。 有什么可以也做是得到一个指向数组的第一个元素。 它将服务ES的C指针数组。
我的解决方案,对于没有。所以简单,它是微不足道的添加:
bar.f90
module bar
use iso_C_binding
implicit none
integer, parameter :: pa = selected_real_kind(15, 307)
real(pa), dimension(:), allocatable,target :: a
integer :: as
contains
subroutine allocArray(asize,ptr) bind(C,name="allocArray")
integer, intent(in) :: asize
type(c_ptr),intent(out) :: ptr
as = asize
allocate(a(asize))
ptr = c_loc(a(1))
end subroutine
subroutine fillArray(values) bind(C,name="fillArray")
real(pa), dimension(as), intent(in) :: values
a = values
end subroutine
subroutine printArray() bind(C,name="printArray")
write(*,*) a
end subroutine
end module
main.c中
#include <dlfcn.h>
#include <stdio.h>
int main()
{
int i, k = 4;
double arr[k];
char * e;
double *a;
void allocArray(int*,double**);
void fillArray(double*);
void allocArray();
for(i = 0; i < k; i++)
arr[i] = i * 3.14;
allocArray(&k,&a);
fillArray(arr);
printArray();
for(i = 0; i < 4; i++)
printf("%f ", a[i]);
printf("\n");
return 0;
}
编译和运行:
gcc -c -g main.c
gfortran -c -g -fcheck=all bar.f90
gfortran main.o bar.o
./a.out
0.0000000000000000 3.1400000000000001 6.2800000000000002 9.4199999999999999
0.000000 3.140000 6.280000 9.420000
注:没有理由在您的Fortran子程序的回报,他们只是模糊的代码。
Many Fortran compilers use internally something called array descriptors - structures that hold the shape of the array (that is the size and range of each dimension as well as pointer to the real data). It allows for the implementation of things like assumed-shape array arguments, array pointers and allocatable arrays to work. What you are accessing through the __bar_MOD_a
symbol is the descriptor of the allocatable array and not its data.
Array descriptors are compiler-specific and code that relies on specific descriptor format is not portable. Example descriptors:
- GNU Fortran
- Intel Fortran
Note that even those are specific to some versions of those compilers. Intel, for example, states that their current descriptor format is not compatible with the format used in Intel Fortran 7.0.
If you look at both descriptors you would see that they are largerly similar and that the first element is a pointer to the array data. So you would be able to easily read the data using double **
instead of double *
:
double **a_descr = (double**)dlsym(bar, "__bar_MOD_a");
...
for(i = 0; i < 4; i++)
printf("%f ", (*a_descr)[i]);
Once again, this is not portable as the format of those descriptors might change in the future (although I doubt that the data pointer would be moved somewhere else than at the beginning of the descriptor). There is a draft specification that tries to unify all descriptor formats but it is not clear how and when it will be adopted by the different compiler vendors.
Edit: Here is how to use an accessor function that uses C_LOC()
from the ISO_C_BINDING
module to portably obtain a pointer to the allocatable array:
Fortran code:
module bar
use iso_c_binding
...
! Note that the array should be a pointer target
real(pa), dimension(:), allocatable, target :: a
...
contains
...
function getArrayPtr() result(cptr)
type(c_ptr) :: cptr
cptr = c_loc(a)
end function
end module
C code:
...
void * (*getArrayPtr)(void);
*(void **)(&getArrayPtr) = dlsym(bar, "__bar_MOD_getarrayptr");
...
double *a = (*getArrayPtr)();
for(i = 0; i < 4; i++)
printf("%f ", a[i]);
...
Result:
$ ./prog.x
0.0000000000000000 3.1400000000000001 6.2800000000000002
9.4199999999999999
0.000000 3.140000 6.280000 9.420000