2010-02-13 5 views
8

J'essaie d'écrire un wrapper pour la fonction 'allocate', c'est-à-dire une fonction qui reçoit un tableau et des dimensions, alloue de la mémoire et retourne un tableau alloué. La chose la plus importante est que la fonction doit fonctionner avec des tableaux de rang différent. Mais je dois indiquer explicitement le rang de tableau dans l'interface de fonction, et dans ce cas le code compile seulement si je passe des tableaux de certain rang en tant que paramètre. Par exemple, ce code ne compile pas:comment écrire un wrapper pour 'allouer'

module memory_allocator 
contains 

    subroutine memory(array, length) 
    implicit none 

    real(8), allocatable, intent(out), dimension(:) :: array 
    integer, intent(in) :: length 

    integer :: ierr 

    print *, "memory: before: ", allocated(array) 

    allocate(array(length), stat=ierr) 
    if (ierr /= 0) then 
     print *, "error allocating memory: ierr=", ierr 
    end if 

    print *, "memory: after: ", allocated(array) 

    end subroutine memory 

    subroutine freem(array) 
    implicit none 

    real(8), allocatable, dimension(:) :: array 

    print *, "freem: before: ", allocated(array) 
    deallocate(array) 
    print *, "freem: after: ", allocated(array) 

    end subroutine freem 

end module memory_allocator 

program alloc 
    use memory_allocator 
    implicit none 

    integer, parameter :: n = 3 
    real(8), allocatable, dimension(:,:,:) :: foo 
    integer :: i, j, k 

    print *, "main: before memory: ", allocated(foo) 
    call memory(foo, n*n*n) 
    print *, "main: after memory: ", allocated(foo) 

    do i = 1,n 
    do j = 1,n 
     do k = 1, n 
     foo(i, j, k) = real(i*j*k) 
     end do 
    end do 
    end do 

    print *, foo 

    print *, "main: before freem: ", allocated(foo) 
    call freem(foo) 
    print *, "main: after freem: ", allocated(foo) 

end program alloc 

erreur de compilation:

gfortran -o alloc alloc.f90 -std=f2003 
alloc.f90:46.14: 

    call memory(foo, n*n*n) 
       1 
Error: Rank mismatch in argument 'array' at (1) (1 and 3) 
alloc.f90:60.13: 

    call freem(foo) 
      1 
Error: Rank mismatch in argument 'array' at (1) (1 and 3) 

Est-il possible de mettre en œuvre cette enveloppe ..

Merci à!

Répondre

10

Ceci peut être fait via un bloc d'interface générique. Vous devez créer des procédures pour chaque rang que vous souhaitez gérer, par exemple, memory_1d, memory_2d, ... memory_4d. (Évidemment beaucoup de couper & coller.) Ensuite, vous écrivez un bloc d'interface générique qui donne à toutes ces procédures la mémoire de nom alternative comme un nom de procédure générique. Lorsque vous appelez la mémoire, le compilateur distingue quel memory_Xd doit être appelé en fonction du rang de l'argument. La même chose pour vos fonctions freem. C'est ainsi que les fonctions intrinsèques telles que le péché ont longtemps fonctionné - vous pouvez appeler le péché avec de vrais arguments de différentes prévisions, ou avec un argument complexe, et le compilateur détermine avec la fonction sin actuelle à appeler. Dans l'ancien FORTRAN, vous deviez utiliser des noms différents pour les différentes fonctions du péché. Maintenant Fortran moderne, vous pouvez configurer la même chose avec vos propres routines.

Edit: ajout d'un exemple de code montrant la méthode & syntaxe:

module double_array_mod 

    implicit none 

    interface double_array 
     module procedure double_vector 
     module procedure double_array_2D 
    end interface double_array 

    private ! hides items not listed on public statement 
    public :: double_array 

contains 

    subroutine double_vector (vector) 
     integer, dimension (:), intent (inout) :: vector 
     vector = 2 * vector 
    end subroutine double_vector 

    subroutine double_array_2D (array) 
     integer, dimension (:,:), intent (inout) :: array 
     array = 2 * array 
    end subroutine double_array_2D 

end module double_array_mod 


program demo_user_generic 

    use double_array_mod 

    implicit none 

    integer, dimension (2) :: A = [1, 2] 
    integer, dimension (2,2) :: B = reshape ([11, 12, 13, 14], [2,2]) 
    integer :: i 

    write (*, '(/ "vector before:",/2(2X, I3))') A 
    call double_array (A) 
    write (*, '(/ "vector after:",/2(2X, I3))') A 

    write (*, '(/ "2D array before:")') 
    do i=1, 2 
     write (*, '(2(2X, I3))') B (i, :) 
    end do 
    call double_array (B) 
    write (*, '(/ "2D array after:")') 
    do i=1, 2 
     write (*, '(2(2X, I3))') B (i, :) 
    end do 

    stop 
end program demo_user_generic 
+0

Merci beaucoup! Bien qu'il nécessite la duplication de code dans le module allocateur, au moins je peux utiliser le nom commun quand j'appelle cette fonction d'allocateur. C'est ce que je voulais. – robusta

1

subroutine memory(array, length) a comme premier paramètre fictif matrice unidimensionnelle (real(8), allocatable, intent(out), dimension(:) :: array).

Appel de ce sous-programme à partir de votre programme principal avec Le tableau à trois dimensions toto (real(8), allocatable, dimension(:,:,:) :: foo) est évidemment une erreur. Et c'est ce que le compilateur a réellement dit.

Si vous avez vraiment besoin d'écrire une paire sous-routines memory/freem pour chaque sous-routines tableau de différentes dimensions - une paire pour le tableau des sous-routines 1 dimensions, une autre pour le tableau 2 dimensions, etc.

Par ailleurs, memory Les sous-programmes seront différents en général car pour allouer un tableau à n dimensions, vous devez passer n extents au sous-programme mentionné ci-dessus.

+0

kemiisto, je comprends que cette erreur de compilation est tout à fait évident. Je comprends aussi que l'une des façons de mettre en œuvre ce que je veux est d'écrire des allocateurs distincts pour différents grades. Je vais devoir faire ceci en dernier recours :) Mais ma question était - y at-il une manière fortran légitime d'écrire un wrapper pour allouer avec la même fonctionnalité, c'est-à-dire le rang universel ... Merci quand même! – robusta