2
votes

I would like to find a way to get a fortran method to take some value for an optional parameter that will make the argument not appear to be present in the method that has been called.

Here is a reduced test case which distills the problem in the fortran code:

MODULE FortranOptional
  USE ISO_C_BINDING

CONTAINS
  SUBROUTINE optionaltest(scalar)
    REAL(8), OPTIONAL, INTENT(IN) :: scalar

    IF (PRESENT(scalar)) THEN
      print *, "scalar is present: ", scalar
    ELSE
      print *, "scalar is NOT present"
    END IF

  END SUBROUTINE optionaltest

  SUBROUTINE optionaltest_c(scalarC) BIND(C, NAME="optionaltest_c")
    REAL(C_DOUBLE), OPTIONAL, INTENT(IN) :: scalarC
    REAL(8) :: scalar

    IF (PRESENT(scalarC)) THEN
      print *, "scalarC is present: ", scalarC
      scalar = scalarC
    ELSE
      print *, "scalarC is NOT present"
      ! Can I do something here to make scalar appear not present in optionaltest()?
    END IF

    CALL optionaltest(scalar)
  END SUBROUTINE optionaltest_c
END MODULE FortranOptional

And the associated C++ test code:

extern "C"
{
void optionaltest_c(double*);
}

void testMethod()
{
    double v = 5.3;
    optionaltest_c(0);

    std::cout << "\n";

    optionaltest_c(&v);
}

Which produces:

scalarC is NOT present
scalar is present:    6.9118029901527309E-310

scalarC is present:    5.2999999999999998     
scalar is present:    5.2999999999999998     

Is there any way I can set the scalar variable based on the presence of scalarC that will make it appear to not be present when scalarC is not present?

Some constraints:

  • I'd prefer to avoid if statements involving different calls to optionaltest, as the real version of the subroutine has several optional parameters that may be specified or not on an individual basis, resulting in a combinatorial explosion.

  • I cannot bind the optionaltest subroutine directly as a C function, as the real version of the subroutine has assumed shape optional arguments, which are not C-compatible in the version of Fortran I am using.

  • I cannot modify the signature of optionaltest.

1
@francescalus: Unfortunately, in the real method, there are computations that must be done in the translation step, so there is no directly analogous variable that I can simply pass-through.professional_yet_not_trackable
Perhaps I can explain better: is CALL optionaltest(scalar) in subroutine optionaltest_c possible, or are you having (perhaps because of different kind/type) that to have scalar and scalarC distinct variables?francescalus
CALL optionaltest(scalar) does appear in optionaltest_c; did you mean CALL optionaltest(scalarC)? I cannot do the latter since, as you guessed, scalar and scalarC are not quite the same and I can't just pass scalarC through.professional_yet_not_trackable
Yes, I indeed meant scalarC. For interest (or best possible answer), can you state compiler and version? As you've noted we're in areas of variable support.francescalus
I am using gfortran 4.4.7, and I need to write to the Fortran90 specification.professional_yet_not_trackable

1 Answers

4
votes

In the subroutine optionaltest_c the variable scalar is a local one. As you note, a local variable has no concept of optional-ness.

For completeness, if it's just a matter of passing the optional dummy argument down from optionaltest_c to optionaltest, then

call optionaltest(scalarC)

would suffice. That is, an optional dummy argument may be an actual argument for a procedure where the corresponding dummy argument is also optional, regardless of its presence.

So, if instead you need to do some form of manipulation, what to do?1 I note that you have intrinsic assignment between the dummy argument and local variable.

Under Fortran 2008 there is a sensible option: have the local variable allocatable. If a variable is not allocated and is an actual argument for an optional non-allocatable dummy then it is treated as not present:

  subroutine optionaltest_c(scalarC) bind(C)
    type(type1), optional, intent(in) :: scalarC
    type(type2), allocatable :: scalar  ! Local, conformable with scalarC

    if (PRESENT(scalarC)) scalar=scalarC
    ...

    call optionaltest(scalar)
  end subroutine optionaltest_c

[You may need to provide alternative ways of allocating scalar and setting its value, depending on your compiler support.]

With scalarC present, scalar becomes allocated and has its value. Then on the call, the dummy argument is present. With scalarC not present, scalar is left not allocated and on the call the dummy argument is absent.

This works just as well for arrays as for scalars.

Now, I see that you mention gfortran 4.4.7, so it's possible this approach won't work. I'll let someone else answer with a specific way in that case, but this is perhaps still worth documenting for the benefit of others/after upgrade.


1 That may not be the case in this question if c_double has value 8, but we'll consider anyway.