What argument type should I use in C when calling a Fortran function that takes logical
arguments, specifically with gfortran? Where is this documented for gfortran?
Here's an example program that doesn't compile without warnings:
Contents of one.f
:
subroutine proc1(x)
logical x
end
Contents of main.c
:
void proc1_(_Bool *x);
int main() {
_Bool x;
proc1_(&x);
return 0;
}
If I compile using GCC as follows, with LTO enabled, I get a warning about mismatching function prototypes:
gfortran -flto -c one.f
gcc -flto -c main.c
gcc -flto main.o one.o
The warning I get:
main.c:2:6: warning: type of 'proc1_' does not match original declaration [-Wlto-type-mismatch]
2 | void proc1_(_Bool *x);
| ^
one.f:2:22: note: 'proc1' was previously declared here
2 | subroutine proc1(x)
| ^
one.f:2:22: note: code may be misoptimized unless '-fno-strict-aliasing' is used
Note that enabling LTO allows the linker to verify that argument types match between prototypes. Using LTO is unfortunately not our choice. CRAN requires the submitted code to compile without these warnings with LTO enabled.
I only see problems when trying to use logical
arguments. real
, integer
and character
are all fine.
gfortran can be asked to produce C prototypes, and this is the output it gives me:
gfortran -flto -fc-prototypes-external -c one.f
void proc1_ (int_fast32_t *x);
Using int_fast32_t
in the C prototype doesn't work either. No type that I tried did, neither int
, nor _Bool
. Usually, when there is a type mismatch between prototypes, the error message mentions what the type should be—but not in this case.
How can I find what is the correct type to use?
CodePudding user response:
For real modern C-Fortran interoperability you should use the types (kinds) supplied by the iso_c_binding
module and make your Fortran procedure bind(c)
. That way you can use logical(c_bool)
.
In the old style the best thing is to work with integers and pass an int
and only correct from integer
to logical
inside Fortran. Old C did not have any bool
, it was added later.
With minimal changes:
subroutine proc1(x)
use iso_c_binding
logical(c_bool) x
end
#include <stdbool.h>
void proc1_(bool *x);
int main() {
bool x;
proc1_(&x);
return 0;
}
> gfortran -flto -c one.f
> gcc -flto -c main.c
> gcc -flto main.o one.o
issues no warning on my Linux and GCC 7 and 10.
Or after further changes:
subroutine proc1(x) bind(C, name="proc1")
use iso_c_binding
logical(c_bool), value :: x
end
#include <stdbool.h>
void proc1(bool x);
int main() {
bool x;
proc1(x);
return 0;
}
The change to pass-by-value of course only when it is indeed just an input parameter.
CodePudding user response:
The correct and guaranteed to be portable solution is, as explained in the answer by Vladimir F, to create a Fortran wrapper routine that uses ISO_C_BINDING. This wrapper can also take the opportunity to make a more idiomatic C interface, e.g. using the value
specifier to pass scalars by value.
However, for the quick and dirty solution that works on GFortran WITHOUT LTO (and somewhat likely on other compilers, but no guarantees), see https://gcc.gnu.org/onlinedocs/gfortran/Internal-representation-of-LOGICAL-variables.html#Internal-representation-of-LOGICAL-variables . That is, you can pass a C integer variable of the appropriate size containing 1
for true and 0
for false. Appropriate size here meaning that unless you have compiled your Fortran code with -fdefault-integer-8
or such compile options, the GFortran default kind logical will be 4 bytes, so a plain C int
should be good (or int32_t
if you really want to be sure, though I don't think GFortran supports any targets where the C int
is not 32 bits).
The reason this doesn't work with LTO is that while the above works, in the bowels of GCC the Fortran LOGICAL variables are almost the same as integers, but not quite. So in practice they are special integer variables with max value 1 and min value 0 even though they take up more space (as specified by their kind parameter). So this kind of type mismatch is likely what it complains about. Unfortunately no solution to this one, except the above correct solution via ISO_C_BINDING.