resueltos que nombres matriz ejercicios ejemplos caracteres cadenas arreglo array almacenar c++ c fortran fortran-iso-c-binding

c++ - que - matriz de caracteres en c



¿Cómo pasar arreglos de cadenas de C y Fortran a Fortran? (3)

Estoy tratando de pasar una serie de cadenas de C a una subrutina Fortran, así como de Fortran a esa misma subrutina Fortran. He logrado pasar cadenas simples (es decir, matrices de caracteres 1D) con éxito desde C y Fortran. Sin embargo, tengo problemas con los arreglos de cadenas. Estoy usando el enlace ISO C en el lado de Fortran, e idealmente me gustaría que esto sea lo más transparente posible en el lado de la llamada.

He leído algunas preguntas y respuestas relacionadas. Algunos, (es decir, esto y esto ) son simplemente "Use ISO C" sin más detalles, lo que no ayuda mucho. Esta respuesta fue muy útil (respuesta similar a una pregunta diferente ), pero solo funciona para cadenas simples, donde parece que el c_null_char se reconoce en la cadena Fortran única. No puedo imaginar qué hacer para el caso de matriz sin tener dos rutinas separadas.

Lo que tengo actualmente es una rutina C que quiero pasar la matriz de cadenas ( string ) de:

#include <iostream> extern "C" void print_hi_array(char input_string[][255]); using namespace std; int main() { char string[3][255] = {"asdf","ghji","zxcv"}; print_hi_array(string); return 0; }

Y, una rutina Fortran similar:

program main implicit none call print_hi_array( (/"asdf", "ghji", "zxcv"/) ) end program

Hasta ahora, esto es lo que tengo para el receptor:

subroutine print_hi_array(input_string) bind(C) use iso_c_binding, only: C_CHAR, c_null_char implicit none character (kind=c_char, len=1), dimension (3,255), intent (in) :: input_string character (len=255), dimension (3) :: regular_string character (len=255) :: dummy_string integer :: i,j,k write (*,*) input_string do j = 1 , 3 dummy_string(:) = c_null_char k = 1 do i = 1 + (j-1)*255, j*255,1 if (input_string(i) .ne. c_null_char) then write (*,*) "i ",i,j, input_string(i) dummy_string(k:k) = input_string(i) endif k = k +1 enddo regular_string(j) = dummy_string enddo write (*,*) regular_string end subroutine print_hi_array

Esto funciona para la función C; Obtengo esta salida:

asdfghjizxcv j= 1 i 1 1 a i 2 1 s i 3 1 d i 4 1 f j= 2 i 256 2 g i 257 2 h i 258 2 j i 259 2 i j= 3 i 511 3 z i 512 3 x i 513 3 c i 514 3 v asdf ghji zxcv

Sin embargo, cuando se hace a través de Fortran me sale sin sentido:

asdfghjizxcv@O,B�@(P,B�]B]6(P,B�@ .......

Parece que no hay c_null_char en este enfoque.

Entonces, ¿cómo puedo escribir una subrutina Fortran para tomar arreglos de cadenas tanto de C como de Fortran?


Fortran usa espacios para llenar el resto de la cadena si se declara más larga que su texto almacenado. No está delimitado por cero, la longitud declarada se almacena en una variable oculta. No contiene c null char y, por lo tanto, está leyendo basura (desbordamiento de búfer). Lo que Fortran debería imprimir cuando tlit imprime una cadena con / 000 no está definido por la norma y depende de la implementación.

En particular, también está transfiriendo una matriz de caracteres (4) con la dimensión 3 a una subrutina que espera mucha más información (255 caracteres, aunque no estoy seguro del orden de los índices). Solo se pasan los punteros, así que creo que no se puede verificar.

Es posible definir la longitud de las cadenas en el constructor de la matriz de esta manera:

[character(255) :: "a","ab","abc"]


Un enfoque que se me ocurrió es modificar la rutina llamada Fortran para usar también el enlace ISO C:

program main use iso_c_binding, only: C_CHAR implicit none character (kind=c_char, len=255), dimension (3) :: input_string input_string = (/ "asdf", "ghji", "zxcv" /) call print_hi_array(input_string) end program


Veo en realidad dos maneras de hacer eso. O bien, escribes un ciclo en C y pasas las cuerdas una por una a Fortran, como ya lo hiciste antes. Alternativamente, si quiere pasar la matriz completa y quiere manejar las matrices Fortran y C con la misma rutina, tendrá que hacer una copia apropiada de su matriz C-string. Debajo de un ejemplo que funciona, pero no demasiado probado:

extern "C" void print_array_c(int nstring, char input_string[][255]); using namespace std; int main() { char string[3][255] = {"asdf","ghji","zxcv"}; print_array_c(3, string); return 0; }

Tenga en cuenta que también paso el número de cadenas, para que el ejemplo pueda manejar matrices con varios tamaños. (Sin embargo, se supone que la longitud de las cadenas es de 255 caracteres). En el tamaño de Fortran, uno necesitaría una rutina para convertirlo en cadenas Fortran. Una posible visualización podría ser:

module arrayprint_module use, intrinsic :: iso_c_binding implicit none integer, parameter :: STRLEN = 255 contains !> The printing routine, works with Fortran character arrays only. subroutine print_array(strings) character(len=STRLEN), intent(in) :: strings(:) integer :: ii do ii = 1, size(strings) write(*,*) ii, strings(ii) end do end subroutine print_array !> Converts C string array to Fortran string array and invokes print_array. subroutine print_array_c(nstring, cptr) bind(C) integer(c_int), value :: nstring type(c_ptr), intent(in), value :: cptr character(kind=c_char), pointer :: fptr(:,:) character(STRLEN), allocatable :: fstrings(:) integer :: ii, lenstr call c_f_pointer(cptr, fptr, [ STRLEN, nstring ]) allocate(fstrings(nstring)) do ii = 1, nstring lenstr = cstrlen(fptr(:,ii)) fstrings(ii) = transfer(fptr(1:lenstr,ii), fstrings(ii)) end do call print_array(fstrings) end subroutine print_array_c !> Calculates the length of a C string. function cstrlen(carray) result(res) character(kind=c_char), intent(in) :: carray(:) integer :: res integer :: ii do ii = 1, size(carray) if (carray(ii) == c_null_char) then res = ii - 1 return end if end do res = ii end function cstrlen end module arrayprint_module

Tenga en cuenta que la matriz que pasa desde C debe ser contigua para que esto funcione y supongo que el carácter (kind = c_char) es compatible con el tipo de carácter fortran, que normalmente debería ser.