我正在尝试编写一个包装器来使用带有Fortran的gsl库。我设法让一个简单的包装器工作 - 来自http://www.helsinki.fi/~fyl_tlpk/luento/ohj-13-GSL-e.html
的例子Fortran代码
program gsltest
implicit none
real(kind=selected_real_kind(12)) :: a = 0.11, res
external :: glsgateway
call gslgateway(a,res)
write(*,*) 'x', a, 'atanh(x)', res
end program gsltest
c功能
#include <gsl/gsl_math.h>
void gslgateway_(double *x, double *res){
*res = gsl_atanh(*x);
}
这一切都很好。但是,我遇到了更复杂的包装器问题。我在http://apwillis.staff.shef.ac.uk/aco/freesoftware.html
的示例中修改了以下代码c wrapper(rng_initialise.c)
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
static gsl_rng* r;
void rng_initialise__(int* s) {
r = gsl_rng_alloc(gsl_rng_taus);
gsl_rng_set(r, (unsigned long int)(*s));
}
Fortran main(main.f90)
PROGRAM main
integer seed
call system_clock(seed)
WRITE (*,*) 'calling rng_initialise'
call rng_initialise(seed)
END PROGRAM main
然后我按
编译和链接gcc -c rng_initialise.c
g95 -c main.f90
g95 -o main main.o rng_initialise.o -L/usr/libs -lgsl
当我运行这个程序时,我没有输出。但是,如果我注释掉rng_initialise
中的行...
void rng_initialise__(int* s) {
// r = gsl_rng_alloc(gsl_rng_taus);
// gsl_rng_set(r, (unsigned long int)(*s));
}
然后我从Fortran代码中获取输出(它将'calling_rng_initialise'写入STDOUT)。
所以,问题似乎是对gsl_rng_alloc和gsl_rng_set的调用。但我没有得到任何错误消息,我不知道为什么他们会阻止Fortran代码做任何事情。有什么想法吗?
答案 0 :(得分:4)
正如已经建议的那样,最好的方法是使用Fortran ISO C Binding,因为它将指示Fortran使用C调用约定来匹配GSL库的C例程。 ISO C绑定是Fortran 2003标准的一部分,已经在许多Fortran 95编译器中使用了好几年。作为标准的一部分,它使得Fortran和C在两个方向上的接口都是可移植的,并且比以前需要的OS和编译器相关的hacks容易得多。我建议忽略ISO C Binding之前的指令作为过时的。
通常,您不需要编写任何C包装代码来调用GSL库,只需编写Fortran规范语句来描述Fortran语法中Fortran的C例程接口。这是一个调用GSL例程gsl_cdf_chisq_Q
的简单示例program cum_chisq_prob
use iso_c_binding
interface GSL_CummulativeChiSq_Prob_Upper
function gsl_cdf_chisq_Q ( x, nu ) bind ( C, name="gsl_cdf_chisq_Q" )
import
real (kind=c_double) :: gsl_cdf_chisq_Q
real (kind=c_double), VALUE, intent (in) :: x
real (kind=c_double), VALUE, intent (in) :: nu
end function gsl_cdf_chisq_Q
end interface GSL_CummulativeChiSq_Prob_Upper
real (kind=c_double) :: chisq_value
real (kind=c_double) :: DoF
real (kind=c_double) :: Upper_Tail_Prob
write ( *, '( / "Calculates cumulative upper-tail probability for Chi-Square distribution." )' )
write ( *, '( "Input Chisq Value, Degrees of Freedom: " )', advance='no' )
read ( *, * ) chisq_value, DoF
Upper_Tail_Prob = gsl_cdf_chisq_Q ( chisq_value, DoF )
write ( *, '( "Probability is:", 1PG17.10 )' ) Upper_Tail_Prob
stop
end program cum_chisq_prob
更简单:您可以找到预先编写的库,以便在http://www.lrz.de/services/software/mathematik/gsl/fortran/
从Fortran调用GSL答案 1 :(得分:1)
最有可能的是,两个例程之间的链接在某种程度上是错误的。如果在跳过该界面时没有正确处理堆栈,则可能发生任何事情。
我没有注意到Fortran端或C端的任何代码都指定了另一个的调用约定。我不是Gnu Fortran的专家,但我知道大多数编译器需要注意他们应该使用另一个编译器的调用约定,否则可能会发生Bad Things。
只需进行一些网络搜索,我就会看到G95 Fortran manual (PDF)有一个标题为“与G95程序接口”的长篇部分,这似乎详细介绍了这一点。只是从略读,看起来你应该在你的Fortran函数声明中使用该{C}例程的BIND(C)
属性。
答案 2 :(得分:0)
问题出在您的static gsl_rng* r;
文件中定义的C
上。但是我不知道/不理解为什么标准不允许这样做。在研究了fgsl
程序包的源文件后,我发现了一个可行的调整。 fortran
文件random_f.f90
module fgsl
use, intrinsic :: iso_c_binding
implicit none
type, bind(C) :: fgsl_rng_type
type(c_ptr) :: gsl_rng_type_ptr = c_null_ptr
end type fgsl_rng_type
type, bind(C) :: fgsl_rng
type(c_ptr) :: gsl_rng_ptr = c_null_ptr
end type fgsl_rng
end module fgsl
PROGRAM call_fgsl_rndm
use, intrinsic :: iso_c_binding
use fgsl
implicit none
interface
subroutine srndm(seed, t, r) bind(C)
import
integer(C_INT) :: seed
type(fgsl_rng) :: r
type(fgsl_rng_type) :: t
end subroutine srndm
function rndm(r) bind(C)
import
real(C_DOUBLE) :: rndm
type(fgsl_rng) :: r
end function rndm
end interface
type(fgsl_rng) :: r
type(fgsl_rng_type) :: t
integer(C_INT) :: seed
real(C_DOUBLE) :: xi
seed = 1
call srndm(seed, t, r)
xi = rndm(r)
print *, xi
xi = rndm(r)
print *, xi
xi = rndm(r)
print *, xi
END PROGRAM
和C
文件random_c.c
#include <gsl/gsl_rng.h>
typedef struct{
gsl_rng *gsl_rng_ptr;
} fgsl_rng;
typedef struct{
gsl_rng_type *gsl_rng_type_ptr;
} fgsl_rng_type;
void srndm(int *seed, fgsl_rng_type* t, fgsl_rng* r) {
t->gsl_rng_type_ptr = (gsl_rng_type *) gsl_rng_mt19937; // cast to remove the const qualifier
r->gsl_rng_ptr = gsl_rng_alloc(gsl_rng_mt19937);
gsl_rng_set(r->gsl_rng_ptr, *seed);
}
double rndm(fgsl_rng* r) {
return gsl_rng_uniform(r->gsl_rng_ptr);
}
尽管仅使用结构中的指针,但必须引入fgsl_rng
和fgsl_rng_type
。否则,程序将失败。不幸的是,我仍然不清楚为什么它必须这样工作。