2010-02-13 9 views
8

Estoy tratando de escribir un contenedor para la función 'allocate', es decir, la función que recibe un conjunto y las dimensiones, asigna memoria y devuelve el conjunto asignado. Lo más importante es que la función debe funcionar con matrices de diferente rango. Pero tengo que indicar explícitamente rango de matriz en la interfaz de función, y en este caso el código solo se compila si paso las matrices de cierto rango como un parámetro. Por ejemplo, este código no se compila: errorcómo escribir el contenedor para 'asignar'

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 

Compilación:

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) 

¿Hay alguna manera de poner en práctica tales envoltorio ..

Gracias?!

Respuesta

10

Esto se puede hacer a través de un bloque de interfaz genérico. Debe crear procedimientos para cada rango que desee controlar, por ejemplo, memory_1d, memory_2d, ... memory_4d. (Obviamente, se cortó un montón de &). A continuación, escribe un bloque de interfaz genérico que da a todos estos procedimientos la memoria de nombre alternativa como nombre de procedimiento genérico. Cuando llamas a la memoria, el compilador distingue a qué memory_Xd se debe llamar en función del rango del argumento. Lo mismo para tus funciones de freem

Así es como las funciones intrínsecas como el pecado han funcionado durante mucho tiempo: puedes llamar a sin con argumentos reales de varias previsiones, o con un argumento complejo, y el compilador se da cuenta de que la función pecado real es llamar. En FORTRAN realmente antiguo, tenías que usar diferentes nombres para las diferentes funciones de pecado. Ahora que eres un Fortran moderno, puedes configurar lo mismo con tus propias rutinas.

Editar: la adición de un ejemplo de código que demuestra la sintaxis del método &:

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

¡Muchas gracias! A pesar de que requiere la duplicación de código en el módulo de asignación, al menos puedo usar un nombre común cuando llamo a esta función de asignador. Esto es lo que yo quería. – robusta

1

subroutine memory(array, length) tiene como array primero parámetro ficticio 1-dimensional (real(8), allocatable, intent(out), dimension(:) :: array).

llamar a esta subrutina de su programa principal con 3 dimensiones gama foo (real(8), allocatable, dimension(:,:,:) :: foo) es el error obviamente. Y esto es lo que realmente dijo el compilador.

Si realmente necesita, tales subrutinas escribir un par memory/freem subrutinas para cada variedad de diferentes dimensiones - un par de subrutinas gama de 1 dimensión, otro para el arreglo de 2 dimensiones, etc.

Por cierto, memory las subrutinas serán diferentes en general porque para asignar una matriz de n dimensiones necesita pasar n extensiones a la subrutina antes mencionada.

+0

kemiisto, entiendo que este error de compilación es absolutamente evidente. También entiendo que una forma de implementar lo que quiero es escribir asignadores separados para diferentes rangos. Voy a tener que hacer esto como último recurso :) Pero mi pregunta era: ¿existe una forma fortán legítima de escribir un envoltorio para asignar con la MISMA funcionalidad, es decir, el rango de rango universal ... ¡Gracias de todos modos! – robusta

Cuestiones relacionadas