2

I pass an optional mask to a subroutine. Within this routine I then have many statements of the kind

...
if (present(Mask)) then
  where(Mask)
    y(:) = A(:)*B(:)
  end where
else
  y(:) = A(:)*B(:)
end if
...

This is very ugly in regards of code duplication. Are there any advices to do this in a better way? I have many statements like this in my code and I need to perform the operations on whole arrays.

Edit: At least a part of the problem can be solved through elemental functions. Because I have the same operations on the right side I can write

...
if (present(Mask)) then
  where(Mask)
    y(:) = multiply(A(:),B(:))
  end where
else
  y(:) = multiply(A(:),B(:))
end if
...
elemental real function multiply(a,b)
  real, intent(in) :: a,b
  multiply = a*b
end function

This way, I have at least a single place for the operations.

Lithium
  • 223
  • 1
  • 7
  • 1
    There is discussion around [this other question](https://stackoverflow.com/q/37723973/3157076). Does anything in there suit you? – francescalus Jul 05 '20 at 15:30
  • Thanks for providing the link. It seems to me that there is no straight forward solution and my problem is very similar. I guess that's ok for me to know for now. – Lithium Jul 05 '20 at 20:05

1 Answers1

2

There is the option (pun intended) to no use optional arguments, and instead, write a generic interface. For example, instead of writing

subroutine bar(arg0, opt)
  integer, intent(inout) :: arg0
  integer, intent(in), optional :: opt
  arg0 = int(4 * atan(1.))
  if (present(opt)) arg0 = arg0 + opt
module foo

You can do

interface bar
  module procedure bar0
  module procedure bar1
end interface bar

contains

  subroutine bar0(arg0)
     integer, intent(inout) :: arg0
     arg0 = int(4 * atan(1.))
  end subroutine bar0

  subroutine bar1(arg0, opt)
     integer, intent(inout) :: arg0
     integer, intent(in) :: opt
     arg0 = arg0 + opt
  end subroutine bar1

end module foo

program bah
  use foo
  integer :: a0 = 1, a1 = 42
  call bar(a0)
  print *, a0
  call bar(a0, a1)
  print *, a0
end program bah

One advantage that you may gain from this approach is that bar0 and bar1 may allow a compiler to do a better job of optimizing the code. The if (present(opt)) construct may get in the way.

evets
  • 996
  • 1
  • 5
  • 6
  • This is a very nice idea, especially from a performance viewpoint. Unfortunately, in my case I pass this Mask through many subroutines which would mean I would also need to write interfaces for all these routines. Additionally, I would still have code duplications for the right sides. But it led me to another idea - I could use elemental functions, than I would at least get rid of the duplicate assignments from the right side. – Lithium Jul 06 '20 at 13:11
  • 1
    Yes, the ability to pass `optional` arguments down a call chain is a nice feature. When I do use `optional` arguments, I structure the code to minimize the `if (present(arg))` testing (i.e., typically two blocks of code). – evets Jul 06 '20 at 16:58