Módulos e Programas#
Módulos é o modo preferido para criar bibliotecas e aplicações Fortran modernas. Como uma convenção, um arquivo de fonte única deve sempre conter apenas um módulo, enquanto que o nome do módulo deve sempre corresponder ao caminho de arquivo para facilitar a navegação em projetos grandes. É também recomendado prefixar o nome dos módulos com o nome da biblioteca para evitar conflite de nomes quando usando como dependências em outros projetos.
Um exemplo para tal arquivo de módulo é dado aqui
!> 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
H[a algumas coisas nesse exemplo a destacar. Primeiro, cada módulo começa com comentários documentando o propósito e conteúdo do módulo. Semelhante, cada procedimento começa com um breve comentário descrevendo seu propósito e a intenção dos argumentos fictícios. Documentação é uma das partes mais importantes para a criação de software de longa duração, independente da linguagem.
Segundo, imports (use) e exports (public) são passados explicitamente, isso permite uma visão da fonte do módulo para checar os procedimentos usados e disponíveis, tipos constantes e derivadas. Os imports são normalmente limitados ao escopo do módulo ao invés de reimportado em cada escopo de procedimento ou interface. Semelhante, exports são feitos explicitamente adicionando a declaração private em uma única linha e explicitamente listando todos os símbolos exportados na declaração public.
Finalmente, a declaração implicit none
funciona para todo o módulo e não há necessidade de repetir dentro de cada procedimento.
Variáveis dentro de módulos são estáticos (implicitly saved). É altamente recomendado limitar o uso de variáveis de módulos para expressões constantes, como unicamente parâmetros ou enumeráveis, ou exportá-los como protected ao invés de public.
Submódulos podem ser usar para quebrar longas cadeias de dependência e encurtar a cascata de recompilação em programas Fortran. Eles também oferecem a possibilidade de fornecer implementações especializadas e otimizadas sem necessidade de uso de pré-processador.
Um exemplo da Fortran standard library é o módulo quadrature, que define somente interfaces para os procedimentos de módulo, mas sem implementações
!> 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
Enquanto que a implementação é fornecida em submódulos separados como esse para a regra de integração trapezoidal.
!> 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
Note que os procedimentos de módulos não tem que ser implementados no mesmo submódulo. Vários submódulos podem ser usados para reduzir a carga de compilação para módulos grandes.
Finalmente, quando configurando um programa, é recomendado que mantenha as reais implementações no corpo programa ao mínimo. Reusar implementações de módulos permite que você escreva código reutilizável e foque a unidade do programa em transmitir o input do usuário para os respectivos objetos e funções da biblioteca.