Нет вывода из программы на Фортране, использующей Научную библиотеку Gnu, через оболочку c - PullRequest
1 голос
/ 08 сентября 2011

Я пытаюсь написать оболочку для использования библиотеки gsl с Fortran. Мне удалось заставить работать простую оболочку - пример из http://www.helsinki.fi/~fyl_tlpk/luento/ohj-13-GSL-e.html

Код Фортрана

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 (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)); 
}

Фортран основной (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)); 
}

затем я получаю вывод из кода Фортрана (он записывает 'call_rng_initialise' в STDOUT).

Итак, проблема, кажется, в вызовах gsl_rng_alloc и gsl_rng_set. Но я не получаю никаких сообщений об ошибках, и я не знаю, почему они мешают коду Фортрана делать что-либо. Есть идеи?

Ответы [ 3 ]

4 голосов
/ 08 сентября 2011

Как уже предлагалось, лучший способ сделать это - использовать привязку Fortran ISO C, поскольку он будет инструктировать Fortran использовать соглашения о вызовах C для соответствия подпрограммам C библиотеки GSL. Связывание ISO C является частью стандарта Fortran 2003 и было доступно во многих компиляторах Fortran 95 в течение нескольких лет. Как часть стандарта, он делает интерфейс Fortran и C в обоих направлениях переносимыми и намного проще, чем взломы, зависящие от ОС и компилятора, которые раньше были необходимы. Я рекомендую игнорировать инструкции, которые предшествуют привязке ISO C как устаревшей.

Как правило, вам не нужно писать какой-либо код-обертку C для вызова библиотеки GSL, только операторы спецификаций Fortran для описания рутинных интерфейсов C к Fortran в синтаксисе Fortran. Вот простой пример, который вызывает процедуру 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

Еще проще: вы можете найти заранее написанную библиотеку, которая позволит вам вызывать GSL из Фортрана по номеру http://www.lrz.de/services/software/mathematik/gsl/fortran/

1 голос
/ 08 сентября 2011

Скорее всего, у вас неправильная связь между двумя подпрограммами.Если стек не обрабатывается правильно, когда вы прыгаете через этот интерфейс, может случиться что-то опасное.

Я не замечаю никакого кода ни на стороне Фортрана, ни на стороне С, определяющей соглашение о вызовах другого.Я не эксперт по Gnu Fortran, но я знаю, что большинству компиляторов потребуется какое-то замечание, что они должны использовать соглашение о вызовах другого компилятора, иначе могут произойти плохие вещи.

С помощью небольшого поиска в Интернете,Я вижу, что в руководстве G95 Fortran (PDF) есть хороший длинный раздел под названием «Взаимодействие с программами G95», в котором, как представляется, подробно об этом говорится.Просто из скимминга, похоже, что вы должны использовать атрибут BIND(C) в объявлении функции Fortran для этой подпрограммы C.

0 голосов
/ 18 октября 2018

Проблема с 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. В противном случае программа потерпит неудачу. К сожалению, я до сих пор не имею четкого представления, почему это должно работать именно так.

...