sirven que punteros puntero parametros para operadores los lenguaje funciones direccion declaracion como cadenas c interop fortran fortran-iso-c-binding derived-types

que - punteros como parametros de funciones en c



Manteniendo un puntero a una funciĆ³n C dentro de un tipo derivado Fortran (1)

Un argumento ficticio en un procedimiento de Fortran con el atributo BIND(C) que no tiene el argumento VALOR es equivalente en el lado C a un parámetro de puntero (esto es ampliamente coherente con la convención habitual de Fortran de que las cosas pasen por referencia). Entonces, si en el lado de Fortran tienes INTEGER(C_INT) :: a (sin atributo de valor), eso es equivalente en el lado C a int *a .

Tal vez eso sea obvio, pero tiene una consecuencia sorprendente: si tiene TYPE(C_PTR) :: p , eso es equivalente a void **p - un C_PTR es un puntero, por lo que un C_PTR pasado sin valor es un puntero a un puntero. Dado esto, su interfaz para la devolución de llamada está desactivada (debe agregar VALUE ).

El análogo interoperable en un sentido de tipo a un puntero C a una función (que es lo que el nombre de una función sin paréntesis está en C) en Fortran es un TYPE(C_FUNPTR) . Se aplican las mismas consideraciones con respecto a la ausencia del atributo VALUE y C_PTR ; un argumento declarado TYPE(C_FUNPTR) :: f es un puntero a un puntero a una función. Teniendo en cuenta esto y su llamada lateral C del Fortran, el argumento correspondiente al puntero a la función debe tener el atributo VALUE .

El hecho de que un puntero de procedimiento Fortran funcione es solo una coincidencia (no terriblemente sorprendente) de la implementación subyacente de punteros de función C y punteros de procedimiento de Fortran, y la forma en que se pasan los punteros de procedimiento de Fortran.

En resumen, su procedimiento de Fortran probablemente necesite una interfaz que se vea así:

integer(c_int) fortran_function(n, image, callback, c_object) & bind(c, name=''fortran_function'') integer(c_int), value :: n integer(c_signed_char), intent(inout), dimension(n) :: image type(c_funptr), intent(in), value :: callback type(c_ptr), intent(in), value :: c_object

(su declaración de la matriz de imágenes en su código original parece descarriarse, quizás lo anterior sea apropiado, tal vez no)

y su declaración de la interfaz de la devolución de llamada C necesita tener una interfaz de:

abstract interface subroutine c_callback(c_object) bind(c) use, intrinsic :: iso_c_binding implicit none type(c_ptr), intent(in), value :: c_object end subroutine c_callback end interface

(Como se discutió en los foros de Intel en los últimos meses (¿dónde has estado?), Ifort actual puede tener un problema con su manejo de C_PTR y VALUE .)

Tengo una DLL Fortran que se llama desde un programa C, y uno de mis procedimientos necesita llamar periódicamente a una función de devolución de llamada que es suministrada por el programa C. Actualmente lo tengo funcionando bien en su forma ''simple'', pero me gustaría poder almacenar mi puntero de devolución de llamada dentro de un tipo derivado, para que pueda ser transmitido dentro de mi código Fortran más fácilmente. Hasta ahora, nada de lo que he intentado parece funcionar.

Para empezar, esto es lo que tengo en este momento, y esto funciona:

Comenzando en el programa C (OK, en realidad C ++), el prototipo del encabezado para la devolución de llamada es:

typedef void (*fcb)(void *)

y el prototipo para la llamada Fortran es:

extern "C" __declspec(dllexport) int fortran_function(int n, uchar *image_buffer, fcb callback, void *object);

La función de devolución de llamada real es:

void callback(void* pObject) { // Cast the void pointer back to the appropriate class type: MyClass *pMyObject = static_cast<MyClass *>(pObject); pMyObject -> updateImageInGUI(); }

y la llamada al código de Fortran desde C ++ es:

int error = fortran_function(m_image.size(), m_image.data, callback, this);

donde m_image es una matriz de datos de imagen que es un atributo miembro del objeto actual. Lo que sucede es que C ++ pasa los datos de imagen en bruto a la DLL de Fortran y le pide al Fortran que la procese, y como esto lleva mucho tiempo, Fortran actualiza periódicamente el búfer de imagen y llama a la devolución de llamada para actualizar la GUI. De todos modos, pasando al lado de Fortran, definimos una interfaz para la devolución de llamada C:

abstract interface subroutine c_callback(c_object) bind(c) use, intrinsic :: iso_c_binding type(c_ptr), intent(in) :: c_object end subroutine c_callback end interface

y define nuestra rutina principal de Fortran así:

integer(c_int) fortran_function(n, image, callback, c_object) & bind(c, name=''fortran_function'') integer(c_int), value :: n integer(4), intent(inout), dimension(n) :: image procedure(c_callback) :: callback type(c_ptr), intent(in) :: c_object

En algún lugar de la rutina principal llamamos a nuestra subrutina, foo :

call foo(data, callback, c_object)

... donde foo se define como:

subroutine foo(data, callback, c_object) type(my_type), intent(inout) :: data procedure(c_callback) :: callback type(c_ptr), intent(in) :: c_object ... call callback(c_object) ... end function foo

Como dije, todo esto funciona bien y lo ha hecho durante mucho tiempo.

Ahora por las cosas que he intentado pero que no funcionan:

El enfoque ingenuo, simplemente copiar los argumentos en los campos de una estructura

Espero que esto funcione, ya que todo lo que hago es copiar los elementos originales en una estructura sin modificaciones. Nada cambia en el lado C, ni en la definición de la función Fortran principal ni en la interfaz abstracta de c_callback . Todo lo que hago es crear un nuevo tipo derivado Fortran:

type :: callback_data procedure(c_callback), pointer, nopass :: callback => null() type(c_ptr) :: c_object end type callback_data

y luego en mi función principal llenó esto con los valores recibidos de la aplicación C:

data%callback_data%callback => callback data%callback_data%c_object = c_object call foo(data)

La subrutina foo ha sido ligeramente modificada para que ahora busque la devolución de llamada y el objeto C dentro de la estructura:

subroutine foo(data) type(my_augmented_type), intent(inout) :: data ... call data%callback_data%callback(data%callback_data%c_object) ... end function foo

Esto falla en la llamada con una "ubicación de lectura de violación de acceso 0xffffffffffffffff".

El enfoque sofisticado usando más de las características iso_c_binding

De nuevo, nada cambia en el lado C, pero modifico el lado Fortran de la función principal para recibir la devolución de llamada como c_funptr :

integer(c_int) fortran_function(n, image, callback, c_object) & bind(c, name=''fortran_function'') integer(c_int), value :: n integer(4), intent(inout), dimension(n) :: image type(c_funptr), intent(in) :: callback type(c_ptr), intent(in) :: c_object

subroutine c_callback interfaz abstracta para la subroutine c_callback igual que antes, aunque he experimentado con dejar la parte bind(c) y omitirla. El código dentro de la función principal que llama a la subrutina foo ahora es:

call c_f_procpointer(callback, data%callback_data%callback) data%callback_data%c_object = c_object call foo(data)

... con la subrutina foo misma definida como en el ejemplo anterior.

Lamentablemente, esto falla exactamente de la misma manera que en el ejemplo anterior.

Supongo que hay una sintaxis correcta para lograr lo que estoy tratando de lograr aquí, y estaría muy agradecido por cualquier consejo.