没有来自Fortran程序的输出使用Gnu科学库通过c包装器

时间:2011-09-08 12:23:54

标签: c fortran wrapper gsl fortran-iso-c-binding

我正在尝试编写一个包装器来使用带有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代码做任何事情。有什么想法吗?

3 个答案:

答案 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_rngfgsl_rng_type。否则,程序将失败。不幸的是,我仍然不清楚为什么它必须这样工作。