Aquí hay un uso típico de los punteros de función en C. Me gustaría hacer algo similar en Fortran. Tengo algunas ideas, pero me gustaría saber si hay alguna forma canónica de hacerlo.
Los punteros de función y los contextos pasados por el usuario se almacenan y luego se llaman más tarde.
typedef PetscErrorCode (*TSIFunction)(TS,PetscReal,Vec,Vec,Vec,void*);
PetscErrorCode TSSetIFunction(TS ts,Vec res,TSIFunction f,void *ctx);
La función del usuario se vuelve a llamar utilizando su contexto en varios momentos posteriores.
En PETSc, también hacen un uso intensivo de cadenas -> tablas de puntero de función. Todo es un complemento, por lo que el usuario puede registrar sus propias implementaciones y son de primera clase.
#define PCGAMG "gamg"
PCRegisterDynamic(PCGAMG ,path,"PCCreate_GAMG",PCCreate_GAMG);
Esto registra la rutina de creación en un "FList", luego PCSetFromOptions () ofrece la posibilidad de elegir este método frente a cualquiera de las otras opciones. Si el sistema admite carga dinámica, puede omitir la dependencia en tiempo de compilación del símbolo PCCreate_GAMG y simplemente pasar NULL, entonces el símbolo se buscará en la biblioteca compartida en tiempo de ejecución.
Tenga en cuenta que este paso más allá de una "fábrica", es una inversión del dispositivo de control similar a lo que Martin Fowler llama "localizador de servicio".
Nota: esto surgió en mi correspondencia privada con Jed Brown, donde me hizo esta pregunta. Decidí externalizarlo y ver qué respuestas pueden dar las personas.
Supongo que hay mucho de lo que es lenguaje específico de PETSc en su pregunta (con lo que no estoy familiarizado), por lo que puede haber una arruga aquí que no entiendo del todo, pero tal vez esto aún sea útil para ayudarlo empezado.
Básicamente, debe definir la interfaz para el procedimiento, y luego puede pasar un puntero a una función que sigue a esta interfaz. El siguiente código muestra un ejemplo. Primero, hay un módulo que define la interfaz y muestra un ejemplo rápido de un fragmento de código que ejecutaría la rutina proporcionada por el usuario que sigue esa interfaz. El siguiente es un programa que muestra cómo el usuario usaría este módulo y definiría la función a ejecutar.
fuente
PROCEDURE(function_template), POINTER :: func
internamente.void*
, el usuario termina teniendo que escribir interfaces para las funciones de la biblioteca. Si implementa la biblioteca en C, esto es suficiente, pero si implementa en Fortran, debe asegurarse de que el compilador nunca vea la INTERFAZ "ficticia" de la biblioteca al mismo tiempo que la INTERFAZ del usuario.void*
en Fortran es untransfer
método. Vea aquí un ejemplo de uso. Los otros 3 enfoques además deltransfer
método son "matrices de trabajo", "tipo derivado específico en lugar devoid *
" y usan variables de módulo locales para el módulo.transfer
un tipo sin sentido (character (len=1), allocatable
) solo para llamar a la función.