valores una todas tipos retornan que parametros llamar las funciones funcion ejemplos ejemplo con como c interface char fortran fortran-iso-c-binding

todas - Crear una interfaz FORTRAN para una función C que devuelve un carácter*



todas las funciones de c++ (6)

Me he retrasado en esto durante una semana, y he buscado foro tras foro para obtener una explicación clara de cómo enviar un char * de C a FORTRAN. Para hacer que el asunto sea más frustrante, enviar un argumento de char * de FORTRAN a C fue directo ...

Enviar un argumento char * de FORTRAN a C (esto funciona bien):

// The C header declaration (using __cdecl in a def file): extern "C" double GetLoggingValue(char* name);

Y de FORTRAN:

! The FORTRAN interface: INTERFACE REAL(8) FUNCTION GetLoggingValue [C, ALIAS: ''_GetLoggingValue''] (name) USE ISO_C_BINDING CHARACTER(LEN=1, KIND=C_CHAR), DIMENSION(*), INTENT(IN) :: name END FUNCTION GetLoggingValue END INTERFACE ! Calling the function: GetLoggingValue(user_name)

Cuando trato de usar una lógica análoga para devolver un carácter * desde C, aparece un problema tras otro. Un intento que sentí que debería funcionar es:

// The C declaration header (using __cdecl in a def file): extern "C" const char* GetLastErrorMessage();

Y la interfaz FORTRAN:

INTERFACE FUNCTION GetLastErrorMessage [C, ALIAS: ''_GetLastErrorMessage''] () USE ISO_C_BINDING CHARACTER(LEN=1, KIND=C_CHAR), DIMENSION(255), :: GetLastErrorMessage END FUNCTION GetLastErrorMessage END INTERFACE

(No puedo usar literalmente la DIMENSIÓN (*), así que he pasado de tamaño a 255).

Esto debería devolver un puntero a una matriz de 255 caracteres estilo C, pero si lo hace, no he podido convertir esto en una cadena significativa. En la práctica, devuelve un conjunto aleatorio de caracteres, desde Wingdings hasta el personaje ''campana'' ...

También intenté regresar:

  • Un puntero al CARÁCTER (LEN = 255, KIND = C_CHAR).
  • Literalmente CARÁCTER (LEN = 255, KIND = C_CHAR).
  • Un INTEGER (C_SIZE_T), y trató de convertirlo en un puntero a una matriz de cadenas.
  • UN PERSONAJE.
  • etc.

Si alguien puede darme un ejemplo de cómo hacer esto, estaría muy agradecido ...

Atentamente,

Micro


Siempre lucho con estas funciones de interoperabilidad. Creo que su interfaz debería declarar

CHARACTER(KIND=C_CHAR),DIMENSION(*) :: getlasterrormessage

y que, cuando llame a la función, pase una variable de caracteres Fortran correspondiente con una longitud igual o mayor que la longitud de la matriz de C caracteres que espera devolver.

Dado que parece tener Intel Fortran, consulte las muestras de código proporcionadas, dan un ejemplo completo de esto.

Supongo que sabes que lo que has publicado no es sintácticamente correcto Fortran.


Las cadenas de longitud dinámica siempre son un poco complicadas con la interacción C. Una posible solución es usar punteros.

Primero, un caso simple, donde debe entregar una cadena terminada de carácter nulo a una función C. Si realmente solo pasa la cuerda, debe asegurarse de finalizarla con el c_null_char, por lo tanto, esta dirección es bastante directa. Aquí hay ejemplos de una interfaz LuaFortran :

subroutine flu_getfield(L, index, k) type(flu_State) :: L integer :: index character(len=*) :: k integer(kind=c_int) :: c_index character(len=len_trim(k)+1) :: c_k c_k = trim(k) // c_null_char c_index = index call lua_getfield(L%state, c_index, c_k) end subroutine flu_getfield

Y la interfaz de lua_getfield se ve así:

subroutine lua_getfield(L, index, k) bind(c, name="lua_getfield") use, intrinsic :: iso_c_binding type(c_ptr), value :: L integer(kind=c_int), value :: index character(kind=c_char), dimension(*) :: k end subroutine lua_getfield

Y la interfaz de C-Code es:

void lua_getfield (lua_State *L, int idx, const char *k)

Ahora el caso un poco más complejo, donde tenemos que lidiar con una cadena devuelta de C con una longitud dinámica. La solución más portátil que encontré hasta ahora es el uso de punteros. Aquí hay un ejemplo con un puntero, donde la cadena está dada por la C-Rutina (también de la biblioteca de Aotus mencionada anteriormente):

function flu_tolstring(L, index, len) result(string) type(flu_State) :: L integer :: index integer :: len character,pointer,dimension(:) :: string integer :: string_shape(1) integer(kind=c_int) :: c_index integer(kind=c_size_t) :: c_len type(c_ptr) :: c_string c_index = index c_string = lua_tolstring(L%state, c_index, c_len) len = int(c_len,kind=kind(len)) string_shape(1) = len call c_f_pointer(c_string, string, string_shape) end function flu_tolstring

donde lua_tolstring tiene la siguiente interfaz :

function lua_tolstring(L, index, len) bind(c, name="lua_tolstring") use, intrinsic :: iso_c_binding type(c_ptr), value :: L integer(kind=c_int), value :: index integer(kind=c_size_t) :: len type(c_ptr) :: lua_tolstring end function lua_tolstring

Finalmente, aquí hay un intento de aclarar cómo un c_ptr puede interpretarse como una cadena de caracteres Fortran: Supongamos que tiene un c_ptr apuntando a la cadena:

type(c_ptr) :: a_c_string

Y la longitud del mismo viene dada por una variable len con el siguiente tipo:

integer(kind=c_size_t) :: stringlen

Desea obtener esta cadena en un puntero a una cadena de caracteres en Fortran:

character,pointer,dimension(:) :: string

Entonces haces el mapeo:

call c_f_pointer(a_c_string, string, [ stringlen ])


Este hilo es un poco viejo, pero como tuve un problema similar (y probablemente otros lo hagan), publico una respuesta de todos modos.

Los códigos publicados anteriormente causarán un error de segmentación si, por alguna razón, la cadena C es nula. Además, no es necesario devolver una cadena de 255 caracteres (que probablemente será necesario recortar antes de su uso), ya que Fortran 2003/2008 es compatible con funciones que devuelven entidades asignables. Utilizando toda la información publicada anteriormente, terminé con la siguiente función, que obtiene una cadena C (puntero) y devuelve la cadena Fortran correspondiente; Si la cadena C es nula, devuelve "NULO", de forma similar a C "(nulo)" impreso en casos similares:

function C_to_F_string(c_string_pointer) result(f_string) use, intrinsic :: iso_c_binding, only: c_ptr,c_f_pointer,c_char,c_null_char type(c_ptr), intent(in) :: c_string_pointer character(len=:), allocatable :: f_string character(kind=c_char), dimension(:), pointer :: char_array_pointer => null() character(len=255) :: aux_string integer :: i,length call c_f_pointer(c_string_pointer,char_array_pointer,[255]) if (.not.associated(char_array_pointer)) then allocate(character(len=4)::f_string); f_string="NULL"; return end if aux_string=" " do i=1,255 if (char_array_pointer(i)==c_null_char) then length=i-1; exit end if aux_string(i:i)=char_array_pointer(i) end do allocate(character(len=length)::f_string) f_string=aux_string(1:length) end function C_to_F_string


En Fortran, el elemento debe declararse como "carácter (tipo = c_char, len = 1), dimensión (255)" en lugar de len = 255. Esto creará una matriz de caracteres de longitud uno, que es lo que necesita en el lado C. Lo que puede ser confuso es que hay una excepción que permite a Fortran hacer coincidir cadenas con matrices unidimensionales.

¿Quiere decir que quiere llamar a un procedimiento de Fortran desde C? Vea este ejemplo: Llamar a una subrutina FORTRAN desde C.

EDITAR: Tanto ifort como gfortran dicen que las matrices no están permitidas ya que la función vuelve en este contexto. Lo que hace que devolver cadenas como argumentos de función de C a Fortran sea más difícil que usar una cadena como argumento (ejemplo en el enlace de arriba) ... tienes que usar un puntero y luego el c_f_pointer Fortran intrínseco para convertir de la secuencia C a una cadena Fortran , según lo explicado por haraldkl. Aquí hay otro ejemplo de código:

program test_c_func use iso_c_binding implicit none type (C_PTR) :: C_String_ptr character (len=1, kind=c_char), dimension (:), pointer :: ErrChars => null () character (len=255) :: ErrString integer :: i INTERFACE FUNCTION GetLastErrorMessage () bind (C, name="GetLastErrorMessage" ) USE ISO_C_BINDING type (C_PTR) :: GetLastErrorMessage END FUNCTION GetLastErrorMessage END INTERFACE C_String_ptr = GetLastErrorMessage () call c_f_pointer ( C_String_ptr, ErrChars, [255] ) ErrString = " " xfer_string: do i=1, 255 if ( ErrChars (i) == c_null_char) exit xfer_string ErrString (i:i) = ErrChars (i) end do xfer_string write (*, ''( "Fortran: <", A, ">" )'' ) trim (ErrString) end program test_c_func


Mi agradecimiento a heraldkl por darme la solución a este problema tan frustrante. Estoy publicando lo que finalmente implementé, los roles de la conversión del puntero a la interfaz, lo que significa que la aplicación final puede llamar a la función C sin tener que conocer la conversión del puntero:

La función C:

// The C declaration header (using __cdecl in a def file): extern "C" const char* GetLastErrorMessage();

El módulo de interfaz FORTRAN:

MODULE mINTERFACES USE ISO_C_BINDING INTERFACE FUNCTION GetLastErrorMessagePtr [C, ALIAS: ''_GetLastErrorMessage''] () USE ISO_C_BINDING TYPE(C_PTR) :: GetLastErrorMessagePtr END FUNCTION GetLastErrorMessagePtr END INTERFACE CONTAINS ! this must be after all INTERFACE definitions FUNCTION GetLastErrorMessage() USE ISO_C_BINDING CHARACTER*255 :: GetLastErrorMessage CHARACTER, POINTER, DIMENSION(:) :: last_message_array CHARACTER*255 last_message INTEGER message_length CALL C_F_POINTER(GetLastErrorMessagePtr(), last_message_array, [ 255 ]) DO 10 i=1, 255 last_message(i:i+1) = last_message_array(i) 10 CONTINUE message_length = LEN_TRIM(last_message(1:INDEX(last_message, CHAR(0)))) GetLastErrorMessage = last_message(1:message_length-1) END FUNCTION GetLastErrorMessage

Y para llamar a esta función desde un programa FORTRAN:

USE MINTERFACES PRINT *, "--> GetLastErrorMessage: ''", TRIM(GetLastErrorMessage()), "''"

Mi agradecimiento nuevamente a heraldkl por proporcionar esta solución. No tenía ni idea de cómo hacerlo sin su aporte.


Si conoce la longitud de la cadena, entonces la respuesta anterior de Pap se puede simplificar enormemente:

function stringc2f(n, cstr) result(fstr) integer, intent(in) :: n type(c_ptr), intent(in) :: cstr character(:), allocatable :: fstr character(n, kind=c_char), pointer :: fptr call c_f_pointer(cstr, fptr) fstr = fptr end function

La función anterior acepta un puntero C con la cadena y la longitud de la cadena, y devuelve una copia como una cadena Fortran.