Zeus
Zeus

Reputation: 1525

Fortran convert string to number

I want to have a subroutine that converts a contents of a numeric string to a numeric type (int, real, double precision, real(real128)).

However I am getting an error when trying to use Class(*). The error is shown below:

gfortran -o build/lib/larsa.o -c -ffree-form -g -J./build/lib lib/larsa.f
lib/larsa.f:1933.35:

 Read (s, frmt, iostat=ios) num
                               1
Error: Data transfer element at (1) cannot be polymorphic unless 
it is processed by a defined     input/output procedure
lib/larsa.f:1935.32:

 Read (s, *, iostat=ios) num
                            1
Error: Data transfer element at (1) cannot be polymorphic unless 
it is processed by a defined input/output procedure

This is the subroutine I have written.

Subroutine converts_str_to_num  &
  (                             &
    s, num,                     &
    fmt, wrn                    &
  )

Character (len=*), Intent (in) :: s
Character (len=*), Intent (in), Optional :: fmt 

Class (*) :: num
Character (len=*), Intent (inout), Optional :: wrn 

Integer :: ios
Character (len=65) :: frmt

!!$ Reads contents of s and puts value in i.

If (Present (fmt)) Then
  frmt = "(" // Trim (fmt) //  ")"
  Read (s, frmt, iostat=ios) num
Else
  Read (s, *, iostat=ios) num
End If

End Subroutine converts_str_to_num

Upvotes: 1

Views: 786

Answers (1)

francescalus
francescalus

Reputation: 32366

To tidy up the comments, I'll provide an answer.

The error message is clear: you cannot have a polymorphic variable in an input/output list unless the list is processed by defined input/output. This is 9.6.3.5 in Fortran 2008. class(*) num is (unlimited) polymorphic.

Now, for polymorphic derived types you could define such a defined input/output procedure, but that counts as a lot of work and gfortran certainly doesn't (yet) support that notion. Also, you can't do this for intrinsic types. These factors mean you have to deal with non-polymorphic variables in the input list you have.

Of course, it's possible to use generics to avoid polymorphism, but the alternative (as it is for about everything polymorphic) is to use a select type construct. For simplicity, ignore the list-directed and explicit format cases:

select type (assoc => num)
type is (int)
   Read (s, *, iostat=ios) assoc
type is (real)
   ...
type is (...)
class default
   error stop "Oh noes!"
end select

I've used an associate name in the select type to address one part of your confusion. If you've just done

select type(num)
type is (int)
  Read (s, *, iostat=ios) num
end select

to think that "now using num is fine: why?" that's because the num inside the construct is not the same as the num outside. Crucially, it isn't polymorphic but is the exact type matching the type is.

Upvotes: 4

Related Questions