Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Using type variables instead of enums to write cleaner code in Fortran

216 views
Skip to first unread message

Arash Agan

unread,
Dec 8, 2021, 9:23:27 AM12/8/21
to
I have been trying to write clearer fortran code by using enums but it seems using a type variable instead would be better in terms of code readability.
you can find the code here: https://github.com/AAGAN/fortran_type_clean_code/blob/main/rang.f95

Program Rang

use iso_fortran_env, only : int8, int16, int32, int64

implicit none

! mix and match integer types for demonstration
integer(int16) :: red
integer(int32) :: blue
integer(int64) :: col

! create a type with some integer values
!(probably it's better to put this in a module)
type colortype
integer(int8) :: red = 1, blue = 2, veryDarkGreen = 3
end type colortype
! instantiate a variable of type colortype to use in the rest of the program
type(colortype) :: color

! output: 1 2 3
print *, color%red, color%blue, color%veryDarkGreen

red = color%red
if (red .eq. color%red) then
print *, "it is red"
end if

blue = 2
if (blue .eq. color%blue) then
print *, "it is blue"
end if

col = color%veryDarkGreen
if (col .eq. color%blue) then
print *, "it is not blue"
else if (col .eq. color%veryDarkGreen) then
print *, "it is very dark green"
else
print *, "it is red"
end if

End Program Rang

Gary Scott

unread,
Dec 8, 2021, 12:47:39 PM12/8/21
to
True, enums seldom if ever improve code readability or logic.

FortranFan

unread,
Dec 8, 2021, 4:34:09 PM12/8/21
to
On Wednesday, December 8, 2021 at 9:23:27 AM UTC-5, arash...@gmail.com wrote:

> I have been trying to write clearer fortran code by using enums but it seems using a type variable instead would be better in terms of code readability.
> you can find the code here: https://github.com/AAGAN/fortran_type_clean_code/blob/main/rang.f95 ..

Did you mean named constants instead of "enums"?

Or to ask differently, where is your Fortran "code by using enums"?

FortranFan

unread,
Dec 8, 2021, 4:41:59 PM12/8/21
to
On Wednesday, December 8, 2021 at 12:47:39 PM UTC-5, Gary Scott wrote:

> ..
> True, enums seldom if ever improve code readability or logic.

Well, Fortran does *not* have any meaningful facility with ENUMs. So *no* such assertion can be made in the context of Fortran. Fortran includes some rudimentary support toward ENUMs that was introduced starting Fortran 2003 along with interoperability with C but it provides no type safety in actual usage and it doesn't really add much that named constants (PARAMETER attribute) does not provide already.

With other languages though that do include well-feature ENUMs feature, ENUMs do indeed help greatly while coding and readability and maintenance and also with usage such as with libraries.

Gary Scott

unread,
Dec 9, 2021, 1:03:07 PM12/9/21
to
I did not say that Fortran DOES have enum. My statement was generic.

John

unread,
Dec 10, 2021, 1:10:35 PM12/10/21
to
I am not a user of enums, but someone told me they needed an enum, and that the members needed to be constant integers so they could
be used in a select; and after he described what he wanted I said he could do the following and he said it was what he needed. Not sure if this helps or not
> program demo_enum
>
> ! pseudo-enum
> type enum_colors
> integer :: RED
> integer :: BLUE
> integer :: GREEN
> end type enum_colors
> type(enum_colors),parameter :: colors=enum_colors(1,2,3)
>
> integer :: col
>
> col=colors%red
>
> select case(col)
> case(colors%red)
> write(*,*)'red'
> case(colors%green)
> write(*,*)'green'
> case(colors%blue)
> write(*,*)'blue'
> end select
>
> end program demo_enum

unfortunately values declared in a user-defined type cannot have the PARAMETER attribute, which might make it a bit more elegant.
It worked with two compilers so I cannot say I know for certain that is standard-conforming but cannot think of a reason why it would not be as long as the values are constant; but at the time I wrote it I was not sure that a user defined type would work in the SELECT even if it was a constant, but it did and he says he has used it subsequently with other compilers and not had a problem.

John

unread,
Dec 10, 2021, 1:18:41 PM12/10/21
to
(test1) urbanjs@venus:~$ vi /tmp/tmp.f90
(test1) urbanjs@venus:~$ nvfortran /tmp/tmp.f90
(test1) urbanjs@venus:~$ ./a.out
red
(test1) urbanjs@venus:~$ ifort /tmp/tmp.f90
(test1) urbanjs@venus:~$ ./a.out
red
(test1) urbanjs@venus:~$ gfortran /tmp/tmp.f90
(test1) urbanjs@venus:~$ ./a.out
red


Worked with at least these three

Arash Agan

unread,
Dec 10, 2021, 2:05:16 PM12/10/21
to
Thanks John for sharing your pseudo-enum code.
Creating the type as a parameter as you suggested makes it even a better "enum".
I updated my original code on github using your improvement.

https://github.com/AAGAN/fortran_type_clean_code/blob/main/rang.f95

Best

John

unread,
Dec 10, 2021, 7:54:56 PM12/10/21
to

Well, I have a love-hate relationship with another similiar use, because
I showed someone how to do this and now I have to use it; but consider that
with Fortran you are not limited to constant integers, so you can create
a "Universal Constant" (uc) that has attributes like bit size, units,
and even a description which is actually not that bad to use (I will
admit), but the code for the module is a sight to behold. It started
with this example that I made, where you want a module of constants ...

But first, before you see the module, you can see it is actually easy
to use. If I want a "universal constant" in Double Precision for the
Golden Ratio I just use "uc%dp%golden_precision". That really is not that
bad. An example program that also shows how ASSOCIATE is often used with
the module:

>program test_universal_constants
>use M_constants, only : uc
>write(*,*)uc%qp%gamma ! universal constant, quad-precision, gamma
>associate (gamma => uc%dp%gamma)
> write(*,*)gamma
>end associate
>end program test_universal_constants

But even for this "simple" initial example the "hidden" code gets a
little crazy. Add units and descriptors (as was done) and the code gets
interesting to maintain.

>module M_constants
>use, intrinsic :: iso_fortran_env, only : r4=>real32, r8=>real64, r16=>real128
>implicit none
>private
>
>real(r16),parameter :: &
> pi = 3.141592653589793238462643383279502884197169399375105820974944592307_r16, &
> gamma = 0.577215664901532860606512090082402431042_r16, &
> e = 2.71828182845904523536028747135266249775724709369995_r16, &
> Golden_Ratio = 1.6180339887498948482045868_r16, &
> euler = 0.577215664901532860606512090082402431042_r16, &
> Deg_Per_Rad = 57.2957795130823208767981548_r16, &
> Rad_Per_Deg = 0.01745329251994329576923691_r16
>
>type r128; real(r16) :: pi,gamma,e,golden_ratio,euler; endtype r128
>type r64; real(r8) :: pi,gamma,e,golden_ratio,euler; endtype r64
>type r32; real(r4) :: pi,gamma,e,golden_ratio,euler; endtype r32
>
>type rall
> type(r128) :: qp
> type(r64) :: dp
> type(r32) :: sp
>end type rall
>
>type(rall),parameter,public :: uc=rall( &
>& r128(pi=pi,gamma=gamma,e=e,golden_ratio=golden_ratio,euler=euler), &
>& r64(pi=real(pi,r8),gamma=real(gamma,r8),e=real(e,r8),golden_ratio=real(golden_ratio,r8),euler=real(euler,r8) ), &
>& r32(pi=real(pi,r4),gamma=real(gamma,r4),e=real(e,r4),golden_ratio=real(golden_ratio,r4),euler=real(euler,r4) ) &
>& )
>end module M_constants

John

unread,
Dec 10, 2021, 8:25:19 PM12/10/21
to
I forgot to use the most common usage

> real,parameter :: e=uc%sp%e

John

unread,
Dec 10, 2021, 9:16:46 PM12/10/21
to
actually, this syntax might be nicer; I guess when I first did the first example and found I could not add the PARAMETER attribute in the TYPE definition I did not try it with just the values specified:


program demo_enum
type enum_colors
integer :: RED=1
integer :: BLUE=2
integer :: GREEN=3
integer :: MAGENTA=4
integer :: CYAN=5
end type enum_colors
type(enum_colors),parameter :: colors=enum_colors()
write(*,*)colors


end program demo_enum

Ron Shepard

unread,
Dec 12, 2021, 12:35:00 PM12/12/21
to
On 12/10/21 12:10 PM, John wrote:
> unfortunately values declared in a user-defined type cannot have the PARAMETER attribute, which might make it a bit more elegant.

Yes, this capability would have been useful since user defined types
were introduced in f90. Now that defined types can have stuff below the
CONTAINS, I think that would be a good place to allow parameters. That
is, everything below the CONTAINS would be stuff associated with the
type, and it is not replicated in memory for each entity or array
element of that type.

$.02 -Ron Shepard

mies...@gmail.com

unread,
Dec 12, 2021, 4:18:11 PM12/12/21
to
> unfortunately values declared in a user-defined type cannot have the PARAMETER attribute, which might make it a bit more elegant.

I did ask for this earlier here: https://github.com/j3-fortran/fortran_proposals/issues/110
The use of the PARAMETER attribute would make things more safe.

But even without the PARAMETER attribute, we can effectively use integer based enums with the compilers already. Integer based enums are essentially for implementing distributed objects with Coarray Fortran because we can use the values with atomic coarrays. I am currently working on a channel implementation using coarrays, code snippets below are taken from that: John’s last syntax example will not work for this, and the enum type component must be declared above CONTAINS yet, of course. The enum type itself can even be extended in Fortran, which makes the use of such enum types very flexible and efficient as we may use them differently at multiple levels.

!************************
!**** enumeration types: ***
!************************
!*** Enum_FragmentedMethodChannel1Status:
type, extends(OOOPchnl00_ChannelStatus_EnumDef), private :: OOOPfrob03_00_Channel1Status_EnumDef
! (values below 10000000 are reserved for the channel itself here)
!
! values for controlling the execution flow:
integer(kind=OOOGglob_kint) :: InitiateFragmentedMethod3 != 10000000
integer(kind=OOOGglob_kint) :: ReadyToStartFragmentedMethod3 != 11000000
integer(kind=OOOGglob_kint) :: StartFragmentedMethod3 != 12000000
integer(kind=OOOGglob_kint) :: SendCurrentTestValue != 13000000
integer(kind=OOOGglob_kint) :: AdjustTestValueToMaxTestValue != 14000000
integer(kind=OOOGglob_kint) :: FinishedFragmentedMethod3 != 15000000
integer(kind=OOOGglob_kint) :: ExitFragmentedMethod3 != 16000000
!
! always required:
integer(kind=OOOGglob_kint) :: Enum_MaxValue != 17000000
end type OOOPfrob03_00_Channel1Status_EnumDef

!********************
!*** type definition: ***
!********************
type, extends(OOOPfrob03_00_objBaseFragmentedMethod3), abstract, public :: OOOPfrob03_01_objFragmentedMethod3_CE
private
!*****
! enumeration type members:
! (values below 10000000 are reserved for the channel itself):
type (OOOPfrob03_00_Channel1Status_EnumDef), public :: Enum_Channel1Status &
= OOOPfrob03_00_Channel1Status_EnumDef (1000000,2000000,3000000, &
10000000,11000000, 12000000,13000000,14000000,15000000,16000000,17000000)
!*****
! local members:
!*****
! coarray component members: (not recommended if channels are in use)
!*****
contains
private
procedure, public :: FragmentedMethod3_CE => OOOPfrob03_01_FragmentedMethod3_CE
end type OOOPfrob03_01_objFragmentedMethod3_CE

mies...@gmail.com

unread,
Dec 16, 2021, 4:38:03 PM12/16/21
to
> John’s last syntax example will not work for this,...

That was my mistake, the syntax does work (using ifort and gfortran) and it is a huge improvement for such nested/extended enum types:

!*****************************
!**** enumeration types: ***
!*****************************
type, extends(OOOPchnl00_ChannelStatus_EnumDef), private :: OOOPfrob03_00_Channel1Status_EnumDef
! (values below 10000000 are reserved for the channel itself here)
!
! values for controlling the execution flow:
integer(kind=OOOGglob_kint) :: InitiateFragmentedMethod3 = 10000000
integer(kind=OOOGglob_kint) :: ReadyToStartFragmentedMethod3 = 11000000
integer(kind=OOOGglob_kint) :: StartFragmentedMethod3 = 12000000
integer(kind=OOOGglob_kint) :: SendCurrentTestValue = 13000000
integer(kind=OOOGglob_kint) :: AdjustTestValueToMaxTestValue = 14000000
integer(kind=OOOGglob_kint) :: FinishedFragmentedMethod3 = 15000000
integer(kind=OOOGglob_kint) :: ExitFragmentedMethod3 = 16000000
!
! always required:
integer(kind=OOOGglob_kint) :: Enum_MaxValue = 17000000
end type OOOPfrob03_00_Channel1Status_EnumDef
!

!*************************
!*** type definition: ***
!*************************
type, extends(OOOPfrob03_00_objBaseFragmentedMethod3), abstract, public :: OOOPfrob03_01_objFragmentedMethod3_CE
private
!*****
! enumeration type members:
type (OOOPfrob03_00_Channel1Status_EnumDef), public :: Enum_Channel1Status & ! we can not use PARAMETER here
= OOOPfrob03_00_Channel1Status_EnumDef ()
.
.
0 new messages