Reputation: 207
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
.
Upvotes: 2
Views: 898
Reputation: 32366
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.
Upvotes: 4