So the module can set defaults and does not need recompiled for the calling program(s) to change the values. The user can subsequently change the values
by name on the command line. Values can be changed only via a call to SINGLE_SET(), which can be called only once. Trying to set the values anywhere in the user program is caught as an error at compile time.
./a.out times=100 width=200
1 2 3.00000000 4.00000000
100 2 200.000000 20.0000000
perhaps not as nice as a new variable properly like “IMMUTABLE”, but not too bad in less than one page of code.
module m_immutable
implicit none
private
public guarded , init , single_set
type :: init
integer :: times = 1, flag = 2
real :: width = 3.0, height = 4.0
end type init
type (init) , save , protected :: guarded
contains
subroutine single_set(pin)
implicit none
type (init) , intent(in) :: pin
logical , save :: firsttime = .true. ! flag so can only call once
namelist /args/ guarded
character(len=256) :: input(3)=[character(len=256) :: '&args','','/']
character(len=256) :: message
integer :: i, ios
if ( firsttime ) then
guarded=pin ! apply values from call
! read arguments from command line
do i=1,command_argument_count()
call get_command_argument(i,input(2))
input(2)='guarded%'//adjustl(input(2))
read(input,nml=args,iostat=ios,iomsg=message)
if(ios.ne.0)then
write(*,*)'ERROR:'//trim(message)
stop 2
endif
enddo
firsttime = .false.
else
write (*,*) 'nevermore'
stop
endif
end subroutine single_set
end module m_immutable
program testit
use m_immutable , only: g=>guarded , init , single_set
implicit none
write (*,*) g%times , g%flag , g%width, g%height
call single_set(init(width=10.0,height=20.0))
write (*,*) g
end program testit
And for fun, add reading a config file called “.config” from the current directory in
NAMELIST format, like:
&args
guarded%width=1000.0
guarded%height=2000.0
/
add reading a config file
module m_immutable
implicit none
private
public guarded , init , single_set
type :: init
integer :: times = 1, flag = 2
real :: width = 3.0, height = 4.0
end type init
type (init) , save , protected :: guarded
contains
subroutine single_set(pin)
implicit none
type (init) , intent(in) :: pin
logical , save :: firsttime = .true. ! flag so can only call once
namelist /args/ guarded
character(len=256) :: input(3)=[character(len=256) :: '&args','','/']
character(len=256) :: message
integer :: i, ios, lun
logical :: exist
if ( firsttime ) then
guarded=pin ! apply values from call
inquire(file='.config',exist=exist)
if(exist)then
open(file='.config',newunit=lun,iostat=ios,iomsg=message)
call checkios()
read(lun,nml=args,iostat=ios,iomsg=message)
call checkios()
endif
! read arguments from command line
do i=1,command_argument_count()
call get_command_argument(i,input(2))
input(2)='guarded%'//adjustl(input(2))
read(input,nml=args,iostat=ios,iomsg=message)
call checkios()
enddo
firsttime = .false.
else
write (*,*) 'nevermore'
stop
endif
contains
subroutine checkios()
if(ios.ne.0)then
write(*,*)'ERROR:',trim(message)
stop
endif
end subroutine checkios
end subroutine single_set
end module m_immutable
program testit
use m_immutable , only: g=>guarded , init , single_set
implicit none
write (*,*) g%times , g%flag , g%width, g%height
call single_set(init(width=10.0,height=20.0))
write (*,*) g
end program testit