Совместимость Fortran со структурой указателя C

У меня есть коммерческая библиотека C, которую я хочу использовать с Fortran. Есть две функции и структура указателя, например:

struct env;
typedef struct env *ENVptr;

две функции имеют прототип:

ENVptr open(int *status_p);
int close(ENVptr **env_p);

Я написал интерфейс Fortran для доступа к ним:

interface
    function c_open(status) bind(c, name='c_open')
        use, intrinsic :: iso_c_binding
        integer(kind = c_int) :: status
        type(c_ptr) :: c_open
    end function 

    function c_close(env) bind(c, name='c_close')
        use, intrinsic :: iso_c_binding
        type(c_ptr)                  :: env
        integer(kind = c_int)        :: c_close
    end function
end interface

Я использую этот код, чтобы использовать это в моей программе Fortran:

type(c_ptr)  :: env = c_null_ptr

env = c_open(status)

if ( status .ne. 0 ) then
   print *, 'Could not open environment'
   stop
end if

...some more code...

if ( c_associated(env) ) then
   status = c_close(env)
    if ( status .ne. 0 ) then
       print *, 'Could not close environment.'
    end if
end if

но когда я выполняю программу, я получаю ошибку ошибки сегментации, когда программа достигает функции c_close.

Это правильный способ взаимодействия с подпрограммами C?


person JaW.    schedule 19.07.2018    source источник
comment
Является ли эта строка int close(ENVptr *env_p); возможно опечатка int close(ENVptr env_p);?   -  person roygvib    schedule 19.07.2018
comment
Да, но другая опечатка, чем вы предполагаете. Исправлено   -  person JaW.    schedule 19.07.2018


Ответы (1)


Я не понимаю, как ваша программа могла быть связана, потому что связывающее имя процедуры должно согласовываться с именем в фактическом прототипе C. Я полагаю, вы могли бы согласовать имена с файлом *.def. Кроме того, в Фортране есть концепция ключевых слов аргументов, поэтому, на мой взгляд, хорошей практикой является согласование фиктивных аргументов Фортрана в интерфейсе с их задокументированными именами аргументов. Помимо этого, у вас, похоже, правильный уровень косвенности в телах интерфейса, поэтому моя версия будет такой:

interface
   function c_open(status_p) bind(C,name='open')
      use, intrinsic :: iso_c_binding
      implicit none
      type(c_ptr) :: c_open
      integer(kind = c_int) status_p
   end function c_open

   function c_close(env_p) bind(c,name='close')
      use, intrinsic :: iso_c_binding
      implicit none
      integer(c_int) c_close
      type(c_ptr) env_p
   end function c_close
end interface

Теперь возникает проблема с уровнем косвенности при вызове c_close, потому что определение типа C для ENVptr уже делает его указателем, поэтому ENVptr** envp является указателем на указатель на указатель. В вашем коде Fortran вы передаете c_ptr, который указывает на непрозрачный тип по ссылке, поэтому вы передаете указатель на указатель. Таким образом, вам нужно создать дополнительный уровень косвенности, чтобы заставить его летать. Соответственно, я бы попытался изменить ваш код на что-то вроде:

type(c_ptr)  :: env = c_null_ptr, envpp = c_null_ptr
target env
integer(c_int) status

env = c_open(status)

if ( status .ne. 0 ) then
   print *, 'Could not open environment'
   stop
end if

!...some more code...

if ( c_associated(env) ) then
   envpp = c_loc(env)
   status = c_close(envpp)
    if ( status .ne. 0 ) then
       print *, 'Could not close environment.'
    end if
end if

Я не могу проверить это, очевидно, но на данный момент это синтаксически правильно и имеет правильный уровень косвенности в соответствии с моим прочтением вашей постановки задачи.

person user5713492    schedule 19.07.2018
comment
ваш код выдаст ошибку: penv = c_loc(env) 1 Error: Argument X at (1) to C_LOC shall have either the POINTER or the TARGET attribute - person JaW.; 20.07.2018
comment
@JanW. Спасибо, я тестировал его только с ifort, который в этом отношении мягче, чем gfortran. Теперь исправлено и проходит проверку синтаксиса с обоими компиляторами. - person user5713492; 20.07.2018