Reputation: 465
I have a subroutine which contains a simple if-statement right in the middle which I would like to get rid of. The reason being that I know in advance if that statement will be true or false based on where the call occurs and not based on the input data. This is even more attractive since it is a 1-liner if that really does not do too much.
Right now there is an extra logical input which is purely for that if statement and it is called twice - once with "false" and once with "true".
Essentially, I want to make the if-statement work out during compilation, but an #ifdef will not allow for two different usages/configurations of the same subroutine.
The dumb solution would be to copy the subroutine completely and have one instance with the "true" segment while the other with the "false" segment only.
However, besides this being far from elegant (and have 99.9% similarity), this is an opening for trouble, once the subroutine is changed in the future.
I will note that I do not mean overloading the subroutine (at least not in the "traditional" sense of it), although this is sort of the same general idea.
So my question is - is there a solution to this situation or I will have to leave it as a run-time if statement and have an additional logical true/false input?
I'm using Fortran 90/95.
Upvotes: 2
Views: 444
Reputation: 8140
So my understanding is that the code is something like:
module mymod
implicit none
contains
subroutine mysub(a, l)
integer, intent(inout) :: a
logical, intent(in) :: l
a = mod(a**3, 53)
if (l) then
a = a + 1
else
a = a - 1
end if
end subroutine mysub
end module mymod
program myprog
use mymod
implicit none
integer :: b
b = 10
call mysub(b, .TRUE.)
print *, b
call mysub(b, .FALSE.)
print *, b
end program myprog
And you are concerned that the mysub
subroutine is not as efficient as it can be because it has an if statement, even though the compiler knows at compile time which of the paths it will take each time.
Before you investigate this further, I'd strongly suggest you evaluate whether that's necessary. Everything you can do will make the code less readable, less maintainable, and is likely to deliver marginal performance increase at best.
You could write two different subroutines, and have the parts that are identical in a separate file and INCLUDE
it into both subroutines, like this:
same.inc:
integer, intent(inout) :: n
n = mod(n**3, 53)
sub.f90:
module mymod
implicit none
contains
subroutine mysub_true(n)
include 'same.inc'
n = n + 1
end subroutine mysub_true
subroutine mysub_false(n)
include 'same.inc'
n = n - 1
end subroutine mysub_false
end module mymod
Together with the preprocessor, you can even go all-in:
mysub.inc:
#if PATH == 1
#define SUB_NAME mysub_true
#else
#define SUB_NAME mysub_false
#endif
subroutine SUB_NAME(n)
integer, intent(inout) :: n
n = mod(n**3, 53)
#if PATH == 1
n = n + 1
#else
n = n - 1
#endif
end subroutine SUB_NAME
#undef SUB_NAME
#undef PATH
mymod.F90:
module mymod
implicit none
contains
#define PATH 1
#include mysub.inc
#define PATH 0
#include mysub.inc
end module mymod
myprog.F90:
program myprog
use mymod
implicit none
integer :: b
b = 10
call mysub_true(b)
print *, b
call mysub_false(b)
print *, b
end program myprog
and compile with
gfortran -c -o mymod.o -cpp mymod.F90
gfortran -o myprog myprog.F90 mymod.o
Whether that makes it easier or harder to read depends on the complexity of the parts that are the same.
Upvotes: 4