Вложенная структура данных пользовательского типа в Фортране - PullRequest
0 голосов
/ 11 июля 2019

Я ищу способ построения древовидной структуры с использованием пользовательского типа в Fortran 2008. Хотя я могу заставить работать некоторый базовый код, я сталкиваюсь с утечками памяти, которые я не могу точно определить.

Древовидная структура не должна быть слишком общей, поскольку она используется как одноразовая вставка и хранилище с множественным чтением, поэтому я решил использовать выделяемые.Поскольку Fortran не позволяет использовать тип для выделяемого в качестве одного из его собственных членов, я использую промежуточную структуру, на которую ссылается указатель, для хранения этого выделяемого.Итак, следующее, что я хотел бы использовать, но не разрешено:

type :: invalid_section
  type(invalid_section), dimension(:), allocatable :: subsections
end type

В следующем примере я использую ленивое выделение (выделяйте его только при наличии дочерних узлов для добавления / представления) этого указателя на тип, содержащий выделение.

module sectiontest

   type :: section
      type(subsections), pointer :: subsections_ => null()

      contains
         procedure, pass(self) :: section_assign
         generic :: assignment(=) => section_assign

         final :: section_cleanup, section_cleanup_arr
   end type

   type :: subsections
      type(section), dimension(:), allocatable :: arr
   end type

   interface section
      module procedure constructor
   end interface

contains

   type(section) function constructor(subsections)
      type(section), optional, intent(in) :: subsections(:)
      integer :: idx

      print *, "constructor"

      if (present(subsections)) then
         print *, "allocating subsection"
         allocate(constructor%subsections_)
         allocate(constructor%subsections_%arr(size(subsections)))
         do idx=1,size(subsections)
            ! make sure we recursively copy everything
            constructor%subsections_%arr(idx) = subsections(idx)
         enddo
      endif
   end function

   recursive subroutine section_assign(self, rhs)
      class(section), intent(inout) :: self
      type(section), intent(in) :: rhs
      integer :: idx

      print *, "assign"
      if (associated(self%subsections_)) then
         deallocate(self%subsections_)
      endif

      if (associated(rhs%subsections_)) then
         print *, "allocation subsection"
         allocate(self%subsections_)
         allocate(self%subsections_%arr(size(rhs%subsections_%arr)))

         do idx=1,size(rhs%subsections_%arr)
            self%subsections_%arr(idx) = rhs%subsections_%arr(idx)
         enddo
      endif
   end subroutine

   recursive subroutine section_cleanup(sec)
      type(section), intent(inout) :: sec

      print *, "section_cleanup"
      if (associated(sec%subsections_)) then
         print *, "  deallocated a subsection"
         deallocate(sec%subsections_)
      endif
   end subroutine

   recursive subroutine section_cleanup_arr(arr)
      type(section), dimension(:), intent(inout) :: arr
      integer :: idx

      print *, "deallocating array of sections of size:", size(arr)

      do idx=1,size(arr)
         print *, "deallocating subsection array index", idx
         if (associated(arr(idx)%subsections_)) then
            print *, "  deallocated a subsection"
            deallocate(arr(idx)%subsections_)
         endif
      end do
   end subroutine

   subroutine demo()
      type(section) :: root

      root = section(subsections=[ &
         section(subsections=[section(), section(), section()]), &
         section() &
         ])
   end subroutine
end module sectiontest
program main
   use sectiontest
   implicit none

   call demo()
end program

Из gfortran (7 и 9), flang и nagfor Я получаю прямые утечки памяти, происходящие из allocate(constructor%subsections_)в constructor.

Здесь от gfortran-7 и построено с -fsanitize=address:

==26536==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 48 byte(s) in 1 object(s) allocated from:
    #0 0x7f965539c510 in malloc (/usr/lib64/libasan.so.4+0xdc510)
    #1 0x407e35 in __sectiontest_MOD_constructor /users/tiziano/work/tests/fortran/cp2k_input_parser/recursive_mwe.f90:31
    #2 0x40432a in __sectiontest_MOD_demo /users/tiziano/work/tests/fortran/cp2k_input_parser/recursive_mwe.f90:92
    #3 0x4090d9 in MAIN__ /users/tiziano/work/tests/fortran/cp2k_input_parser/recursive_mwe_prog.f90:5
    #4 0x409119 in main /users/tiziano/work/tests/fortran/cp2k_input_parser/recursive_mwe_prog.f90:2
    #5 0x7f96543c2f89 in __libc_start_main (/lib64/libc.so.6+0x20f89)

Я ищу любую альтернативную реализацию (но желательно подобную элегантную инициализацию)или объяснение и возможное решение для утечки памяти.

1 Ответ

3 голосов
/ 13 июля 2019

Fortran 2008 поддерживает тип, содержащий выделяемый компонент определяемого типа. Это упрощает код до:

module sectiontest
   type :: section
      type(section), allocatable :: subsections(:)
   end type
contains
   subroutine demo()
      type(section) :: root

      root = section( subsections=[ &
         section(subsections=[section(), section(), section()]), &
         section() ])
   end subroutine
end module sectiontest

program main
   use sectiontest
   implicit none

   call demo()
end program

Последние версии gfortran поддерживают эту языковую функцию.

Для компиляторов, которые недостаточно поддерживают Fortran 2008, приведенный код представляет собой разумный обходной путь и будет работать на компиляторах, которые правильно реализуют Fortran 2003.

Однако gfortran (по крайней мере, до 9.1.1) неправильно выполняет финализацию результатов функции - отсюда и наблюдаемая утечка памяти.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...