Модули и программы#
Использование модулей является предпочтительным способом создания современных библиотек и приложений на языке Fortran. По соглашению, один файл исходного кода всегда должен содержать только один модуль, а имя модуля должно соответствовать относительному пути к файлу, чтобы обеспечить удобную навигацию в больших проектах. Также рекомендуется добавлять к имени модуля префикс в виде имени библиотеки, чтобы избежать конфликта имён при использовании библиотеки в качестве зависимости в других проектах.
Пример такого файла модуля приведён здесь
!> Interface to TOML processing library.
!>
!> ...
module fpm_toml
use fpm_error, only : error_t, fatal_error, file_not_found_error
use fpm_strings, only : string_t
use tomlf, only : toml_table, toml_array, toml_key, toml_stat, get_value, &
& set_value, toml_parse, toml_error, new_table, add_table, add_array, &
& toml_serializer, len
implicit none
private
public :: read_package_file
public :: toml_table, toml_array, toml_key, toml_stat, get_value, set_value
public :: new_table, add_table, add_array, len
public :: toml_error, toml_serializer, toml_parse
contains
!> Process the configuration file to a TOML data structure
subroutine read_package_file(table, manifest, error)
!> TOML data structure
type(toml_table), allocatable, intent(out) :: table
!> Name of the package configuration file
character(len=*), intent(in) :: manifest
!> Error status of the operation
type(error_t), allocatable, intent(out) :: error
! ...
end subroutine read_package_file
end module fpm_toml
В этом примере модуля можно выделить несколько моментов. Во-первых, каждый модуль начинается с комментариев, документирующих цель и содержание модуля. Аналогично, каждая процедура начинается с комментария, кратко описывающего ее назначение и смысл фиктивных аргументов. Документирование – одна из самых важных частей создания долговечного программного обеспечения, независимо от языка.
Во-вторых, импорты (use) и экспорты (public) указаны в явном виде, что позволяет с первого взгляда на исходный текст модуля проверить используемые и доступные процедуры, константы и производные типы. Импорт обычно ограничивается областью применения модуля, а не реимпортируется в каждую процедуру или область применения интерфейса. Аналогично, экспорт осуществляется в явном виде путем добавления оператора private в отдельной строке и явного перечисления всех экспортируемых символов в операторах public.
Наконец, оператор implicit none
работает для всего модуля, и нет необходимости дублировать его в каждой процедуре.
Переменные внутри модуля являются статическими (неявно сохранёнными, implicitly saved). Настоятельно рекомендуется ограничить использование переменных модуля только константными выражениями, такими как параметры или перечисления, или экспортировать их как protected, а не как public.
Подмодули (Submodules) можно использовать для разрыва длинных цепочек зависимостей и сокращения каскадов перекомпиляции в программах Fortran. Они также дают возможность предоставлять специализированные и оптимизированные реализации, не требуя использования препроцессора.
Примером из Fortran standard library является модуль квадратур, который определяет только интерфейсы для процедур модуля, но не их реализацию
!> Numerical integration
!>
!> ...
module stdlib_quadrature
use stdlib_kinds, only: sp, dp, qp
implicit none
private
public :: trapz
! ...
!> Integrates sampled values using trapezoidal rule
interface trapz
pure module function trapz_dx_dp(y, dx) result(integral)
real(dp), intent(in) :: y(:)
real(dp), intent(in) :: dx
real(dp) :: integral
end function trapz_dx_dp
module function trapz_x_dp(y, x) result(integral)
real(dp), intent(in) :: y(:)
real(dp), intent(in) :: x(:)
real(dp) :: integral
end function trapz_x_dp
end interface trapz
! ...
end module stdlib_quadrature
В то время как реализация предоставляется в отдельных подмодулях, как, например, для описания интегрирования методом трапеций, приведённого здесь.
!> Actual implementation of the trapezoidal integration rule
!>
!> ...
submodule (stdlib_quadrature) stdlib_quadrature_trapz
use stdlib_error, only: check
implicit none
contains
pure module function trapz_dx_dp(y, dx) result(integral)
real(dp), intent(in) :: y(:)
real(dp), intent(in) :: dx
real(dp) :: integral
integer :: n
n = size(y)
select case (n)
case (0:1)
integral = 0.0_dp
case (2)
integral = 0.5_dp*dx*(y(1) + y(2))
case default
integral = dx*(sum(y(2:n-1)) + 0.5_dp*(y(1) + y(n)))
end select
end function trapz_dx_dp
! ...
end submodule stdlib_quadrature_trapz
Обратите внимание, что процедуры модуля не обязательно должны быть реализованы в одном подмодуле. Несколько подмодулей могут быть использованы для снижения нагрузки компиляции для больших модулей.
Наконец, при создании программы рекомендуется свести к минимуму количество фактических реализаций в теле программы. Повторное использование реализаций из модулей позволяет написать многократно используемый код и сосредоточиться в программном блоке на передаче пользовательского ввода соответствующим библиотечным функциям и объектам.