Как правильно вызывать функции C с непрозрачными указателями из Fortran 2003? - PullRequest
0 голосов
/ 27 сентября 2018

Я изучаю Fortran 2003. В качестве учебного задания я пытаюсь звонить из Fortran 2003 в библиотеку C, в которой используются непрозрачные указатели:

struct foobar_s;
typedef struct foobar_s *foobar;
foobar foo_create(enum foo, unsigned int);
void foo_destroy(foobar);

Большинство советов, которые я нашел в Интернетеговорит мне описать тип foobar как type(c_ptr), поэтому должно работать следующее:

!foobar foo_create(enum foo, unsigned int);
function foo_create(mode,n) bind(c) ret(foo)
 type(c_ptr) :: foo
 integer(kind(ENUM_FOO_CONSTANT)), value :: mode
 integer(kind=c_int), value :: n
end function

Это объявляет foo_create как возвращающее void* вместо foobar = struct foobar_s *, но все равно работает на современных архитектурах.

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

type, bind(c) :: foobar
  private
  type(c_ptr) :: ptr
end type

, что соответствует:

typedef struct {
    void * ptr;
} foobar;

на стороне C.Теперь §6.7.2.1 стандарта C гарантирует, что адрес начала struct является адресом первого элемента (верно?), Но в его конце может быть некоторое заполнение (но на архитектурах, которые я там использую)это не так, потому что указатели выровнены), поэтому вся эта штуковина работает на моих машинах:

!foobar foo_create(enum foo, unsigned int);
function foo_create(mode,n) bind(c) ret(foo)
 type(foobar) :: foo
 integer(kind(ENUM_FOO_CONSTANT)), value :: mode
 integer(kind=c_int), value :: n
end function

!void foo_destroy(foobar);
 sobroutine foo_destroy(foo) bind(c)
 type(foobar), value :: foo
end subroutine

Я убедился, что Valgrind не показывает ошибок для программы, которая вызывает функции C foo_create() и foo_destroy() из Фортрана, использующего это определение типа.Тем не менее, это не может быть определенным доказательством.

Предполагается ли, что struct { void * ptr } имеет тот же размер и битовый шаблон, что и struct foobar_s *, сломается?Это лучший способ обернуть непрозрачный указатель C (и создать отдельный тип) в Fortran 2003?

1 Ответ

0 голосов
/ 30 сентября 2018

Язык C требует, чтобы все объявления, которые ссылаются на один и тот же объект или функцию, имели совместимый тип.Учитывая эффективные объявления C вашего кода на Фортране, ваш подход нарушает это требование.Практический результат этого требования состоит в том, что компилятор может использовать разные подходы для возврата чего-либо, объявленного struct { void * ptr }, чем чего-то, объявленного struct foobar_s * (например, агрегат может быть возвращен в области, обозначенной скрытым аргументом, переданным функции, указателем).результат может быть возвращен в регистр).Такая разница в реализации будет катастрофической для вашего кода.

TYPE (C_PTR) в Фортране может использоваться как для void *, так и для struct foobar_s*, существует неявное требование компилятора С к Фортранупроцессор, использующий один и тот же метод представления для всех типов указателей объектов C (см. примечание 15.9 к f2003).

Типичный подход заключается в написании небольших процедур-оболочек Fortran вокруг функций C, которые присваивают набор и ссылаются на частный C_PTRсоставная часть.Тип Fortran с компонентом C_PTR не должен быть совместимым.Если тип Fortran не совместим, вы можете использовать современные функции Fortran, такие как расширение типа и финализаторы - foo_destroy выглядит как то, что было бы полезно вызвать из финализатора.

MODULE Fortran_Wrapper
  USE, INTRINSIC :: ISO_C_BINDING, ONLY: xxxxx
  ...
  ! Enum definition in here somewhere.
  ...
  PUBLIC :: foobar
  PUBLIC :: Create

  ! Wrapper for a pointer to foobar_s.
  TYPE :: foobar
    PRIVATE
    TYPE(C_PTR) :: ptr = C_NULL_PTR
  CONTAINS
    FINAL :: final
  END TYPE foobar
  ...
CONTAINS
  ! Wrapper around foo_create, exposed to Fortran client code.
  FUNCTION Create(mode, n) RESULT(obj)
    INTEGER(KIND(ENUM_FOO_CONSTANT)), INTENT(IN) :: mode
    ! Perhaps the next argument is taken as default integer, and 
    ! you do kind conversion inside this wrapper.
    INTEGER(C_INT), INTENT(IN) :: n
    TYPE(foobar) :: obj

    INTERFACE
      FUNCTION foo_create(mode, n) BIND(C, NAME='foo_create')
        USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_INT, C_PTR
        IMPORT :: ENUM_FOO_CONSTANT
        IMPLICIT NONE
        INTEGER(KIND(ENUM_FOO_CONSTANT)), VALUE :: mode
        INTEGER(KIND=C_INT), VALUE :: n
        TYPE(C_PTR) :: foo_create
      END FUNCTION foo_create
    END INTERFACE

    obj%ptr = foo_create(mode, n)
  END FUNCTION Create

  ! Use a finalizer to do automatic cleanup off the C structures.
  ! (Impure elemental is F2008.)
  IMPURE ELEMENTAL SUBROUTINE final(obj)
    TYPE(foobar), INTENT(INOUT) :: obj
    INTERFACE
      SUBROUTINE foo_destroy(obj) BIND(C, NAME='foo_destroy')
        USE, INTRINSIC :: ISO_C_BINDING, ONLY: C_PTR
        IMPLICIT NONE
        TYPE(C_PTR), VALUE :: obj
      END SUBROUTINE foo_destroy
    END INTERFACE

    IF (C_ASSOCIATED(obj%ptr)) CALL foo_destroy(obj%ptr)
  END SUBROUTINE final 
END MODULE Fortran_Wrapper

(Обратите внимание, что тело интерфейса Fortranв вопросе отсутствует атрибут VALUE в двух фиктивных определениях аргументов, в противном случае соответствующий прототип C равен foobar foo_create(enum foo*, unsigned int*).)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...