Zeus
Zeus

Reputation: 1565

Fortran Function that accepts different types

I have a three convert functions that convert angles, the code shown below.

The functions returns are of the same type as the input. This means that the input angle has either to be of the type the programmer wishes the output to be or convert it to output type required using for example al = convert(Real(30),"deg_to_rad").

I would like the programmer to be able to do al = convert(30,"deg_to_rad"). I have the idea to use the strategy of the transfer intrinsic to define the output type.

Real :: al, mold    
al = convert(30,"deg_to_rad", mold)
al = convert(30.0,"deg_to_rad", mold)

Double Precision :: al, mold    
al = convert(30,"deg_to_rad", mold)
al = convert(30.0,"deg_to_rad", mold)

There are the functions I have. Would be good to have some ideas on ways to proceed and implement a good scheme.

Interface convert
  Module Procedure convert
  Module Procedure convert_real
  Module Procedure convert_dble
  Module Procedure convert_real128
End Interface convert

Contains

Function convert_real   &
  (                &
    qa, label      &
  )                &
 Result (qb)

Real, Intent (in) :: qa
Character (len=*), Intent (in) :: label

Real :: qb

If (label .contains. "angle:") Then

 Block

   Real :: pi, deg_to_rad

   pi = 22.0 / 7.0
   deg_to_rad  = pi  / 180.0

   Select Case (Trim (label))

     Case ("angle: deg_to_rad")
       qb = deg_to_rad * qa

   End Select

 End Block

End If

End Function convert_real


Function convert_dble  &
  (                    &
    qa, label          &
  )                    &
 Result (qb)

Double Precision, Intent (in) :: qa
Character (len=*), Intent (in) :: label

Double Precision :: qb


If (label .contains. "angle:") Then

 Block

   Double Precision :: pi, deg_to_rad

   pi = Dble(22) / Dble(7)
   deg_to_rad  = pi / Dble(180)

   Select Case (Trim (label))

     Case ("angle: deg_to_rad")
       qb = deg_to_rad * qa

   End Select

 End Block

End If

End Function convert_dble


Function convert_real128  &
  (                       &
    qa, label             &
  )                       &
 Result (qb)

Real (Real128), Intent (in) :: qa
Character (len=*), Intent (in) :: label

Real (Real128) :: qb

If (label .contains. "angle:") Then

 Block

   Real (Real128) :: pi, deg_to_rad
   pi = Real(22,Real128) / Real(7,Real128)
   deg_to_rad  = pi / Real(180,Real128)

   Select Case (Trim (label))

     Case ("angle: deg_to_rad")
       qb = deg_to_rad * qa

   End Select

 End Block

End If

End Function convert_real128

I am trying to use a subroutine when the input and outputs are of different types. However I get the error below

Subroutine convertor  & 
  (                   &
    qa, label, qb     &
  )

Class (*), Intent (in) :: qa
Character (len=*), Intent (in) :: label

Class (*), Intent (out) :: qb

Select Type (qb)

 Type Is (Real)
   qb = convert (Real(qa),label)

 Type Is (Double Precision)
   qb = convert (Dble(qa),label)

 Type Is (Real (Real128))
   qb = convert (Real(qa,Real128),label)

End Select

End Subroutine convertor

Here is the error

gfortran -o build/lib/foul.o -c -ffree-form -g -J./build/lib lib/foul.f
gfortran -o build/lib/meidum.o -c -ffree-form -g -J./build/lib lib/meidum.f
lib/meidum.f:890.26:

   qb = convert (Real(qa),label)
                      1
Error: 'a' argument of 'real' intrinsic at (1) must be a numeric type
lib/meidum.f:893.26:

   qb = convert (Dble(qa),label)
                      1
Error: 'a' argument of 'dble' intrinsic at (1) must be a numeric type
lib/meidum.f:896.26:

   qb = convert (Real(qa,Real128),label)
                      1
Error: 'a' argument of 'real' intrinsic at (1) must be a numeric type

Upvotes: 2

Views: 1394

Answers (1)

M. S. B.
M. S. B.

Reputation: 29401

If I understand what you are trying to do, this seems unnecessarily complicated. Instead of using transfer as your model, use the arithmetic intrinsic functions such as sin. Fortran (since FORTRAN 77) can automatically distinguish between the various sine functions (real, double precision, quad precision) based on their argument. Write each function, then an interface listing them as module procedure to make them generic. Example: Overloading functions with Fortran

EDIT: It depends on what you want:

result = convert (input)

To me, it seems most natural to have type of the function return controlled by the type of the argument input. That is what I described and is what is shown in the example. If the type of result is different, Fortran will convert in the assignment ... the drawback is if you have selected compiler options so that this generates a warning. So if instead you want the type of result to control the type calculated by convert, then this technique will have to be modified. You can't use a function because function return doesn't distinguish for this overloading. Use a subroutine instead, which will then have an argument that will distinguish:

call convert (input, result)

This will work just fine in with an interface / module procedure. I'm not sure why think this won't work. It might be a bit less elegant in usage than the assignment statement / function notation.

Upvotes: 3

Related Questions