mike sadler
He estado detenido en esto durante aproximadamente 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 el asunto más frustrante, enviar un argumento char* desde FORTRAN a C fue sencillo…
Enviando un argumento char* desde FORTRAN a C (esto funciona bien):
// The C header declaration (using __cdecl in a def file):
extern "C" double GetLoggingValue(char* name);
Y desde 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 * de C, obtengo 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 ido sobredimensionado a 255.) Esta deberían
devolver un puntero a una matriz de 255 caracteres de 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 carácter ‘campana’…
- También he intentado volver:
- Un puntero a CHARACTER(LEN=255, KIND=C_CHAR).
- Literalmente CARÁCTER (LEN=255, TIPO=C_CHAR).
- A INTEGER(C_SIZE_T), y traté de convertirlo en un puntero a una matriz de cadenas.
- UN PERSONAJE.
etc
si alguien me puede dar un ejemplo de como hacerlo se lo agradeceria mucho…
Atentamente,
avatar de usuario
haraldkl
Las cadenas de longitud dinámica siempre son un poco complicadas con la interacción C. Una posible solución es utilizar punteros. Primero, un caso simple, en el que debe entregar una cadena terminada en un carácter nulo a una función C. Si realmente pasa la cadena solo, debe asegurarse de finalizarla con c_null_char, por lo que esta dirección es bastante sencilla. Aquí hay ejemplos de unInterfaz 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 el interfaz
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
de lua_getfield se parece a:
void lua_getfield (lua_State *L, int idx, const char *k)
Y la interfaz de C-Code es:
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
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 usar punteros. Aquí hay un ejemplo con un puntero, donde la C-Routine proporciona la cadena (también de la biblioteca Aotus mencionada anteriormente): donde lua_tolstring tiene lo siguienteinterfaz
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
:
type(c_ptr) :: a_c_string
Finalmente, aquí hay un intento de aclarar cómo se puede interpretar un c_ptr como una cadena de caracteres Fortran: suponga que tiene un c_ptr que apunta a la cadena:
integer(kind=c_size_t) :: stringlen
Y la longitud de la misma viene dada por una variable len con el siguiente tipo:
character,pointer,dimension(:) :: string
Desea obtener esta cadena en un puntero a una cadena de caracteres en Fortran:
call c_f_pointer(a_c_string, string, [ stringlen ])
-
Entonces haces el mapeo:
¡Gracias, heraldkl! El ejemplo anterior es para interactuar con una función C nula: ¿funcionaría este enfoque cuando el tipo de retorno de la función C es un char*? Mi paradigma operativo general es que mis funciones API devuelvan el resultado, en lugar de pasarlas a través de subrutinas vacías, y esto funciona bien cuando se usan todos los demás tipos de datos, EXCEPTO caracteres 🙁
–Mike Sadler
-
2 de abril de 2012 a las 9:37
Supongo que el enfoque anterior también funcionaría con funciones no nulas. No veo una razón por la que esto deba estar influenciado por el valor de retorno.
– Haraldkl
-
2 abr 2012 a las 10:10
Ciertamente estoy feliz de usar punteros; de hecho, ese fue mi primer enfoque. Me temo que tengo problemas para seguir los fragmentos que proporcionó anteriormente. ¿Es todo necesario para convertir un puntero C en una matriz de caracteres const en una cadena FORTRAN? Para decirlo de otra manera, si tengo un TIPO (C_PTR) y una longitud de cadena, ¿cuál es el mínimo que necesito para convertir esto en una cadena FORTRAN? Siento ser obtuso…
–Mike Sadler
-
2 abr 2012 a las 10:51
intenté agregar algún ejemplo para la transferencia a mi respuesta ahora;)
– Haraldkl
-
2 abr 2012 a las 14:21
¡FUNCIONA! Muchas, muchas gracias heraldkl – Estuve cerca de pensar que era imposible. Gracias a todos los muchachos que me ayudaron y comentaron; me ayudó a continuar.
–Mike Sadler
avatar de usuario
mike sadler
Mi agradecimiento a heraldkl por darme la solución a este problema tan frustrante. Estoy publicando lo que finalmente implementé, que incluye la conversión del puntero en la interfaz, lo que significa que la aplicación final puede llamar a la función C sin tener que saber acerca de la conversión del puntero:
// The C declaration header (using __cdecl in a def file):
extern "C" const char* GetLastErrorMessage();
La función C:
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
El módulo de interfaz FORTRAN:
USE MINTERFACES
PRINT *, "--> GetLastErrorMessage: '", TRIM(GetLastErrorMessage()), "'"
Y para llamar a esta función desde un programa FORTRAN:
-
Mi agradecimiento nuevamente a heraldkl por proporcionar esta solución; no habría tenido idea de cómo hacer esto sin su aporte.
No hay problema, me alegro de que te haya ayudado 😉
– Haraldkl
3 abr 2012 a las 18:53
Este hilo es un poco viejo, pero como tuve un problema similar (y probablemente otros lo tendrán), publico una respuesta de todos modos.
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
-
Los códigos publicados anteriormente provocarán una falla 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 deba recortarse antes de usarse), ya que Fortran 2003/2008 admite funciones que devuelven entidades asignables. Usando 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 “NULL”, de manera similar a “(null)” de C impreso en casos similares:
Publiqué una pequeña simplificación de su subrutina a continuación.
– Ondřej Čertík
15 de noviembre de 2016 a las 20:09
CHARACTER(KIND=C_CHAR),DIMENSION(*) :: getlasterrormessage
Siempre lucho con estas funciones de interoperabilidad. Creo que tu interfaz debería declarar
y que, cuando llama a la función, pasa una variable de carácter Fortran correspondiente con una longitud igual o mayor que la longitud de la matriz de caracteres C que espera devolver.
Dado que parece tener Intel Fortran, mire los ejemplos de código proporcionados, le brindan un ejemplo completo para esto.
¿Supongo que sabe que lo que ha publicado no es Fortran sintácticamente correcto?
program test
use iso_c_binding
implicit none
! A C function that returns a string need a pointer to the array of single char
type (c_ptr) :: C_String_ptr
! This is the Fortran equivalent to a string of single char
character (len=1, kind=c_char), dimension(:), pointer :: filchar=>null()
! Interface to a C routine which opens a window to browse for a file to open
interface
function tinyopen(typ) bind(c, name="tinyopen")
use iso_c_binding
implicit none
integer(c_int), value :: typ
type (C_Ptr) :: tinyopen
end function tinyopen
end interface
character (len=256) :: filename
integer typ,jj
typ=1
C_String_ptr = tinyopen(typ)
! convert C pointer to Fortran pointer
call c_f_pointer(C_String_ptr,filchar,[256])
filename=" "
if(.not.associated(filchar)) then
! if no characters give error message
write(*,*)'No file name'
else
! convert the array of single characters to a Fortran character
jj=1
do while(filchar(jj).ne.c_null_char)
filename(jj:jj)=filchar(jj)
jj=jj+1
enddo
endif
write(*,*)'Text is: ',trim(filename)
end program test
También tuve problemas para llamar a una rutina C que devuelve una cadena y las respuestas anteriores han sido muy útiles, pero como no sé casi nada de C y las respuestas son un poco confusas, solo quería contribuir con mi solución que usa un puntero C, lo hice no consigue hacer uso de ninguna de las otras propuestas anteriores. El programa C al que llamo abre una ventana separada para buscar un nombre de archivo.
avatar de usuario
Comunidad
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 resultar confuso es que existe una excepción que permite a Fortran hacer coincidir cadenas con arreglos unidimensionales.
¿Quiere decir que quiere llamar a un procedimiento Fortran desde C? Vea este ejemplo: Llamar a una subrutina FORTRAN desde C.
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
EDITAR: tanto ifort como gfortran dicen que las matrices no están permitidas como función devuelve 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 anterior) … debe usar el puntero y luego el c_f_pointer Fortran intrínseco para convertir de la cadena C a una cadena Fortran , como lo explica haraldkl. Aquí hay otro ejemplo de código:
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
Si conoce la longitud de la cadena, la respuesta anterior de Pap se puede simplificar enormemente:
-
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.
n
No estoy seguro de que esto sea Fortran moderno, mientras que mi respuesta no lo es; de hecho, ambos códigos son Fortran 2003. De todos modos, su código es más simple PERO requiere pasar la longitud de la cadena (), como argumento. Por supuesto, conocer la longitud de la cadena de antemano hace que el código sea mucho más pequeño, pero también hace que la función sea menos útil. En la mayoría de los casos, simplemente no sabe cuánto dura la cadena C.
– Papanicolaou
-
15 de junio de 2017 a las 11:18
@Papi, tienes razón. He aclarado mi respuesta para reflejar esto. De hecho, ambos son F2003. Usé la asignación automática del LHS, pero eso también es F2003. Para mi aplicación, tengo acceso a los códigos C y Fortran, por lo que no es un problema pasar la longitud de la cadena C a la interfaz, lo que simplifica enormemente las cosas.
– Ondřej Čertík
Miguel
¿Qué cadenas de herramientas estás usando?
– Michael Burr
2 de abril de 2012 a las 8:07
Estoy usando el compilador VC ++, pero compilando la interfaz en C puro. FORTAN es Visual Fortran 2011, que creo que es FORTRAN 90. Sin embargo, esto es para una API publicada, por lo que debe poder llamarse desde tantos sabores como sea posible. ..
–Mike Sadler