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?
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.