Rob Farmer
Rob Farmer

Reputation: 13

Fortran Fallback code for optional string argument

So I'd like to pass an optional string to a subroutine, that if the string doesn't exist set a fallback string. However i don't want to set the length of the fallback string at compile time. So i've had two ideas for sorting this out doStufFirst and doStuffSecond

So i have this:

MODULE stuff
    implicit none

    contains

subroutine dostuffFirst (string)
   implicit none
   character(len=*),intent(in),optional :: string
   character(len=min(8,len(string))) :: outString

   if(present(string)) THEN
    outString=string
   else
     outString="fallBack"
   endif

  call doStuff2(outString)

 end subroutine dostuffFirst


subroutine dostuffSecond (string)
    implicit none
    character(len=*),optional :: string
    character,dimension(:),allocatable :: outString
    integer :: i

   if(present(string)) THEN
    ALLOCATE(outString(len(string)))
        do i=1,len(string)
          outString(i)=string(i:i)
        end do
    else
        ALLOCATE(outString(8))
        outString(1:8)=(/"f","a","l","l","B","a","c","k"/)
    endif

    call doStuff2(outString)

end subroutine dostuffSecond


      subroutine doStuff2(str)
       implicit none
       character(len=*),intent(in) :: str

       write(*,*) str

      end subroutine doStuff2

end module stuff

PROGRAM prog
     use stuff
     implicit none


      call dostuffFirst("123")

      call dostuffSecond("123")

END program prog

But the problem with doStuffFirst is that i can't have the len(string) of an optional argument in the declaration of outString. And the problem with dostuffSecond is i've now made a rank-1 array but doStuff2 is expecting a scalar (and would be infeasible to change everything afterwards to expect an array)

Any suggestions? Thanks

Upvotes: 1

Views: 400

Answers (1)

francescalus
francescalus

Reputation: 32366

Considering the problem in dostuffFirst, trying to establish the length of the character variable to pass to doStuff2 could you do the PRESENT logic in doStuff2 itself?

subroutine doStuff2(str)
  character(len=*), intent(in), optional :: str

  if (PRESENT(str)) then
    write(*,*) str
  else
    write(*,*) "fallback"
  end if
emd subroutine doStuff2

I'll admit this latter can get a bit cumbersome if going on more than once, but one can indeed use wrappers as with your dostuffFirst attempt. One way, if you don't need a variable which takes either the input string or the fallback string is as follows.

subroutine dostuffFirst (string)
   character(len=*),intent(in),optional :: string

   if(present(string)) THEN
     call doStuff2(string)
   else
     call doStuff2("fallBack")
   endif

 end subroutine dostuffFirst

Of course, if you have a lot of possible strings, this also gets very awkward very quickly (lots of nested ifs).

subroutine dostuffFirst(string1, string2, string3)
  character(len=*), intent(in), optional :: string1, string2, string3

  if (PRESENT(string1)) then
    if (PRESENT(string2)) then
      if (PRESENT(string3)) then
        call doStuff2(string1, string2, string3)
      else
        call doStuff2(string1, string2, "fallback3")
  ! etc
end subroutine dostuffFirst

The traditional way to get around these things is choosing a massively long str_to_uses.

subroutine dostuffFirst(string1, string2, string3)
  character(len=*), intent(in), optional :: string1, string2, string3
  character(AS_LONG_AS_I_EVER_NEED) str_to_use1, str_to_use2, str_to_use3

  if (PRESENT(string1)) then
    str_to_use1 = string1
  else
    str_to_use1 = "fallback1"
  end if
  ! etc
  call doStuff2(str_to_use1, str_to_use2, str_to_use3)
end subroutine dostuffFirst

However, if you are happy to move to a more modern compiler/coding standard, then you have the luxury of deferred length character variables.

subroutine dostuffFirst(string1, string2, string3)
  character(len=*), intent(in), optional :: string1, string2, string3
! Deferred length working variables
  character(:), allocatable :: str_to_use1, str_to_use2, str_to_use3

  if (PRESENT(string1)) then
    str_to_use1 = string1
  else
    str_to_use1 = "fallback1"
  end if
  ! etc
  call doStuff2(str_to_use1, str_to_use2, str_to_use3)
end subroutine dostuffFirst

Which is much the same as the previous case, but without the worry of the correct choice of length.

Upvotes: 2

Related Questions