¿Qué tipo de argumento debo usar en C al llamar a una función Fortran que toma argumentos logical , específicamente con gfortran? ¿Dónde está documentado esto para gfortran?
Aquí hay un programa de ejemplo que no se compila sin advertencias:
Contenido de one.f :
subroutine proc1(x) logical x end Contenido de main.c :
void proc1_(_Bool *x); int main() { _Bool x; proc1_(&x); return 0; }Si compilo usando GCC de la siguiente manera, con LTO habilitado, recibo una advertencia sobre prototipos de funciones que no coinciden:
gfortran -flto -c one.f gcc -flto -c main.c gcc -flto main.o one.oLa advertencia que recibo:
main.c:2:6: warning: type of 'proc1_' does not match original declaration [-Wlto-type-mismatch] 2 | void proc1_(_Bool *x); | ^ one.f:2:22: note: 'proc1' was previously declared here 2 | subroutine proc1(x) | ^ one.f:2:22: note: code may be misoptimized unless '-fno-strict-aliasing' is usedTenga en cuenta que habilitar LTO le permite al enlazador verificar que los tipos de argumentos coincidan entre prototipos. Desafortunadamente, usar LTO no es nuestra elección. CRAN requiere que el código enviado se compile sin estas advertencias con LTO habilitado.
Solo veo problemas cuando trato de usar argumentos logical . real , integer y character están todos bien.
Se le puede pedir a gfortran que produzca prototipos en C, y este es el resultado que me da:
gfortran -flto -fc-prototypes-external -c one.f void proc1_ (int_fast32_t *x); Usar int_fast32_t en el prototipo C tampoco funciona. Ningún tipo que probé lo hizo, ni int , ni _Bool . Por lo general, cuando hay una discrepancia de tipos entre los prototipos, el mensaje de error menciona cuál debería ser el tipo, pero no en este caso.
¿Cómo puedo encontrar cuál es el tipo correcto para usar?
Para una interoperabilidad C-Fortran realmente moderna, debe usar los tipos (tipos) proporcionados por el módulo iso_c_binding y hacer que su procedimiento Fortran se bind(c) . De esa manera puedes usar logical(c_bool) .
Al estilo antiguo, lo mejor es trabajar con enteros y pasar un int y solo corregir de integer a logical dentro de Fortran. La antigua C no tenía ningún bool , se agregó más tarde.
Con cambios mínimos:
subroutine proc1(x) use iso_c_binding logical(c_bool) x end #include <stdbool.h> void proc1_(bool *x); int main() { bool x; proc1_(&x); return 0; } > gfortran -flto -c one.f > gcc -flto -c main.c > gcc -flto main.o one.ono emite ninguna advertencia en mi Linux y GCC 7 y 10.
O después de más cambios:
subroutine proc1(x) bind(C, name="proc1") use iso_c_binding logical(c_bool), value :: x end #include <stdbool.h> void proc1(bool x); int main() { bool x; proc1(x); return 0; }El cambio a pasar por valor, por supuesto, solo cuando de hecho es solo un parámetro de entrada.
La solución portátil correcta y garantizada es, como se explica en la respuesta de Vladimir F, crear una rutina de envoltura Fortran que use ISO_C_BINDING. Este contenedor también puede aprovechar la oportunidad para hacer una interfaz C más idiomática, por ejemplo, usando el especificador de value para pasar escalares por valor.
Sin embargo, para la solución rápida y sucia que funciona en GFortran SIN LTO (y algo probable en otros compiladores, pero no hay garantías), consulte https://gcc.gnu.org/onlinedocs/gfortran/Internal-representation-of-LOGICAL- variables.html#Representación-interna-de-variables-LOGICAS . Es decir, puede pasar una variable entera de C del tamaño apropiado que contenga 1 para verdadero y 0 para falso. Tamaño apropiado aquí, lo que significa que, a menos que haya compilado su código Fortran con -fdefault-integer-8 o tales opciones de compilación, el tipo lógico predeterminado de GFortran será de 4 bytes, por lo que un C int simple debería ser bueno (o int32_t si realmente desea asegúrese, aunque no creo que GFortran admita ningún objetivo en el que C int no sea de 32 bits).
La razón por la que esto no funciona con LTO es que, si bien lo anterior funciona, en las entrañas de GCC, las variables LÓGICAS de Fortran son casi las mismas que los números enteros, pero no del todo. Entonces, en la práctica, son variables enteras especiales con un valor máximo de 1 y un valor mínimo de 0, aunque ocupan más espacio (según lo especificado por su tipo de parámetro). Por lo tanto, es probable que este tipo de discrepancia de tipo sea de lo que se queja. Desafortunadamente, no hay solución para esta, excepto la solución correcta anterior a través de ISO_C_BINDING.