web-dev-qa-db-fra.com

Utilisation correcte des modules, sous-programmes et fonctions dans Fortran

J'ai récemment découvert les blocs d'interface lors de l'ajout d'une fonction à mon programme Fortran. Tout fonctionne bien et proprement, mais maintenant je veux ajouter une deuxième fonction dans le bloc d'interface.

Voici mon bloc d'interface:

interface
    function correctNeighLabel (A,i,j,k)
    integer :: correctNeighLabel
    integer, intent(in) :: i,j,k
    integer,dimension(:,:,:),intent(inout) :: A
    end function

    function correctNeighArray (B,d,e,f)
        character :: correctNeighArray
    integer, intent(in) :: d,e,f
    character, dimension(:,:,:),intent(inout) :: B
    end function
end interface

Il me semble que ce n'est peut-être pas la meilleure option.

J'ai étudié des sous-programmes, mais je ne suis pas très sûr que ce soit la bonne solution. Ce que je fais est relativement simple, et je dois passer des arguments au sous-programme, mais tous les sous-programmes que j'ai vus sont a) compliqués (c'est-à-dire trop compliqués pour une fonction), et b) ne prennent pas d'arguments, ils se comportent comme s'ils manipulaient des variables sans qu'elles ne leur soient transmises.

Je n'ai pas vraiment examiné les modules correctement, mais d'après ce que j'ai vu, ce n'est pas la bonne chose à utiliser.

Laquelle dois-je utiliser quand et comment dois-je m'y prendre le mieux?

28
Pureferret

Les modules sont toujours la bonne chose à utiliser ;-)

Si vous avez un programme F90 très simple, vous pouvez inclure des fonctions et des sous-programmes dans le bloc "contient":

 program simple
   implicit none
   integer :: x, y
   x = ...
   y = myfunc(x)
 contains
   function myfunc(x) result(y)
     implicit none
     integer, intent(in)  :: x
     integer              :: y
     ...
   end function myfunc
 end program

L'interface des fonctions/sous-programmes sera alors connue dans le programme et n'a pas besoin d'être définie dans un bloc d'interface.

Pour les programmes plus complexes, vous devez conserver toutes les fonctions/sous-programmes dans les modules et les charger si nécessaire. Vous n'avez donc pas besoin de définir d'interfaces:

 module mymod
   implicit none
   private
   public :: myfunc
 contains
   function myfunc(x) result(y)
     implicit none
     integer, intent(in)  :: x
     integer              :: y
     ...
   end function myfunc
 end module mymod

 program advanced
   use mymod, only: myfunc
   implicit none
   integer :: x, y
   x = ...
   y = myfunc(x)
 end program advanced

Le module et le programme peuvent (devraient en fait) être dans des fichiers séparés, mais le module doit être compilé avant le programme réel.

40
alexurba

Appuyer et étendre ce qui a déjà été dit. Il est préférable de placer vos procédures (sous-programmes et fonctions) dans des modules et de les "utiliser" car vous obtenez un contrôle automatique de cohérence des interfaces avec peu d'effort. Les autres moyens présentent des inconvénients. Si vous définissez l'interface avec un bloc d'interface, vous avez trois choses à gérer au lieu de deux: l'interface, la procédure elle-même et l'appel. Si vous apportez une modification, les trois doivent être modifiés pour être cohérents. Si vous utilisez un module, seuls deux doivent être modifiés. Une raison d'utiliser un bloc d'interface est si vous n'avez pas accès au code source (par exemple, une bibliothèque précompilée) ou si le code source est dans une autre langue (par exemple, vous utilisez du code C via la liaison ISO C).

L'inconvénient de l'approche "contient" est que les procédures contenues héritent de toutes les variables locales du programme parent ... ce qui n'est pas très modulaire et peut être très déroutant si vous oubliez cette "fonctionnalité".

17
M. S. B.

les réponses d'Alexurba et de MSB sont correctes et utiles comme d'habitude; permettez-moi de développer un peu plus de détails sur un point - si les modules sont le chemin à parcourir (et ils le sont), à quoi servent les interfaces?

Pour les fonctions et sous-programmes dans les modules, tout ce qui uses ce module peut voir automatiquement ces interfaces; les interfaces sont générées lors de la compilation du module (ces informations, entre autres, vont dans le fichier .mod généré lors de la compilation d'un module). Vous n'avez donc pas besoin de l'écrire vous-même. De même, lorsque vous utilisez un sous-programme CONTAINed (qui, en accord avec MSB, je trouve plus déroutant qu'utile - ils sont beaucoup mieux considérés comme fermetures ou sous-programmes imbriqués) que les sous-programmes externes), le programme principal peut déjà 'voir' explicitement l'interface et il n'a pas besoin que vous l'écriviez pour cela.

Les blocs d'interface sont destinés lorsque vous ne pouvez pas le faire - lorsque le compilateur n'est pas en mesure de générer l'interface explicite pour vous, ou lorsque vous voulez quelque chose différent de ce qui est donné. Un exemple est lorsque vous utilisez interopérabilité C-Fortran dans Fortran 2003. Dans ce cas, le code Fortran est lié à une bibliothèque C (par exemple) et n'a aucun moyen de générer une interface fortran correcte pour la routine C pour vous - vous devez le faire vous-même, en écrivant votre propre bloc d'interface.

Un autre exemple est lorsque vous connaissez déjà les interfaces des sous-programmes, mais lorsque vous souhaitez créer une nouvelle interface pour "masquer" les sous-programmes derrière - par exemple, lorsque vous avez une routine qui fonctionne sur (disons) des entiers, et une sur des réels , et vous voulez pouvoir appeler le même nom de routine sur l'un et laisser le compilateur le trier en fonction des arguments. De telles constructions sont appelées routines génériques et existent depuis Fortran 90. Dans ce cas, vous créez explicitement une interface pour cette nouvelle routine générique et listez les interfaces des routines "réelles" dans ce bloc d'interface .

16
Jonathan Dursi