Как выделить массив внутри подпрограммы фортрана, вызванной из C

Я думаю, название говорит о том, что мне нужно. Я знаю, что для этого можно использовать функцию "asd", но по некоторым причинам мне нужно сделать выделение на Фортране (т.е. в подпрограмме "asd_"). Вот код C:

#include <stdio.h>

void asd(float **c) {
  *c = (float *) malloc (2*sizeof(float));
  **c =123;
  *(*c+1)=1234;
}

void asd_(float **c);

main () {
  float *c;
  asd_(&c);
// asd(&c); would do the job perfectly
  printf("%f %f \n",c[0],c[1]);
  return 0;
}

А вот код Фортрана:

  subroutine asd(c)

  implicit none

  real, pointer, allocatable ::c(:)

  print *, associated(c)
  if(.not. associated(c))  allocate(c(2))

  end subroutine 

Это случайным образом дает ошибку сегментации. Любая помощь будет оценена по достоинству.


person mem    schedule 13.03.2012    source источник
comment
Какую операционную систему ты используешь?   -  person xxbbcc    schedule 13.03.2012
comment
Я согласен с Андреем. Я знаю, что в компиляторах XL есть модуль ISO_C_BINDING, который поддерживает взаимодействие/совместимость между встроенными типами C и Fortran. Я считаю, что есть аналогичный модуль для gcc. Проверьте это -› gcc.gnu.org/onlinedocs/gfortran/ISO_005fC_005fBINDING.html   -  person Gargi Srinivas    schedule 13.03.2012
comment
Это линукс. Я использую компиляторы ifort и icc. Во всяком случае, решение MSB работает. Спасибо.   -  person mem    schedule 13.03.2012


Ответы (3)


Fortran 2003 ISO C Binding предоставляет портативный способ сделать это. Он реализован во многих компиляторах. Вот пример кода.

#include <stdio.h>

void test_mem_alloc ( float ** array );

int main ( void ) {

   float * array;
   test_mem_alloc (&array);

   printf ( "Values are: %f %f\n", array [0], array [1] );

   return 0;
}

а также

subroutine test_mem_alloc ( c_array_ptr ) bind (C, name="test_mem_alloc")

   use, intrinsic :: iso_c_binding
   implicit none

   type (c_ptr), intent (out) :: c_array_ptr
   real (c_float), allocatable, dimension (:), target, save :: FortArray

   allocate (FortArray (1:2) )
   FortArray = [ 2.5_c_float, 4.4_c_float ]

   c_array_ptr = c_loc (FortArray)

end subroutine test_mem_alloc
person M. S. B.    schedule 13.03.2012
comment
Ты обалденный. Спасибо большое. - person mem; 13.03.2012
comment
Хорошее решение, однако я нахожу трюк с save немного хаком: он не является потокобезопасным, а также его нельзя снова освободить. В моем сообщении вы найдете решение, которое основано на этом, но устраняет эти проблемы. - person Bálint Aradi; 01.07.2016

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

void mywrap_(void **);
void myprint_(void *);

main () {
  void *d;
  mywrap_(&d);
  myprint_(d);
  return 0;
}

А вот и обертка:

  subroutine mywrap(b)
  implicit none
  include "h.h"     
  type(st), target, save :: a
  integer, pointer :: b
  interface 
     subroutine alloc(a)
        include "h.h"
        type(st) a
     end subroutine alloc
  end interface

  call alloc(a)
  b => a%i
  end

И коды Fortran:

  subroutine alloc(a)
  implicit none 
  include "h.h"
  type(st) a

  a%i = 2
  a%r = 1.5
  if (allocated(a%s)) deallocate(a%s)
  allocate(a%s(2))
  a%s(1) = 1.23
  a%s(2) = 1234
  end
  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  subroutine myprint(a)
  implicit none
  include "h.h"     
  type(st) a

  print *,"INT: ", a%i
  print *,"REAL: ", a%r
  print *,"ALLOC: ", a%s
  end

И заголовочный файл "h.h":

  type st
     sequence
     integer i
     real r
     real, allocatable :: s(:)
  end type

Обратите внимание, что таким образом все объекты непрозрачны в C.

person mem    schedule 13.03.2012

Если вам нужно безопасное для потоков решение и/или возможность снова освободить пространство от C, приведенный ниже пример подойдет:

#include <stdio.h>

void test_mem_alloc(float ** array, void **wrapper);
void free_wrapper(void **wrapper);

int main()
{

  float *array;
  void *wrapper;

  /* Allocates space in Fortran. */
  test_mem_alloc(&array, &wrapper);
  printf( "Values are: %f %f\n", array [0], array [1]);
  /* Deallocates space allocated in Fortran */
  free_wrapper(&wrapper);

  return 0;
}

На стороне Фортрана у вас есть общий тип-оболочка CWrapper, который может содержать любой производный тип. Последний содержит данные, которые вы хотели бы передать. Тип CWrapper принимает произвольную полезную нагрузку, и вы всегда будете вызывать подпрограмму free_wrapper() из C, чтобы освободить память.

module memalloc
  use, intrinsic :: iso_c_binding
  implicit none

  type :: CWrapper
    class(*), allocatable :: data
  end type CWrapper

  type :: CfloatArray
    real(c_float), allocatable :: array(:)
  end type CfloatArray

contains

  subroutine test_mem_alloc(c_array_ptr, wrapper_ptr)&
      & bind(C, name="test_mem_alloc")
    type (c_ptr), intent (out) :: c_array_ptr
    type(c_ptr), intent(out) :: wrapper_ptr

    type(CWrapper), pointer :: wrapper

    allocate(wrapper)
    allocate(CfloatArray :: wrapper%data)
    select type (data => wrapper%data)
    type is (CfloatArray)
      allocate(data%array(2))
      data%array(:) = [2.5_c_float, 4.4_c_float]
      c_array_ptr = c_loc(data%array)
    end select
    wrapper_ptr = c_loc(wrapper)

  end subroutine test_mem_alloc


  subroutine free_cwrapper(wrapper_ptr) bind(C, name='free_wrapper')
    type(c_ptr), intent(inout) :: wrapper_ptr

    type(CWrapper), pointer :: wrapper

    call c_f_pointer(wrapper_ptr, wrapper)
    deallocate(wrapper)

  end subroutine free_cwrapper

end module memalloc
person Bálint Aradi    schedule 01.07.2016