Reputation: 13
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
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 if
s).
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_use
s.
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