Voici une utilisation typique des pointeurs de fonction en C. Je voudrais faire quelque chose de similaire dans Fortran. J'ai quelques idées, mais j'aimerais savoir s'il existe un moyen canonique de le faire.
Les pointeurs de fonction et les contextes transmis par l'utilisateur sont stockés, puis appelés ultérieurement.
typedef PetscErrorCode (*TSIFunction)(TS,PetscReal,Vec,Vec,Vec,void*);
PetscErrorCode TSSetIFunction(TS ts,Vec res,TSIFunction f,void *ctx);
La fonction de l'utilisateur est rappelée à l'aide de son contexte à différents moments ultérieurs.
Dans PETSc, ils font également un usage intensif des tables de pointeurs chaîne -> fonction. Tout est un plugin, donc l'utilisateur peut enregistrer ses propres implémentations et ils sont de première classe.
#define PCGAMG "gamg"
PCRegisterDynamic(PCGAMG ,path,"PCCreate_GAMG",PCCreate_GAMG);
Cela enregistre la routine de création dans un "FList", puis PCSetFromOptions () offre la possibilité de choisir cette méthode par rapport à tout autre choix. Si le système prend en charge le chargement dynamique, vous pouvez ignorer la dépendance au moment de la compilation sur le symbole PCCreate_GAMG et simplement passer NULL, puis le symbole sera recherché dans la bibliothèque partagée au moment de l'exécution.
Notez que cette étape au-delà d'une "usine", c'est une inversion du dispositif de contrôle similaire à ce que Martin Fowler appelle "localisateur de service".
Remarque: cela est venu dans ma correspondance privée avec Jed Brown, où il m'a posé cette question. J'ai décidé de l'externaliser et de voir quelles réponses les gens peuvent trouver.
Il y a beaucoup de ce que je suppose être un langage spécifique à PETSc dans votre question (avec laquelle je ne suis pas familier), donc il pourrait y avoir une ride ici, je ne comprends pas très bien, mais peut-être que cela sera toujours utile pour vous commencé.
Fondamentalement, vous devez définir l'interface de la procédure, puis vous pouvez passer un pointeur vers une fonction qui suit cette interface. Le code suivant montre un exemple. Tout d'abord, il existe un module qui définit l'interface et montre un exemple rapide d'un morceau de code qui exécuterait la routine fournie par l'utilisateur qui suit cette interface. Vient ensuite un programme qui montre comment l'utilisateur utiliserait ce module et définirait la fonction à exécuter.
la source
PROCEDURE(function_template), POINTER :: func
interne.void*
, l'utilisateur finit par devoir écrire lui-même des interfaces pour les fonctions de bibliothèque. Si vous implémentez la bibliothèque en C, cela suffit, mais si vous implémentez dans Fortran, vous devez vous assurer que le compilateur ne voit jamais l'INTERFACE "factice" de la bibliothèque en même temps que l'INTERFACE de l'utilisateur.void*
Fortran est unetransfer
méthode. Voir ici pour un exemple d'utilisation. Les 3 autres approches en plus de latransfer
méthode sont des "tableaux de travail", "type dérivé spécifique au lieu devoid *
" et utilisent des variables de module locales au module.transfer
d'un type absurde (character (len=1), allocatable
) juste pour appeler la fonction.